ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.assSingleChoiceGUI.php
Go to the documentation of this file.
1 <?php
2 
19 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
20 
36 {
37  private bool $rebuild_thumbnails = false;
45  public function __construct($id = -1)
46  {
48  $this->object = new assSingleChoice();
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) {
72  $this->saveTaxonomyAssignments();
73  return 0;
74  }
75  return 1;
76  }
77 
85  protected function getEditAnswersSingleLine($checkonly = false): bool
86  {
87  if ($this->object->getSelfAssessmentEditingMode()) {
88  return $this->object->isSingleline();
89  }
90 
91  if ($checkonly) {
92  $types = $_POST['types'] ?? '0';
93  return $types === '0' ? true : false;
94  }
95 
96  $lastChange = $this->object->getLastChange();
97  if (empty($lastChange) && !isset($_POST['types'])) {
98  // a new question is edited
99  return $this->object->getMultilineAnswerSetting() ? false : true;
100  } else {
101  // a saved question is edited
102  return $this->object->isSingleline();
103  }
104  }
105 
110  public function editQuestion($checkonly = false): bool
111  {
112  $save = $this->isSaveCommand();
113  $this->getQuestionTemplate();
114 
115  $form = new ilPropertyFormGUI();
116  $this->editForm = $form;
117 
118  $form->setFormAction($this->ctrl->getFormAction($this));
119  $form->setTitle($this->outQuestionType());
120  $is_singleline = $this->getEditAnswersSingleLine($checkonly);
121  if ($is_singleline) {
122  $form->setMultipart(true);
123  } else {
124  $form->setMultipart(false);
125  }
126  $form->setTableWidth("100%");
127  $form->setId("asssinglechoice");
128 
129  $this->addBasicQuestionFormProperties($form);
130  $this->populateQuestionSpecificFormPart($form, $is_singleline);
131  $this->populateAnswerSpecificFormPart($form, $is_singleline);
132 
133 
134  $this->populateTaxonomyFormSection($form);
135 
136  $this->addQuestionFormCommandButtons($form);
137 
138  $errors = false;
139 
140  if ($save) {
141  foreach ($this->request->getParsedBody() as $key => $value) {
142  $item = $form->getItemByPostVar($key);
143  if ($item !== null) {
144  switch (get_class($item)) {
145  case 'ilDurationInputGUI':
146  $item->setHours($value['hh']);
147  $item->setMinutes($value['mm']);
148  $item->setSeconds($value['ss']);
149  break;
150  default:
151  $item->setValue($value);
152  }
153  }
154  }
155 
156  $errors = !$form->checkInput();
157  foreach ($this->request->getParsedBody() as $key => $value) {
158  $item = $form->getItemByPostVar($key);
159  if ($item !== null) {
160  switch (get_class($item)) {
161  case 'ilDurationInputGUI':
162  $item->setHours($value['hh']);
163  $item->setMinutes($value['mm']);
164  $item->setSeconds($value['ss']);
165  break;
166  default:
167  $item->setValue($value);
168  }
169  }
170  } // again, because checkInput now performs the whole stripSlashes handling and we need this if we don't want to have duplication of backslashes
171 
172  if ($errors) {
173  $checkonly = false;
174  }
175  }
176 
177  if (!$checkonly) {
178  $this->tpl->setVariable("QUESTION_DATA", $form->getHTML());
179  }
180  return $errors;
181  }
182 
186  public function uploadchoice(): void
187  {
188  $this->writePostData(true);
189  $position = key($_POST['cmd']['uploadchoice']);
190  $this->editQuestion();
191  }
192 
196  public function removeimagechoice(): void
197  {
198  $this->writePostData(true);
199  $position = key($_POST['cmd']['removeimagechoice']);
200  $filename = $_POST['choice']['imagename'][$position];
201  $this->object->removeAnswerImage($position);
202  $this->editQuestion();
203  }
204 
208  public function addchoice(): void
209  {
210  $this->writePostData(true);
211  $position = key($_POST['cmd']['addchoice']);
212  $this->object->addAnswer("", 0, $position + 1);
213  $this->editQuestion();
214  }
215 
219  public function removechoice(): void
220  {
221  $this->writePostData(true);
222  $position = key($_POST['cmd']['removechoice']);
223  $this->object->deleteAnswer($position);
224  $this->editQuestion();
225  }
226 
239  public function getSolutionOutput(
240  $active_id,
241  $pass = null,
242  $graphicalOutput = false,
243  $result_output = false,
244  $show_question_only = true,
245  $show_feedback = false,
246  $show_correct_solution = false,
247  $show_manual_scoring = false,
248  $show_question_text = true,
249  bool $show_inline_feedback = true
250  ): string {
251 
252  if (($active_id > 0) && (!$show_correct_solution)) {
253  $user_solutions = $this->object->getSolutionValues($active_id, $pass);
254 
255  } else {
256  $found_index = -1;
257  $max_points = 0;
258  foreach ($this->object->answers as $index => $answer) {
259  if ($answer->getPoints() > $max_points) {
260  $max_points = $answer->getPoints();
261  $found_index = $index;
262  }
263  }
264  $user_solutions = [['value1' => $found_index]];
265  }
266 
267  return $this->renderSolutionOutput(
268  $user_solutions,
269  $active_id,
270  $pass,
271  $graphicalOutput,
272  $result_output,
273  $show_question_only,
274  $show_feedback,
275  $show_correct_solution,
276  $show_manual_scoring,
277  $show_question_text,
278  false,
279  $show_inline_feedback,
280  );
281  }
282 
283  public function renderSolutionOutput(
284  mixed $user_solutions,
285  int $active_id,
286  ?int $pass,
287  bool $graphical_output = false,
288  bool $result_output = false,
289  bool $show_question_only = true,
290  bool $show_feedback = false,
291  bool $show_correct_solution = false,
292  bool $show_manual_scoring = false,
293  bool $show_question_text = true,
294  bool $show_autosave_title = false,
295  bool $show_inline_feedback = false,
296  ): ?string {
297  $user_solution = '';
298  foreach ($user_solutions as $idx => $solution_value) {
299  $user_solution = $solution_value['value1'];
300  }
301 
302  $keys = $this->getChoiceKeys();
303  $template = new ilTemplate("tpl.il_as_qpl_mc_sr_output_solution.html", true, true, "Modules/TestQuestionPool");
304  $solutiontemplate = new ilTemplate("tpl.il_as_tst_solution_output.html", true, true, "Modules/TestQuestionPool");
305  foreach ($keys as $answer_id) {
306  $answer = $this->object->answers[$answer_id];
307  if ($active_id > 0 && !$show_correct_solution && $graphical_output) {
308  $correctness = $this->generateCorrectness(
309  (string) $user_solution,
310  (string) $answer_id,
311  $answer->getPoints(),
312  $this->object->getMaximumPoints()
313  );
314  $template->setCurrentBlock("icon_ok");
315  $template->setVariable("ICON_OK", $this->generateCorrectnessIconsForCorrectness($correctness));
316  $template->parseCurrentBlock();
317  }
318  if ($answer->hasImage()) {
319  $template->setCurrentBlock("answer_image");
320  if ($this->object->getThumbSize()) {
321  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $answer->getImage());
322  } else {
323  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $answer->getImage());
324  }
325  $alt = $answer->getImage();
326  if (strlen($answer->getAnswertext())) {
327  $alt = $answer->getAnswertext();
328  }
329  $alt = preg_replace("/<[^>]*?>/", "", $alt);
330  $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
331  $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
332  $template->parseCurrentBlock();
333  }
334 
335  if (($show_feedback || !$this->isTestPresentationContext()) && $show_inline_feedback) {
336  $this->populateInlineFeedback($template, $answer_id, $user_solution);
337  }
338  $template->setCurrentBlock("answer_row");
339  $template->setVariable("ANSWER_TEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($answer->getAnswertext(), true));
340 
341  if ($this->renderPurposeSupportsFormHtml() || $this->isRenderPurposePrintPdf()) {
342  if ((string) $user_solution === (string) $answer_id) {
343  $template->setVariable("SOLUTION_IMAGE", ilUtil::getHtmlPath(ilUtil::getImagePath("object/radiobutton_checked.png")));
344  $template->setVariable("SOLUTION_ALT", $this->lng->txt("checked"));
345  } else {
346  $template->setVariable("SOLUTION_IMAGE", ilUtil::getHtmlPath(ilUtil::getImagePath("object/radiobutton_unchecked.png")));
347  $template->setVariable("SOLUTION_ALT", $this->lng->txt("unchecked"));
348  }
349  } else {
350  $template->setVariable('QID', $this->object->getId());
351  $template->setVariable('SUFFIX', $show_correct_solution ? 'bestsolution' : 'usersolution');
352  $template->setVariable('SOLUTION_VALUE', $answer_id);
353  if ((string) $user_solution === (string) $answer_id) {
354  $template->setVariable('SOLUTION_CHECKED', 'checked');
355  }
356  }
357 
358  if ($result_output) {
359  $points = $this->object->answers[$answer_id]->getPoints();
360  $resulttext = ($points == 1) ? "(%s " . $this->lng->txt("point") . ")" : "(%s " . $this->lng->txt("points") . ")";
361  $template->setVariable("RESULT_OUTPUT", sprintf($resulttext, $points));
362  }
363  $template->parseCurrentBlock();
364  }
365 
366  $questiontext = $this->object->getQuestionForHTMLOutput();
367  if ($show_inline_feedback && $this->hasInlineFeedback()) {
368  $questiontext .= $this->buildFocusAnchorHtml();
369  }
370  if ($show_question_text == true) {
371  $template->setVariable("QUESTIONTEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($questiontext, true));
372  }
373  $questionoutput = $template->get();
374  $feedback = ($show_feedback && !$this->isTestPresentationContext()) ? $this->getGenericFeedbackOutput((int) $active_id, $pass) : "";
375  if ($feedback !== '') {
376  $cssClass = (
377  $this->hasCorrectSolution($active_id, $pass) ?
379  );
380 
381  $solutiontemplate->setVariable("ILC_FB_CSS_CLASS", $cssClass);
382  $solutiontemplate->setVariable("FEEDBACK", ilLegacyFormElementsUtil::prepareTextareaOutput($feedback, true));
383  }
384  $solutiontemplate->setVariable("SOLUTION_OUTPUT", $questionoutput);
385 
386  $solutionoutput = $solutiontemplate->get();
387 
388  if (!$show_question_only) {
389  // get page object output
390  $solutionoutput = $this->getILIASPage($solutionoutput);
391  }
392  return $solutionoutput;
393  }
394 
395  private function generateCorrectness(
396  string $user_solution,
397  string $answer_id,
398  float $answer_points,
399  float $maximum_points
400  ): int {
401  if ($user_solution === $answer_id
402  && $answer_points === $maximum_points
403  || $user_solution !== $answer_id
404  && $answer_points === 0.0
405  ) {
406  return self::CORRECTNESS_OK;
407  }
408 
409  if ($user_solution === $answer_id
410  && $answer_points > 0.0) {
411  return self::CORRECTNESS_MOSTLY_OK;
412  }
413 
414  return self::CORRECTNESS_NOT_OK;
415  }
416 
417  public function getPreview($show_question_only = false, $showInlineFeedback = false): string
418  {
419  $keys = $this->getChoiceKeys();
420 
421  $template = new ilTemplate("tpl.il_as_qpl_mc_sr_output.html", true, true, "Modules/TestQuestionPool");
422  foreach ($keys as $answer_id) {
423  $answer = $this->object->answers[$answer_id];
424  if ($answer->hasImage()) {
425  if ($this->object->getThumbSize()) {
426  $template->setCurrentBlock("preview");
427  $template->setVariable("URL_PREVIEW", $this->object->getImagePathWeb() . $answer->getImage());
428  $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
429  $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('media/enlarge.svg'));
430  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $answer->getImage());
431  list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
432  $alt = $answer->getImage();
433  if (strlen($answer->getAnswertext())) {
434  $alt = $answer->getAnswertext();
435  }
436  $alt = preg_replace("/<[^>]*?>/", "", $alt);
437  $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
438  $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
439  $template->parseCurrentBlock();
440  } else {
441  $template->setCurrentBlock("answer_image");
442  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $answer->getImage());
443  list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
444  $alt = $answer->getImage();
445  if (strlen($answer->getAnswertext())) {
446  $alt = $answer->getAnswertext();
447  }
448  $alt = preg_replace("/<[^>]*?>/", "", $alt);
449  $template->setVariable("ATTR", $attr);
450  $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
451  $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
452  $template->parseCurrentBlock();
453  }
454  }
455  if ($showInlineFeedback && is_object($this->getPreviewSession())) {
456  $this->populateInlineFeedback($template, $answer_id, $this->getPreviewSession()->getParticipantsSolution());
457  }
458  $template->setCurrentBlock("answer_row");
459  $template->setVariable("QID", $this->object->getId() . 'ID');
460  $template->setVariable("ANSWER_ID", $answer_id);
461  $template->setVariable("ANSWER_TEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($answer->getAnswertext(), true));
462 
463  if (is_object($this->getPreviewSession())) {
464  $user_solution = $this->getPreviewSession()->getParticipantsSolution() ?? '';
465  if ((string) $user_solution === (string) $answer_id) {
466  $template->setVariable("CHECKED_ANSWER", " checked=\"checked\"");
467  }
468  }
469 
470  $template->parseCurrentBlock();
471  }
472  $questiontext = $this->object->getQuestionForHTMLOutput();
473  if ($showInlineFeedback && $this->hasInlineFeedback()) {
474  $questiontext .= $this->buildFocusAnchorHtml();
475  }
476  $template->setVariable("QUESTIONTEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($questiontext, true));
477  $questionoutput = $template->get();
478  if (!$show_question_only) {
479  // get page object output
480  $questionoutput = $this->getILIASPage($questionoutput);
481  }
482  return $questionoutput;
483  }
484 
485  // hey: prevPassSolutions - pass will be always available from now on
486  public function getTestOutput($active_id, $pass, $is_postponed = false, $use_post_solutions = false, $show_feedback = false): string
487  // hey.
488  {
489  $keys = $this->getChoiceKeys();
490 
491  // get the solution of the user for the active pass or from the last pass if allowed
492  $user_solution = "";
493  if ($active_id) {
494  $solutions = $this->object->getTestOutputSolutions($active_id, $pass);
495  foreach ($solutions as $idx => $solution_value) {
496  $user_solution = $solution_value['value1'];
497  }
498  }
499 
500  $template = new ilTemplate("tpl.il_as_qpl_mc_sr_output.html", true, true, "Modules/TestQuestionPool");
501  foreach ($keys as $answer_id) {
502  $answer = $this->object->answers[$answer_id];
503  if ($answer->hasImage()) {
504  if ($this->object->getThumbSize()) {
505  $template->setCurrentBlock("preview");
506  $template->setVariable("URL_PREVIEW", $this->object->getImagePathWeb() . $answer->getImage());
507  $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
508  $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('media/enlarge.svg'));
509  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $answer->getImage());
510  list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
511  $alt = $answer->getImage();
512  if (strlen($answer->getAnswertext())) {
513  $alt = $answer->getAnswertext();
514  }
515  $alt = preg_replace("/<[^>]*?>/", "", $alt);
516  $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
517  $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
518  $template->parseCurrentBlock();
519  } else {
520  $template->setCurrentBlock("answer_image");
521  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $answer->getImage());
522  list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
523  $alt = $answer->getImage();
524  if (strlen($answer->getAnswertext())) {
525  $alt = $answer->getAnswertext();
526  }
527  $alt = preg_replace("/<[^>]*?>/", "", $alt);
528  $template->setVariable("ATTR", $attr);
529  $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
530  $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
531  $template->parseCurrentBlock();
532  }
533  }
534  if ($show_feedback) {
535  $feedbackOutputRequired = false;
536 
537  switch ($this->object->getSpecificFeedbackSetting()) {
538  case 1:
539  $feedbackOutputRequired = true;
540  break;
541 
542  case 2:
543  if ((string) $user_solution === (string) $answer_id) {
544  $feedbackOutputRequired = true;
545  }
546  break;
547 
548  case 3:
549  if ($this->object->getAnswer($answer_id)->getPoints() > 0) {
550  $feedbackOutputRequired = true;
551  }
552  break;
553  }
554 
555  if ($feedbackOutputRequired) {
556  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation(
557  $this->object->getId(),
558  0,
559  $answer_id
560  );
561  if (strlen($fb)) {
562  $template->setCurrentBlock("feedback");
563  $template->setVariable("FEEDBACK", ilLegacyFormElementsUtil::prepareTextareaOutput($fb, true));
564  $template->parseCurrentBlock();
565  }
566  }
567  }
568  $template->setCurrentBlock("answer_row");
569  $template->setVariable("ANSWER_ID", $answer_id);
570  $template->setVariable("ANSWER_TEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($answer->getAnswertext(), true));
571  if ((string) $user_solution === (string) $answer_id) {
572  $template->setVariable("CHECKED_ANSWER", " checked=\"checked\"");
573  }
574  $template->parseCurrentBlock();
575  }
576  $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
577  $questionoutput = $template->get();
578  $pageoutput = $this->outQuestionPage("", $is_postponed, $active_id, $questionoutput, $show_feedback);
579  return $pageoutput;
580  }
581 
582  /*
583  * Create the key index numbers for the array of choices
584  *
585  * @return array
586  */
587  public function getChoiceKeys()
588  {
589  $choice_keys = array_keys($this->object->answers);
590 
591  if ($this->object->getShuffle()) {
592  $choice_keys = $this->object->getShuffler()->transform($choice_keys);
593  }
594 
595  return $choice_keys;
596  }
597 
598  public function getSpecificFeedbackOutput(array $user_solution): string
599  {
600  // No return value, this question type supports inline specific feedback.
601  $output = "";
603  }
604 
606  {
607  $types = $_POST["types"] ?? '0';
608 
609  $this->object->setShuffle($_POST["shuffle"] ?? '0');
610  $this->object->setMultilineAnswerSetting($types);
611 
612  if (isset($_POST['choice']) && isset($_POST['choice']['imagename']) && is_array($_POST['choice']['imagename']) && $types === '1') {
613  $this->object->setIsSingleline(true);
614  $this->tpl->setOnScreenMessage('info', $this->lng->txt('info_answer_type_change'), true);
615  } else {
616  $this->object->setIsSingleline($types === '0' ? true : false);
617  }
618  if (isset($_POST["thumb_size"])
619  && (int) $_POST["thumb_size"] !== $this->object->getThumbSize()) {
620  $this->object->setThumbSize((int) $_POST["thumb_size"]);
621  $this->rebuild_thumbnails = true;
622  }
623  }
624 
625  public function populateQuestionSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline = false): ilPropertyFormGUI
626  {
627  // shuffle
628  $shuffle = new ilCheckboxInputGUI($this->lng->txt("shuffle_answers"), "shuffle");
629  $shuffle->setValue(1);
630  $shuffle->setChecked($this->object->getShuffle());
631  $shuffle->setRequired(false);
632  $form->addItem($shuffle);
633 
634  if ($this->object->getId()) {
635  $hidden = new ilHiddenInputGUI("", "ID");
636  $hidden->setValue($this->object->getId());
637  $form->addItem($hidden);
638  }
639 
640  if (!$this->object->getSelfAssessmentEditingMode()) {
641  // Answer types
642  $types = new ilSelectInputGUI($this->lng->txt("answer_types"), "types");
643  $types->setRequired(false);
644  $types->setValue(($is_singleline) ? 0 : 1);
645  $types->setOptions(
646  [
647  0 => $this->lng->txt('answers_singleline'),
648  1 => $this->lng->txt('answers_multiline'),
649  ]
650  );
651  $form->addItem($types);
652  }
653 
654  if ($is_singleline) {
655  // thumb size
656  $thumb_size = new ilNumberInputGUI($this->lng->txt("thumb_size"), "thumb_size");
657  $thumb_size->setSuffix($this->lng->txt("thumb_size_unit_pixel"));
658  $thumb_size->setMinValue($this->object->getMinimumThumbSize());
659  $thumb_size->setMaxValue($this->object->getMaximumThumbSize());
660  $thumb_size->setDecimals(0);
661  $thumb_size->setSize(6);
662  $thumb_size->setInfo($this->lng->txt('thumb_size_info'));
663  $thumb_size->setValue($this->object->getThumbSize());
664  $thumb_size->setRequired(true);
665  } else {
666  $thumb_size = new ilHiddenInputGUI('thumb_size');
667  $thumb_size->setValue($this->object->getThumbSize());
668  }
669  $form->addItem($thumb_size);
670 
671  return $form;
672  }
673 
684  {
685  return [];
686  }
687 
688  public function writeAnswerSpecificPostData(ilPropertyFormGUI $form): void
689  {
690  // Delete all existing answers and create new answers from the form data
691  $this->object->flushAnswers();
692  $choice = $this->cleanupAnswerText($_POST['choice'], $this->object->isSingleline() === false);
693  if (!$this->object->isSingleline()) {
694  foreach ($choice['answer'] as $index => $answer) {
695  $answertext = $answer;
696  $this->object->addAnswer(
697  $answertext,
698  $this->refinery->kindlyTo()->float()->transform($choice['points'][$index]),
699  $index,
700  null,
701  $choice['answer_id'][$index]
702  );
703  }
704 
705  return;
706  }
707 
708  foreach ($choice['answer'] as $index => $answertext) {
709  $answertext = htmlentities($answertext);
710  $picturefile = $choice['imagename'][$index] ?? null;
711  $file_org_name = $_FILES['choice']['name']['image'][$index] ?? '';
712  $file_temp_name = $_FILES['choice']['tmp_name']['image'][$index] ?? '';
713 
714  if ($file_temp_name !== '') {
715  // check suffix
716  $file_name_parts = explode(".", $file_org_name);
717  $suffix = strtolower(array_pop($file_name_parts));
718  if (in_array($suffix, ["jpg", "jpeg", "png", "gif"])) {
719  // upload image
720  $filename = $this->object->buildHashedImageFilename($file_org_name);
721  if ($this->object->setImageFile($filename, $file_temp_name) == 0) {
722  $picturefile = $filename;
723  }
724  }
725  }
726 
727  $points = (float) str_replace(',', '.', $choice['points'][$index]);
728  $this->object->addAnswer(
729  $answertext,
730  $points,
731  $index,
732  $picturefile,
733  $choice['answer_id'][$index]
734  );
735  }
736 
737  if ($this->rebuild_thumbnails) {
738  $this->object->setAnswers(
739  $this->object->rebuildThumbnails(
740  $this->object->isSingleline(),
741  $this->object->getThumbSize(),
742  $this->object->getImagePath(),
743  $this->object->getAnswers()
744  )
745  );
746  }
747  }
748 
749  public function populateAnswerSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline = false): ilPropertyFormGUI
750  {
751  $choices = new ilSingleChoiceWizardInputGUI($this->lng->txt("answers"), "choice");
752  $choices->setRequired(true);
753  $choices->setQuestionObject($this->object);
754  $choices->setSingleline($is_singleline);
755  $choices->setAllowMove(false);
756  if ($this->object->getSelfAssessmentEditingMode()) {
757  $choices->setSize(40);
758  }
759  if ($this->object->getAnswerCount() == 0) {
760  $this->object->addAnswer("", 0, 0);
761  }
762  $choices->setValues(array_map(
763  function (ASS_AnswerBinaryStateImage $value) {
764  $value->setAnswerText(html_entity_decode($value->getAnswerText()));
765  return $value;
766  },
767  $this->object->getAnswers()
768  ));
769  $form->addItem($choices);
770  return $form;
771  }
772 
783  {
784  return [];
785  }
786 
793  public function getAggregatedAnswersView(array $relevant_answers): string
794  {
795  return $this->renderAggregateView(
796  $this->aggregateAnswers($relevant_answers, $this->object->getAnswers())
797  )->get();
798  }
799 
800  public function aggregateAnswers($relevant_answers_chosen, $answers_defined_on_question): array
801  {
802  $aggregate = [];
803  foreach ($answers_defined_on_question as $answer) {
804  $aggregated_info_for_answer = [];
805  $aggregated_info_for_answer['answertext'] = $answer->getAnswerText();
806  $aggregated_info_for_answer['count_checked'] = 0;
807 
808  foreach ($relevant_answers_chosen as $relevant_answer) {
809  if ($relevant_answer['value1'] == $answer->getOrder()) {
810  $aggregated_info_for_answer['count_checked']++;
811  }
812  }
813  $aggregated_info_for_answer['count_unchecked'] =
814  ceil(count($relevant_answers_chosen) / count($answers_defined_on_question))
815  - $aggregated_info_for_answer['count_checked'];
816 
817  $aggregate[] = $aggregated_info_for_answer;
818  }
819  return $aggregate;
820  }
821 
827  public function renderAggregateView($aggregate): ilTemplate
828  {
829  $tpl = new ilTemplate('tpl.il_as_aggregated_answers_table.html', true, true, "Modules/TestQuestionPool");
830 
831  $tpl->setCurrentBlock('headercell');
832  $tpl->setVariable('HEADER', $this->lng->txt('tst_answer_aggr_answer_header'));
834 
835  $tpl->setCurrentBlock('headercell');
836  $tpl->setVariable('HEADER', $this->lng->txt('tst_answer_aggr_frequency_header'));
838 
839  foreach ($aggregate as $line_data) {
840  $tpl->setCurrentBlock('aggregaterow');
841  $tpl->setVariable('OPTION', $line_data['answertext']);
842  $tpl->setVariable('COUNT', $line_data['count_checked']);
844  }
845  return $tpl;
846  }
847 
848  private function populateInlineFeedback($template, $answer_id, $user_solution): void
849  {
850  $feedbackOutputRequired = false;
851 
852  switch ($this->object->getSpecificFeedbackSetting()) {
853  case 1:
854  $feedbackOutputRequired = true;
855  break;
856 
857  case 2:
858  if (strcmp((string) $user_solution, $answer_id) == 0) {
859  $feedbackOutputRequired = true;
860  }
861  break;
862 
863  case 3:
864  if ($this->object->getAnswer($answer_id)->getPoints() > 0) {
865  $feedbackOutputRequired = true;
866  }
867  break;
868  }
869 
870  if ($feedbackOutputRequired) {
871  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation($this->object->getId(), 0, $answer_id);
872  if (strlen($fb)) {
873  $template->setCurrentBlock("feedback");
874  $template->setVariable("FEEDBACK", ilLegacyFormElementsUtil::prepareTextareaOutput($fb, true));
875  $template->parseCurrentBlock();
876  }
877  }
878  }
879 
880  public function getAnswersFrequency($relevantAnswers, $questionIndex): array
881  {
882  $agg = $this->aggregateAnswers($relevantAnswers, $this->object->getAnswers());
883 
884  $answers = [];
885 
886  foreach ($agg as $ans) {
887  $answers[] = [
888  'answer' => $ans['answertext'],
889  'frequency' => $ans['count_checked']
890  ];
891  }
892 
893  return $answers;
894  }
895 
897  {
898  $choices = new ilAssSingleChoiceCorrectionsInputGUI($this->lng->txt("answers"), "choice");
899  $choices->setRequired(true);
900  $choices->setQuestionObject($this->object);
901  $choices->setValues($this->object->getAnswers());
902  $form->addItem($choices);
903  }
904 
909  {
910  $input = $form->getItemByPostVar('choice');
911  $values = $input->getValues();
912 
913  foreach ($this->object->getAnswers() as $index => $answer) {
914  /* @var ASS_AnswerMultipleResponseImage $answer */
915  $points = (float) str_replace(',', '.', $values[$index]->getPoints());
916  $answer->setPoints($points);
917  }
918  }
919 }
hasCorrectSolution($activeId, $passIndex)
populateQuestionSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline=false)
generateCorrectnessIconsForCorrectness(int $correctness)
aggregateAnswers($relevant_answers_chosen, $answers_defined_on_question)
setCurrentBlock(string $blockname=self::DEFAULT_BLOCK)
Sets the template to the given block.
This class represents a selection list property in a property form.
getItemByPostVar(string $a_post_var)
writeAnswerSpecificPostData(ilPropertyFormGUI $form)
Extracts the answer specific values from $_POST and applies them to the data object.
Class for answers with a binary state indicator.
populateInlineFeedback($template, $answer_id, $user_solution)
static getImagePath(string $img, string $module_path="", string $mode="output", bool $offline=false)
get image path (for images located in a template directory)
addBasicQuestionFormProperties(ilPropertyFormGUI $form)
parseCurrentBlock(string $blockname=self::DEFAULT_BLOCK)
Parses the given block.
ilGlobalPageTemplate $tpl
static prepareFormOutput($a_str, bool $a_strip=false)
getSpecificFeedbackOutput(array $user_solution)
getAnswersFrequency($relevantAnswers, $questionIndex)
populateTaxonomyFormSection(ilPropertyFormGUI $form)
generateCorrectness(string $user_solution, string $answer_id, float $answer_points, float $maximum_points)
addQuestionFormCommandButtons(ilPropertyFormGUI $form)
getSolutionOutput( $active_id, $pass=null, $graphicalOutput=false, $result_output=false, $show_question_only=true, $show_feedback=false, $show_correct_solution=false, $show_manual_scoring=false, $show_question_text=true, bool $show_inline_feedback=true)
Get the question solution output.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getTestOutput($active_id, $pass, $is_postponed=false, $use_post_solutions=false, $show_feedback=false)
__construct(VocabulariesInterface $vocabularies)
getPreview($show_question_only=false, $showInlineFeedback=false)
getAfterParticipationSuppressionAnswerPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
Class for single choice questions.
writePostData(bool $always=false)
{}
string $key
Consumer key/client ID value.
Definition: System.php:193
getAfterParticipationSuppressionQuestionPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
populateAnswerSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline=false)
removechoice()
Remove an answer.
static getHtmlPath(string $relative_path)
get url of path
Basic GUI class for assessment questions.
setRequired(bool $a_required)
$filename
Definition: buildRTE.php:78
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
addchoice()
Add a new answer.
This class represents a single choice wizard property in a property form.
getILIASPage(string $html="")
Returns the ILIAS Page around a question.
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="", $inlineFeedbackEnabled=false)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
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...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
__construct($id=-1)
assSingleChoiceGUI constructor
editQuestion($checkonly=false)
Creates an output of the edit form for the question.
removeimagechoice()
Remove an image.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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...
getAggregatedAnswersView(array $relevant_answers)
Returns an html string containing a question specific representation of the answers so far given in t...
getEditAnswersSingleLine($checkonly=false)
Get the single/multiline editing of answers.
setVariable(string $variable, $value='')
Sets the given variable to the given value.
uploadchoice()
Upload an image.
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,)
writeQuestionSpecificPostData(ilPropertyFormGUI $form)
Extracts the question specific values from $_POST and applies them to the data object.
getGenericFeedbackOutput(int $active_id, ?int $pass)