ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.assMultipleChoiceGUI.php
Go to the documentation of this file.
1 <?php
2 
19 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
20 
36 {
37  public $choiceKeys;
38 
47  public function __construct($id = -1)
48  {
50  include_once "./Modules/TestQuestionPool/classes/class.assMultipleChoice.php";
51  $this->object = new assMultipleChoice();
52  if ($id >= 0) {
53  $this->object->loadFromDb($id);
54  }
55  }
56 
60  public function hasInlineFeedback(): bool
61  {
62  return $this->object->feedbackOBJ->isSpecificAnswerFeedbackAvailable($this->object->getId());
63  }
64 
68  protected function writePostData(bool $always = false): int
69  {
70  $hasErrors = (!$always) ? $this->editQuestion(true) : false;
71  if (!$hasErrors) {
72  $is_singleline = $this->getEditAnswersSingleLine();
73  $form = $this->buildEditForm($is_singleline);
74  $form->setValuesByPost();
75  require_once 'Services/Form/classes/class.ilPropertyFormGUI.php';
77  $this->writeQuestionSpecificPostData($form);
78  $this->writeAnswerSpecificPostData($form);
79  $this->saveTaxonomyAssignments();
80  return 0;
81  }
82  return 1;
83  }
84 
92  protected function getEditAnswersSingleLine($checkonly = false): bool
93  {
94  if ($this->object->getSelfAssessmentEditingMode()) {
95  return $this->object->isSingleline();
96  }
97 
98  if ($checkonly) {
99  $types = $_POST['types'] ?? '0';
100  return $types === '0' ? true : false;
101  }
102 
103  $lastChange = $this->object->getLastChange();
104  if (empty($lastChange) && !isset($_POST['types'])) {
105  // a new question is edited
106  return $this->object->getMultilineAnswerSetting() ? false : true;
107  } else {
108  // a saved question is edited
109  return $this->object->isSingleline();
110  }
111  }
112 
120  public function editQuestion($checkonly = false): bool
121  {
122  $save = $this->isSaveCommand();
123  $this->getQuestionTemplate();
124 
125  $is_singleline = $this->getEditAnswersSingleLine($checkonly);
126 
127  $form = $this->buildEditForm($is_singleline);
128 
129  if ($is_singleline) {
130  $form->setMultipart(true);
131  } else {
132  $form->setMultipart(false);
133  }
134 
135  $errors = false;
136 
137  if ($save) {
138  $form->getItemByPostVar('selection_limit')->setMaxValue(count((array) $_POST['choice']['answer']));
139 
140  $form->setValuesByPost();
141  $errors = !$form->checkInput();
142  if ($errors) {
143  $checkonly = false;
144  }
145  }
146 
147  if (!$checkonly) {
148  $this->tpl->setVariable("QUESTION_DATA", $form->getHTML());
149  }
150  return $errors;
151  }
152 
154  {
155  parent::addBasicQuestionFormProperties($form);
156  $form->getItemByPostVar('question')->setInitialRteWidth('100');
157  }
158 
162  public function uploadchoice(): void
163  {
164  $this->writePostData(true);
165  $this->editQuestion();
166  }
167 
171  public function removeimagechoice(): void
172  {
173  $this->writePostData(true);
174  $position = key($_POST['cmd']['removeimagechoice']);
175  $this->object->removeAnswerImage($position);
176  $this->editQuestion();
177  }
178 
182  public function addchoice(): void
183  {
184  $this->writePostData(true);
185  $position = key($_POST['cmd']['addchoice']);
186  $this->object->addAnswer("", 0, 0, $position + 1);
187  $this->editQuestion();
188  }
189 
193  public function removechoice(): void
194  {
195  $this->writePostData(true);
196  $position = key($_POST['cmd']['removechoice']);
197  $this->object->deleteAnswer($position);
198  $this->editQuestion();
199  }
200 
214  public function getSolutionOutput(
215  $active_id,
216  $pass = null,
217  $graphicalOutput = false,
218  $result_output = false,
219  $show_question_only = true,
220  $show_feedback = false,
221  $show_correct_solution = false,
222  $show_manual_scoring = false,
223  $show_question_text = true
224  ): string {
225  // shuffle output
226  $keys = $this->getChoiceKeys();
227 
228  // get the solution of the user for the active pass or from the last pass if allowed
229  $user_solution = array();
230  if (($active_id > 0) && (!$show_correct_solution)) {
231  $solutions = $this->object->getSolutionValues($active_id, $pass);
232  foreach ($solutions as $idx => $solution_value) {
233  array_push($user_solution, $solution_value["value1"]);
234  }
235  } else {
236  // take the correct solution instead of the user solution
237  foreach ($this->object->answers as $index => $answer) {
238  $points_checked = $answer->getPointsChecked();
239  $points_unchecked = $answer->getPointsUnchecked();
240  if ($points_checked > $points_unchecked) {
241  if ($points_checked > 0) {
242  array_push($user_solution, $index);
243  }
244  }
245  }
246  }
247 
248  // generate the question output
249  include_once "./Services/UICore/classes/class.ilTemplate.php";
250  $template = new ilTemplate("tpl.il_as_qpl_mc_mr_output_solution.html", true, true, "Modules/TestQuestionPool");
251  $solutiontemplate = new ilTemplate("tpl.il_as_tst_solution_output.html", true, true, "Modules/TestQuestionPool");
252  foreach ($keys as $answer_id) {
253  $answer = $this->object->answers[$answer_id];
254  if (($active_id > 0) && (!$show_correct_solution)) {
255  if ($graphicalOutput) {
256  // output of ok/not ok icons for user entered solutions
257  $ok = false;
258  $checked = false;
259  foreach ($user_solution as $mc_solution) {
260  if (strcmp($mc_solution, $answer_id) == 0) {
261  $checked = true;
262  }
263  }
264  if ($checked) {
265  if ($answer->getPointsChecked() > $answer->getPointsUnchecked()) {
266  $ok = self::CORRECTNESS_OK;
267  } else {
268  $ok = self::CORRECTNESS_NOT_OK;
269  }
270  } else {
271  if ($answer->getPointsChecked() > $answer->getPointsUnchecked()) {
272  $ok = self::CORRECTNESS_NOT_OK;
273  } else {
274  $ok = self::CORRECTNESS_OK;
275  }
276  }
277  $icon = $this->generateCorrectnessIconsForCorrectness($ok);
278  $template->setCurrentBlock("icon_ok");
279  $template->setVariable("ICON_OK", $icon);
280  $template->parseCurrentBlock();
281  }
282  }
283  if (strlen($answer->getImage())) {
284  $template->setCurrentBlock("answer_image");
285  if ($this->object->getThumbSize()) {
286  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $answer->getImage());
287  } else {
288  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $answer->getImage());
289  }
290  $alt = $answer->getImage();
291  if (strlen($answer->getAnswertext())) {
292  $alt = $answer->getAnswertext();
293  }
294  $alt = preg_replace("/<[^>]*?>/", "", $alt);
295  $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
296  $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
297  $template->parseCurrentBlock();
298  }
299 
300  if ($show_feedback) {
301  if ($this->object->getSpecificFeedbackSetting() == 2) {
302  foreach ($user_solution as $mc_solution) {
303  if (strcmp($mc_solution, $answer_id) == 0) {
304  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation(
305  $this->object->getId(),
306  0,
307  $answer_id
308  );
309  if (strlen($fb)) {
310  $template->setCurrentBlock("feedback");
311  $template->setVariable("FEEDBACK", $this->object->prepareTextareaOutput($fb, true));
312  $template->parseCurrentBlock();
313  }
314  }
315  }
316  }
317 
318  if ($this->object->getSpecificFeedbackSetting() == 1) {
319  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation(
320  $this->object->getId(),
321  0,
322  $answer_id
323  );
324  if (strlen($fb)) {
325  $template->setCurrentBlock("feedback");
326  $template->setVariable("FEEDBACK", $this->object->prepareTextareaOutput($fb, true));
327  $template->parseCurrentBlock();
328  }
329  }
330 
331  if ($this->object->getSpecificFeedbackSetting() == 3) {
332  $answer = $this->object->getAnswer($answer_id);
333 
334  if ($answer->getPoints() > 0) {
335  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation(
336  $this->object->getId(),
337  0,
338  $answer_id
339  );
340  if (strlen($fb)) {
341  $template->setCurrentBlock("feedback");
342  $template->setVariable("FEEDBACK", $this->object->prepareTextareaOutput($fb, true));
343  $template->parseCurrentBlock();
344  }
345  }
346  }
347  }
348  $template->setCurrentBlock("answer_row");
349  $template->setVariable("ANSWER_TEXT", $this->object->prepareTextareaOutput($answer->getAnswertext(), true));
350  $checked = false;
351  if ($result_output) {
352  $pointschecked = $this->object->answers[$answer_id]->getPointsChecked();
353  $pointsunchecked = $this->object->answers[$answer_id]->getPointsUnchecked();
354  $resulttextchecked = ($pointschecked == 1) || ($pointschecked == -1) ? "%s " . $this->lng->txt("point") : "%s " . $this->lng->txt("points");
355  $resulttextunchecked = ($pointsunchecked == 1) || ($pointsunchecked == -1) ? "%s " . $this->lng->txt("point") : "%s " . $this->lng->txt("points");
356  $template->setVariable("RESULT_OUTPUT", sprintf("(" . $this->lng->txt("checkbox_checked") . " = $resulttextchecked, " . $this->lng->txt("checkbox_unchecked") . " = $resulttextunchecked)", $pointschecked, $pointsunchecked));
357  }
358  foreach ($user_solution as $mc_solution) {
359  if (strcmp($mc_solution, $answer_id) == 0) {
360  if ($this->renderPurposeSupportsFormHtml() || $this->isRenderPurposePrintPdf()) {
361  $template->setVariable("SOLUTION_IMAGE", ilUtil::getHtmlPath(ilUtil::getImagePath("checkbox_checked.png")));
362  $template->setVariable("SOLUTION_ALT", $this->lng->txt("checked"));
363  } else {
364  $template->setVariable('QID', $this->object->getId());
365  $template->setVariable('SUFFIX', $show_correct_solution ? 'bestsolution' : 'usersolution');
366  $template->setVariable('SOLUTION_VALUE', $answer_id);
367  $template->setVariable('SOLUTION_CHECKED', 'checked');
368  }
369  $checked = true;
370  }
371  }
372  if (!$checked) {
373  if ($this->renderPurposeSupportsFormHtml() || $this->isRenderPurposePrintPdf()) {
374  $template->setVariable("SOLUTION_IMAGE", ilUtil::getHtmlPath(ilUtil::getImagePath("checkbox_unchecked.png")));
375  $template->setVariable("SOLUTION_ALT", $this->lng->txt("unchecked"));
376  } else {
377  $template->setVariable('QID', $this->object->getId());
378  $template->setVariable('SUFFIX', $show_correct_solution ? 'bestsolution' : 'usersolution');
379  $template->setVariable('SOLUTION_VALUE', $answer_id);
380  }
381  }
382  $template->parseCurrentBlock();
383  }
384  $questiontext = $this->object->getQuestionForHTMLOutput();
385  if ($show_feedback && $this->hasInlineFeedback()) {
386  $questiontext .= $this->buildFocusAnchorHtml();
387  }
388  if ($show_question_text == true) {
389  $template->setVariable("QUESTIONTEXT", $this->object->prepareTextareaOutput($questiontext, true));
390  }
391  $questionoutput = $template->get();
392  $feedback = ($show_feedback && !$this->isTestPresentationContext()) ? $this->getGenericFeedbackOutput((int) $active_id, $pass) : "";
393 
394  if (strlen($feedback)) {
395  $cssClass = (
396  $this->hasCorrectSolution($active_id, $pass) ?
398  );
399 
400  $solutiontemplate->setVariable("ILC_FB_CSS_CLASS", $cssClass);
401  $solutiontemplate->setVariable("FEEDBACK", $this->object->prepareTextareaOutput($feedback, true));
402  }
403  $solutiontemplate->setVariable("SOLUTION_OUTPUT", $questionoutput);
404 
405  $solutionoutput = $solutiontemplate->get();
406 
407  if (!$show_question_only) {
408  // get page object output
409  $solutionoutput = $this->getILIASPage($solutionoutput);
410  }
411  return $solutionoutput;
412  }
413 
414  public function getPreview($show_question_only = false, $showInlineFeedback = false): string
415  {
416  $user_solution = is_object($this->getPreviewSession()) ? (array) $this->getPreviewSession()->getParticipantsSolution() : array();
417  // shuffle output
418  $keys = $this->getChoiceKeys();
419 
420  // generate the question output
421  include_once "./Services/UICore/classes/class.ilTemplate.php";
422  $this->tpl->addOnLoadCode('ilAssMultipleChoiceCharCounterInit();');
423  $template = new ilTemplate("tpl.il_as_qpl_mc_mr_output.html", true, true, "Modules/TestQuestionPool");
424  foreach ($keys as $answer_id) {
425  $answer = $this->object->answers[$answer_id];
426  if (strlen($answer->getImage())) {
427  if ($this->object->getThumbSize()) {
428  $template->setCurrentBlock("preview");
429  $template->setVariable("URL_PREVIEW", $this->object->getImagePathWeb() . $answer->getImage());
430  $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
431  $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('enlarge.svg'));
432  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $answer->getImage());
433  list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
434  $alt = $answer->getImage();
435  if (strlen($answer->getAnswertext())) {
436  $alt = $answer->getAnswertext();
437  }
438  $alt = preg_replace("/<[^>]*?>/", "", $alt);
439  $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
440  $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
441  $template->parseCurrentBlock();
442  } else {
443  $template->setCurrentBlock("answer_image");
444  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $answer->getImage());
445  list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
446  $alt = $answer->getImage();
447  if (strlen($answer->getAnswertext())) {
448  $alt = $answer->getAnswertext();
449  }
450  $alt = preg_replace("/<[^>]*?>/", "", $alt);
451  $template->setVariable("ATTR", $attr);
452  $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
453  $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
454  $template->parseCurrentBlock();
455  }
456  }
457 
458  if ($showInlineFeedback) {
459  $this->populateSpecificFeedbackInline($user_solution, $answer_id, $template);
460  }
461 
462  $template->setCurrentBlock("answer_row");
463  $template->setVariable("QID", $this->object->getId());
464  $template->setVariable("ANSWER_ID", $answer_id);
465  $template->setVariable("ANSWER_TEXT", $this->object->prepareTextareaOutput($answer->getAnswertext(), true));
466  foreach ($user_solution as $mc_solution) {
467  if (strcmp($mc_solution, $answer_id) == 0) {
468  $template->setVariable("CHECKED_ANSWER", " checked=\"checked\"");
469  }
470  }
471  $template->parseCurrentBlock();
472  }
473  if ($this->object->getSelectionLimit()) {
474  $template->setVariable('SELECTION_LIMIT_HINT', sprintf(
475  $this->lng->txt('ass_mc_sel_lim_hint'),
476  $this->object->getSelectionLimit(),
477  $this->object->getAnswerCount()
478  ));
479 
480  $template->setVariable('SELECTION_LIMIT_VALUE', $this->object->getSelectionLimit());
481  } else {
482  $template->setVariable('SELECTION_LIMIT_VALUE', 'null');
483  }
484  $template->setVariable("QUESTION_ID", $this->object->getId());
485  $questiontext = $this->object->getQuestionForHTMLOutput();
486  if ($showInlineFeedback && $this->hasInlineFeedback()) {
487  $questiontext .= $this->buildFocusAnchorHtml();
488  }
489  $template->setVariable("QUESTIONTEXT", $this->object->prepareTextareaOutput($questiontext, true));
490  $questionoutput = $template->get();
491  if (!$show_question_only) {
492  // get page object output
493  $questionoutput = $this->getILIASPage($questionoutput);
494  }
495  return $questionoutput;
496  }
497 
507  public function getTestOutput(
508  $active_id,
509  // hey: prevPassSolutions - will be always available from now on
510  $pass,
511  // hey.
512  $is_postponed = false,
513  $use_post_solutions = false,
514  $show_feedback = false
515  ): string {
516  // shuffle output
517  $keys = $this->getChoiceKeys();
518 
519  // get the solution of the user for the active pass or from the last pass if allowed
520  $user_solution = array();
521  if ($active_id) {
522  $solutions = $this->object->getTestOutputSolutions($active_id, $pass);
523  // hey.
524  foreach ($solutions as $idx => $solution_value) {
525  // fau: testNav - don't add the dummy entry for 'none of the above' to the user options
526  if ($solution_value["value1"] == 'mc_none_above') {
527  $this->setUseEmptySolutionInputChecked(true);
528  continue;
529  }
530 
531  $user_solution[] = $solution_value["value1"];
532  // fau.
533  }
534 
535  if (empty($user_solution) && $this->object->getTestPresentationConfig()->isWorkedThrough()) {
536  $this->setUseEmptySolutionInputChecked(true);
537  }
538  }
539  // generate the question output
540  $this->tpl->addJavaScript('Modules/TestQuestionPool/js/ilAssMultipleChoice.js');
541  $this->tpl->addOnLoadCode('ilAssMultipleChoiceCharCounterInit();');
542  include_once "./Services/UICore/classes/class.ilTemplate.php";
543  $template = new ilTemplate("tpl.il_as_qpl_mc_mr_output.html", true, true, "Modules/TestQuestionPool");
544  foreach ($keys as $answer_id) {
545  $answer = $this->object->answers[$answer_id];
546  if (strlen($answer->getImage())) {
547  if ($this->object->getThumbSize()) {
548  $template->setCurrentBlock("preview");
549  $template->setVariable("URL_PREVIEW", $this->object->getImagePathWeb() . $answer->getImage());
550  $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
551  $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('enlarge.svg'));
552  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $answer->getImage());
553  list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
554  $alt = $answer->getImage();
555  if (strlen($answer->getAnswertext())) {
556  $alt = $answer->getAnswertext();
557  }
558  $alt = preg_replace("/<[^>]*?>/", "", $alt);
559  $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
560  $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
561  $template->parseCurrentBlock();
562  } else {
563  $template->setCurrentBlock("answer_image");
564  $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $answer->getImage());
565  list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
566  $alt = $answer->getImage();
567  if (strlen($answer->getAnswertext())) {
568  $alt = $answer->getAnswertext();
569  }
570  $alt = preg_replace("/<[^>]*?>/", "", $alt);
571  $template->setVariable("ATTR", $attr);
572  $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
573  $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
574  $template->parseCurrentBlock();
575  }
576  }
577 
578  if ($show_feedback) {
579  $this->populateSpecificFeedbackInline($user_solution, $answer_id, $template);
580  }
581 
582  $template->setCurrentBlock("answer_row");
583  $template->setVariable("QID", $this->object->getId());
584  $template->setVariable("ANSWER_ID", $answer_id);
585  $template->setVariable("ANSWER_TEXT", $this->object->prepareTextareaOutput($answer->getAnswertext(), true));
586  foreach ($user_solution as $mc_solution) {
587  if (strcmp($mc_solution, $answer_id) == 0) {
588  $template->setVariable("CHECKED_ANSWER", " checked=\"checked\"");
589  }
590  }
591  $template->parseCurrentBlock();
592  }
593 
594  $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
595  $template->setVariable("QUESTION_ID", $this->object->getId());
596  if ($this->object->getSelectionLimit()) {
597  $template->setVariable('SELECTION_LIMIT_HINT', sprintf(
598  $this->lng->txt('ass_mc_sel_lim_hint'),
599  $this->object->getSelectionLimit(),
600  $this->object->getAnswerCount()
601  ));
602 
603  $template->setVariable('SELECTION_LIMIT_VALUE', $this->object->getSelectionLimit());
604  } else {
605  $template->setVariable('SELECTION_LIMIT_VALUE', 'null');
606  }
607  $questionoutput = $template->get();
608  $pageoutput = $this->outQuestionPage("", $is_postponed, $active_id, $questionoutput, $show_feedback);
609  return $pageoutput;
610  }
611 
612  protected $useEmptySolutionInputChecked = false;
613 
614  public function isUseEmptySolutionInputChecked(): bool
615  {
617  }
618 
620  {
621  $this->useEmptySolutionInputChecked = $useEmptySolutionInputChecked;
622  }
623 
624  protected function getUseUnchangedAnswerCheckboxHtml(): string
625  {
626  // hey: prevPassSolutions - use abstracted template to share with other purposes of this kind
627  $this->tpl->addJavaScript('Modules/TestQuestionPool/js/ilAssMultipleChoice.js');
628 
629  $tpl = new ilTemplate('tpl.tst_question_additional_behaviour_checkbox.html', true, true, 'Modules/TestQuestionPool');
630 
631  // HEY: affects next if (!) /// noneAboveChecked repaired but disabled because the checked input ..
632  if (false) { // .. makes the qstEditController initialize the "edit" instead of the "answered" state
633  if ($this->isUseEmptySolutionInputChecked()) {
634  $tpl->setCurrentBlock('checked');
635  $tpl->touchBlock('checked');
637  }
638  }
639 
640  $tpl->setCurrentBlock('checkbox');
641  $tpl->setVariable('TXT_FORCE_FORM_DIFF_LABEL', $this->object->getTestPresentationConfig()->getUseUnchangedAnswerLabel());
643  // hey.
644  return $tpl->get();
645  }
646 
647  public function getPresentationJavascripts(): array
648  {
649  return array('Modules/TestQuestionPool/js/ilAssMultipleChoice.js');
650  }
651 
657  public function getChoiceKeys(): array
658  {
659  $choiceKeys = array_keys($this->object->answers);
660 
661  if ($this->object->getShuffle()) {
662  $choiceKeys = $this->object->getShuffler()->transform($choiceKeys);
663  }
664 
665  return $choiceKeys;
666  }
667 
668  public function getSpecificFeedbackOutput(array $userSolution): string
669  {
670  // No return value, this question type supports inline specific feedback.
671  $output = "";
672  return $this->object->prepareTextareaOutput($output, true);
673  }
674 
676  {
677  $this->object->setShuffle($_POST["shuffle"] ?? '0');
678 
679  $selectionLimit = (int) $form->getItemByPostVar('selection_limit')->getValue();
680  $this->object->setSelectionLimit($selectionLimit > 0 ? $selectionLimit : null);
681 
682  if (isset($_POST['feedback_setting'])) {
683  $this->object->setSpecificFeedbackSetting($_POST['feedback_setting']);
684  }
685 
686  $types = (int) ($_POST['types'] ?? '0');
687  $this->object->setMultilineAnswerSetting($types);
688  if (isset($_POST['choice']['imagename']) && is_array($_POST['choice']['imagename']) && $types === 1) {
689  $this->object->setIsSingleline(true);
690  $this->tpl->setOnScreenMessage('info', $this->lng->txt('info_answer_type_change'), true);
691  } else {
692  $this->object->setIsSingleline(($types === 0) ? true : false);
693  }
694  if (isset($_POST["thumb_size"])) {
695  $this->object->setThumbSize((int) $_POST["thumb_size"]);
696  }
697  }
698 
699  public function writeAnswerSpecificPostData(ilPropertyFormGUI $form): void
700  {
701  // Delete all existing answers and create new answers from the form data
702  $this->object->flushAnswers();
703  $choice = $this->cleanupAnswerText($_POST['choice'], $this->object->isSingleline() === false);
704  if ($this->object->isSingleline()) {
705  foreach ($choice['answer'] as $index => $answertext) {
706  $answertext = htmlentities($answertext);
707  $picturefile = $choice['imagename'][$index] ?? '';
708  $file_org_name = $_FILES['choice']['name']['image'][$index] ?? '';
709  $file_temp_name = $_FILES['choice']['tmp_name']['image'][$index] ?? '';
710 
711  if ($file_temp_name !== '') {
712  // check suffix
713  $parts = explode(".", $file_org_name);
714  $suffix = strtolower(array_pop($parts));
715  if (in_array($suffix, ["jpg", "jpeg", "png", "gif"])) {
716  // upload image
717  $filename = $this->object->buildHashedImageFilename($file_org_name);
718  if ($this->object->setImageFile($filename, $file_temp_name) == 0) {
719  $picturefile = $filename;
720  }
721  }
722  }
723 
724  $this->object->addAnswer(
725  $answertext,
726  (float) str_replace(',', '.', $choice['points'][$index]),
727  (float) str_replace(',', '.', $choice['points_unchecked'][$index]),
728  $index,
729  $picturefile,
730  $choice['answer_id'][$index]
731  );
732  }
733  } else {
734  foreach ($choice['answer'] as $index => $answer) {
735  $answertext = $answer;
736  $this->object->addAnswer(
737  $answertext,
738  (float) str_replace(',', '.', $choice['points'][$index]),
739  (float) str_replace(',', '.', $choice['points_unchecked'][$index]),
740  $index,
741  '',
742  (int) $choice['answer_id'][$index]
743  );
744  }
745  }
746  }
747 
748  public function populateQuestionSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline = false): ilPropertyFormGUI
749  {
750  // shuffle
751  $shuffle = new ilCheckboxInputGUI($this->lng->txt("shuffle_answers"), "shuffle");
752  $shuffle->setValue(1);
753  $shuffle->setChecked($this->object->getShuffle());
754  $shuffle->setRequired(false);
755  $form->addItem($shuffle);
756 
757  require_once 'Services/Form/classes/class.ilNumberInputGUI.php';
758  $selLim = new ilNumberInputGUI($this->lng->txt('ass_mc_sel_lim_setting'), 'selection_limit');
759  $selLim->setInfo($this->lng->txt('ass_mc_sel_lim_setting_desc'));
760  $selLim->setSize(2);
761  $selLim->setRequired(false);
762  $selLim->allowDecimals(false);
763  $selLim->setMinvalueShouldBeGreater(false);
764  $selLim->setMaxvalueShouldBeLess(false);
765  $selLim->setMinValue(1);
766  $selLim->setMaxValue($this->object->getAnswerCount());
767  $selLim->setValue($this->object->getSelectionLimit());
768  $form->addItem($selLim);
769 
770  if ($this->object->getId()) {
771  $hidden = new ilHiddenInputGUI("ID");
772  $hidden->setValue($this->object->getId());
773  $form->addItem($hidden);
774  }
775 
776  if (!$this->object->getSelfAssessmentEditingMode()) {
777  // Answer types
778  $types = new ilSelectInputGUI($this->lng->txt("answer_types"), "types");
779  $types->setRequired(false);
780  $types->setValue(($is_singleline) ? 0 : 1);
781  $types->setOptions([
782  0 => $this->lng->txt('answers_singleline'),
783  1 => $this->lng->txt('answers_multiline'),
784  ]);
785  $form->addItem($types);
786  }
787 
788  if ($is_singleline) {
789  // thumb size
790  $thumb_size = new ilNumberInputGUI($this->lng->txt('thumb_size'), "thumb_size");
791  $thumb_size->setSuffix($this->lng->txt('thumb_size_unit_pixel'));
792  $thumb_size->setMinValue($this->object->getMinimumThumbSize());
793  $thumb_size->setMaxValue($this->object->getMaximumThumbSize());
794  $thumb_size->setDecimals(0);
795  $thumb_size->setSize(6);
796  $thumb_size->setInfo($this->lng->txt('thumb_size_info'));
797  $thumb_size->setValue($this->object->getThumbSize());
798  $thumb_size->setRequired(true);
799  } else {
800  $thumb_size = new ilHiddenInputGUI('thumb_size');
801  $thumb_size->setValue($this->object->getThumbSize());
802  }
803  $form->addItem($thumb_size);
804 
805  return $form;
806  }
807 
808  public function populateAnswerSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline = false): ilPropertyFormGUI
809  {
810  // Choices
811  include_once "./Modules/TestQuestionPool/classes/class.ilMultipleChoiceWizardInputGUI.php";
812  $choices = new ilMultipleChoiceWizardInputGUI($this->lng->txt("answers"), "choice");
813  $choices->setRequired(true);
814  $choices->setQuestionObject($this->object);
815  $choices->setSingleline($is_singleline);
816  $choices->setAllowMove(false);
817  if ($this->object->getSelfAssessmentEditingMode()) {
818  $choices->setSize(40);
819  }
820  if ($this->object->getAnswerCount() == 0) {
821  $this->object->addAnswer("", 0, 0, 0);
822  }
823  $choices->setValues(array_map(
824  function (ASS_AnswerMultipleResponseImage $value) {
825  $value->setAnswerText(html_entity_decode($value->getAnswerText()));
826  return $value;
827  },
828  $this->object->getAnswers()
829  ));
830  $form->addItem($choices);
831  return $form;
832  }
833 
844  {
845  return array();
846  }
847 
858  {
859  return array();
860  }
861 
868  public function getAggregatedAnswersView(array $relevant_answers): string
869  {
870  return $this->renderAggregateView(
871  $this->aggregateAnswers($relevant_answers, $this->object->getAnswers())
872  )->get();
873  }
874 
875  public function aggregateAnswers($relevant_answers_chosen, $answers_defined_on_question): array
876  {
877  $aggregate = array();
878  foreach ($answers_defined_on_question as $answer) {
879  $aggregated_info_for_answer = array();
880  $aggregated_info_for_answer['answertext'] = $answer->getAnswerText();
881  $aggregated_info_for_answer['count_checked'] = 0;
882 
883  foreach ($relevant_answers_chosen as $relevant_answer) {
884  if ($relevant_answer['value1'] == $answer->getOrder()) {
885  $aggregated_info_for_answer['count_checked']++;
886  }
887  }
888  $aggregated_info_for_answer['count_unchecked'] =
889  ceil(count($relevant_answers_chosen) / count($answers_defined_on_question))
890  - $aggregated_info_for_answer['count_checked'];
891 
892  $aggregate[] = $aggregated_info_for_answer;
893  }
894  return $aggregate;
895  }
896 
902  public function renderAggregateView($aggregate): ilTemplate
903  {
904  $tpl = new ilTemplate('tpl.il_as_aggregated_answers_table.html', true, true, "Modules/TestQuestionPool");
905 
906  $tpl->setCurrentBlock('headercell');
907  $tpl->setVariable('HEADER', $this->lng->txt('tst_answer_aggr_answer_header'));
909 
910  $tpl->setCurrentBlock('headercell');
911  $tpl->setVariable('HEADER', $this->lng->txt('tst_answer_aggr_frequency_header'));
913 
914  foreach ($aggregate as $line_data) {
915  $tpl->setCurrentBlock('aggregaterow');
916  $tpl->setVariable('OPTION', $line_data['answertext']);
917  $tpl->setVariable('COUNT', $line_data['count_checked']);
919  }
920  return $tpl;
921  }
922 
923  private function populateSpecificFeedbackInline($user_solution, $answer_id, $template): void
924  {
925  if ($this->object->getSpecificFeedbackSetting() == 2) {
926  foreach ($user_solution as $mc_solution) {
927  if (strcmp($mc_solution, $answer_id) == 0) {
928  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation($this->object->getId(), 0, $answer_id);
929  if (strlen($fb)) {
930  $template->setCurrentBlock("feedback");
931  $template->setVariable("FEEDBACK", $this->object->prepareTextareaOutput($fb, true));
932  $template->parseCurrentBlock();
933  }
934  }
935  }
936  }
937 
938  if ($this->object->getSpecificFeedbackSetting() == 1) {
939  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation($this->object->getId(), 0, $answer_id);
940  if (strlen($fb)) {
941  $template->setCurrentBlock("feedback");
942  $template->setVariable("FEEDBACK", $this->object->prepareTextareaOutput($fb, true));
943  $template->parseCurrentBlock();
944  }
945  }
946 
947  if ($this->object->getSpecificFeedbackSetting() == 3) {
948  $answer = $this->object->getAnswer($answer_id);
949 
950  if ($answer->getPoints() > 0) {
951  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation($this->object->getId(), 0, $answer_id);
952  if (strlen($fb)) {
953  $template->setCurrentBlock("feedback");
954  $template->setVariable("FEEDBACK", $this->object->prepareTextareaOutput($fb, true));
955  $template->parseCurrentBlock();
956  }
957  }
958  }
959  }
960 
961  // fau: testNav - new functions setWithNoneAbove() and setIsAnswered()
962  // moved functionality to ilTestQuestionPresentationConfig
963  // fau.
964 
968  protected function buildEditForm(bool $is_singleline = true): ilPropertyFormGUI
969  {
970  include_once("./Services/Form/classes/class.ilPropertyFormGUI.php");
971  $form = new ilPropertyFormGUI();
972  $form->setFormAction($this->ctrl->getFormAction($this));
973  $form->setTitle($this->outQuestionType());
974  $form->setTableWidth("100%");
975  $form->setId("assmultiplechoice");
976 
977  $this->addBasicQuestionFormProperties($form);
978  $this->populateQuestionSpecificFormPart($form, $is_singleline);
979  $this->populateAnswerSpecificFormPart($form, $is_singleline);
980  $this->populateTaxonomyFormSection($form);
981  $this->addQuestionFormCommandButtons($form);
982  return $form;
983  }
984 
985  public function getAnswersFrequency($relevantAnswers, $questionIndex): array
986  {
987  $agg = $this->aggregateAnswers($relevantAnswers, $this->object->getAnswers());
988 
989  $answers = array();
990 
991  foreach ($agg as $ans) {
992  $answers[] = array(
993  'answer' => $ans['answertext'],
994  'frequency' => $ans['count_checked']
995  );
996  }
997 
998  return $answers;
999  }
1000 
1002  {
1003  require_once 'Modules/TestQuestionPool/classes/forms/class.ilAssMultipleChoiceCorrectionsInputGUI.php';
1004  $choices = new ilAssMultipleChoiceCorrectionsInputGUI($this->lng->txt("answers"), "choice");
1005  $choices->setRequired(true);
1006  $choices->setQuestionObject($this->object);
1007  $choices->setValues($this->object->getAnswers());
1008  $form->addItem($choices);
1009  }
1010 
1015  {
1016  $input = $form->getItemByPostVar('choice');
1017  $answerElements = $input->getValues();
1018 
1019  foreach ($this->object->getAnswers() as $index => $answer) {
1020  /* @var ASS_AnswerMultipleResponseImage $answer */
1021  $answer->setPointsChecked((float) str_replace(',', '.', $answerElements[$index]->getPointsChecked()));
1022  $answer->setPointsUnchecked((float) str_replace(',', '.', $answerElements[$index]->getPointsUnchecked()));
1023  }
1024  }
1025 }
hasCorrectSolution($activeId, $passIndex)
setSuffix(string $a_value)
generateCorrectnessIconsForCorrectness(int $correctness)
setCurrentBlock(string $blockname=self::DEFAULT_BLOCK)
Sets the template to the given block.
getAnswersFrequency($relevantAnswers, $questionIndex)
$errors
Definition: imgupload.php:65
$type
getItemByPostVar(string $a_post_var)
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:64
setUseEmptySolutionInputChecked($useEmptySolutionInputChecked)
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.
static getImagePath(string $img, 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...
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)
This class represents a checkbox property in a property form.
ilGlobalPageTemplate $tpl
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...
populateTaxonomyFormSection(ilPropertyFormGUI $form)
removeimagechoice()
Remove an image.
$index
Definition: metadata.php:145
addQuestionFormCommandButtons(ilPropertyFormGUI $form)
editQuestion($checkonly=false)
Creates an output of the edit form for the question.
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
get(string $part=self::DEFAULT_BLOCK)
Renders the given block and returns the html string.
$keys
Definition: metadata.php:204
This class represents a number property in a property form.
writeAnswerSpecificPostData(ilPropertyFormGUI $form)
Extracts the answer specific values from $_POST and applies them to the data object.
getPreview($show_question_only=false, $showInlineFeedback=false)
static getHtmlPath(string $relative_path)
get url of path
writeQuestionSpecificPostData(ilPropertyFormGUI $form)
Extracts the question specific values from $_POST and applies them to the data object.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setRequired(bool $a_required)
aggregateAnswers($relevant_answers_chosen, $answers_defined_on_question)
$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.
getEditAnswersSingleLine($checkonly=false)
Get the single/multiline editing of answers.
__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)
__construct(Container $dic, ilPlugin $plugin)
$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.
getTestOutput( $active_id, $pass, $is_postponed=false, $use_post_solutions=false, $show_feedback=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
buildEditForm(bool $is_singleline=true)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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)
Get the question solution output.
getSpecificFeedbackOutput(array $userSolution)
populateSpecificFeedbackInline($user_solution, $answer_id, $template)
setVariable(string $variable, $value='')
Sets the given variable to the given value.
getAggregatedAnswersView(array $relevant_answers)
Returns an html string containing a question specific representation of the answers so far given in t...
addBasicQuestionFormProperties(ilPropertyFormGUI $form)
getGenericFeedbackOutput(int $active_id, ?int $pass)