ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.assClozeTestGUI.php
Go to the documentation of this file.
1 <?php
2 
19 use ILIAS\Refinery\Random\Group as RandomGroup;
25 
42 {
43  public const OLD_CLOZE_TEST_UI = false;
44 
45  public const JS_INSERT_GAP_CODE_AT_CARET = <<<JS
46  jQuery.fn.extend({
47  insertGapCodeAtCaret: function() {
48  return this.each(function(i) {
49  var code_start = "[gap]"
50  var code_end = "[/gap]"
51  if (typeof tinyMCE != "undefined" && typeof tinyMCE.get('cloze_text') != "undefined") {
52  var ed = tinyMCE.get('cloze_text');
53  il.ClozeHelper.internetExplorerTinyMCECursorFix(ed);
54  ed.selection.setContent(code_start + ed.selection.getContent() + code_end);
55  ed.focus();
56  return;
57  }
58  if (document.selection) {
59  //For browsers like Internet Explorer
60  this.focus();
61  sel = document.selection.createRange();
62  sel.text = code_start + sel.text + code_end;
63  this.focus();
64  } else if (this.selectionStart || this.selectionStart == '0') {
65  //For browsers like Firefox and Webkit based
66  var startPos = this.selectionStart;
67  var endPos = this.selectionEnd;
68  var scrollTop = this.scrollTop;
69  this.value = this.value.substring(0, startPos)
70  + code_start
71  + this.value.substring(startPos, endPos)
72  + code_end
73  + this.value.substring(endPos, this.value.length);
74  this.focus();
75  this.scrollTop = scrollTop;
76  } else {
77  this.value += code_start + code_end;
78  this.focus();
79  }
80  });
81  }
82  });
84 
88  private $gapIndex;
89 
90  private RandomGroup $randomGroup;
91  private Container $dic;
93  private \ILIAS\UI\Factory $ui_factory;
94  private \ILIAS\UI\Renderer $ui_renderer;
95 
101  public function __construct(int $id = -1)
102  {
104  global $DIC;
105  $this->dic = $DIC;
106  $this->ui_factory = $DIC->ui()->factory();
107  $this->ui_renderer = $DIC->ui()->renderer();
108  $this->post = $this->dic->http()->wrapper()->post();
109 
110  $this->object = new assClozeTest();
111  if ($id >= 0) {
112  $this->object->loadFromDb($id);
113  }
114 
115  $this->randomGroup = $this->refinery->random();
116  }
117 
118  public function getCommand($cmd)
119  {
120  if (preg_match("/^(removegap|addgap)_(\d+)$/", $cmd, $matches)) {
121  $cmd = $matches[1];
122  $this->gapIndex = $matches[2];
123  }
124  return $cmd;
125  }
126 
130  protected function writePostData(bool $always = false): int
131  {
132  $hasErrors = (!$always) ? $this->editQuestion(true) : false;
133  if ($hasErrors) {
134  return 1;
135  }
136 
137  $cloze_text = $this->removeIndizesFromGapText(
138  $this->request->raw('cloze_text')
139  );
140 
141  $this->object->setQuestion(
142  $this->request->raw('question')
143  );
144 
146  $this->object->setClozeText($cloze_text);
147  $this->object->setTextgapRating($this->request->raw('textgap_rating'));
148  $this->object->setIdenticalScoring((bool) ($this->request->raw('identical_scoring') ?? false));
149  $this->object->setFixedTextLength(($this->request->int('fixedTextLength') ?? 0));
151  $this->saveTaxonomyAssignments();
152  return 0;
153  }
154 
155  public function writeAnswerSpecificPostData(ilPropertyFormGUI $form): void
156  {
157  if (!$this->post->has('gap')) {
158  return;
159  }
160 
161  $gaps = $this->post->retrieve(
162  "gap",
163  $this->refinery->kindlyTo()->listOf($this->refinery->kindlyTo()->string())
164  );
165 
166  if ($this->ctrl->getCmd() !== 'createGaps') {
167  $this->object->clearGapAnswers();
168  }
169 
170  foreach ($gaps as $idx => $hidden) {
171  $clozetype = $this->post->retrieve(
172  "clozetype_" . $idx,
173  $this->refinery->kindlyTo()->string()
174  );
175 
176  $this->object->setGapType($idx, $clozetype);
177 
178  switch ($clozetype) {
179  case CLOZE_TEXT:
180 
181  $this->object->setGapShuffle($idx, 0);
182 
183  if ($this->ctrl->getCmd() != 'createGaps') {
184  if (is_array($_POST['gap_' . $idx]['answer'])) {
185  foreach ($_POST['gap_' . $idx]['answer'] as $order => $value) {
186  $this->object->addGapAnswer($idx, $order, $value);
187  }
188  } else {
189  $this->object->addGapAnswer($idx, 0, '');
190  }
191  }
192 
193  if (is_array($_POST['gap_' . $idx]['points'])) {
194  foreach ($_POST['gap_' . $idx]['points'] as $order => $value) {
195  $this->object->setGapAnswerPoints($idx, $order, $value);
196  }
197  }
198 
199  $k_gapsize = 'gap_' . $idx . '_gapsize';
200  if ($this->request->isset($k_gapsize)) {
201  $this->object->setGapSize($idx, $_POST[$k_gapsize]);
202  }
203  break;
204 
205  case CLOZE_SELECT:
206 
207  $this->object->setGapShuffle($idx, (int) (isset($_POST["shuffle_$idx"]) && $_POST["shuffle_$idx"]));
208 
209  if ($this->ctrl->getCmd() != 'createGaps') {
210  if (is_array($_POST['gap_' . $idx]['answer'])) {
211  foreach ($_POST['gap_' . $idx]['answer'] as $order => $value) {
212  $this->object->addGapAnswer($idx, $order, $value);
213  }
214  } else {
215  $this->object->addGapAnswer($idx, 0, '');
216  }
217  }
218 
219  if (is_array($_POST['gap_' . $idx]['points'])) {
220  foreach ($_POST['gap_' . $idx]['points'] as $order => $value) {
221  $this->object->setGapAnswerPoints($idx, $order, $value);
222  }
223  }
224  break;
225 
226  case CLOZE_NUMERIC:
227 
228  $this->object->setGapShuffle($idx, 0);
229 
230  $gap = $this->object->getGap($idx);
231  if (!$gap) {
232  break;
233  }
234 
235  $this->object->getGap($idx)->clearItems();
236 
237  if ($this->post->has('gap_' . $idx . '_numeric')) {
238  if ($this->ctrl->getCmd() !== 'createGaps') {
239  $this->object->addGapAnswer(
240  $idx,
241  0,
242  str_replace(",", ".", $_POST['gap_' . $idx . '_numeric'])
243  );
244  }
245 
246  $this->object->setGapAnswerLowerBound(
247  $idx,
248  0,
249  str_replace(",", ".", $_POST['gap_' . $idx . '_numeric_lower'])
250  );
251 
252  $this->object->setGapAnswerUpperBound(
253  $idx,
254  0,
255  str_replace(",", ".", $_POST['gap_' . $idx . '_numeric_upper'])
256  );
257 
258  $this->object->setGapAnswerPoints($idx, 0, $_POST['gap_' . $idx . '_numeric_points']);
259  } else {
260  if ($this->ctrl->getCmd() != 'createGaps') {
261  $this->object->addGapAnswer($idx, 0, '');
262  }
263 
264  $this->object->setGapAnswerLowerBound($idx, 0, '');
265 
266  $this->object->setGapAnswerUpperBound($idx, 0, '');
267  }
268 
269  if ($this->post->has('gap_' . $idx . '_gapsize')) {
270  $this->object->setGapSize($idx, $_POST['gap_' . $idx . '_gapsize']);
271  }
272  break;
273  }
274  $assClozeGapCombinationObject = new assClozeGapCombination();
275  $assClozeGapCombinationObject->clearGapCombinationsFromDb($this->object->getId());
276  if (
277  isset($_POST['gap_combination']) &&
278  is_array($_POST['gap_combination']) &&
279  count($_POST['gap_combination']) > 0
280  ) {
281  $assClozeGapCombinationObject->saveGapCombinationToDb(
282  $this->object->getId(),
283  $_POST['gap_combination'],
284  $_POST['gap_combination_values']
285  );
286  $this->object->setGapCombinationsExists(true);
287  }
288  }
289  if ($this->ctrl->getCmd() != 'createGaps') {
290  $this->object->updateClozeTextFromGaps();
291  }
292  }
293 
295  {
296  $this->object->setClozeText($_POST['cloze_text']);
297  $this->object->setTextgapRating($_POST["textgap_rating"]);
298  $this->object->setIdenticalScoring($_POST["identical_scoring"]);
299  $this->object->setFixedTextLength($_POST["fixedTextLength"]);
300  }
301 
307  public function editQuestion($checkonly = false): bool
308  {
309  $save = $this->isSaveCommand();
310  $this->getQuestionTemplate();
311 
312  $form = new ilPropertyFormGUI();
313  $this->editForm = $form;
314 
315  $form->setFormAction($this->ctrl->getFormAction($this));
316  $form->setTitle($this->outQuestionType());
317  $form->setMultipart(false);
318  $form->setTableWidth("100%");
319  $form->setId("assclozetest");
320 
321  $this->addBasicQuestionFormProperties($form);
322  $this->populateQuestionSpecificFormPart($form);
323  $this->populateAnswerSpecificFormPart($form);
324  $this->populateTaxonomyFormSection($form);
325 
326  $this->addQuestionFormCommandButtons($form);
327 
328  $errors = false;
329 
330  if ($save) {
331  $form->setValuesByPost();
332  $errors = !$form->checkInput();
333  $form->setValuesByPost();
334 
335  $gap_combinations = $this->request->raw('gap_combination');
336  if (is_array($gap_combinations)
337  && $gap_combinations !== []
338  && $this->hasErrorInGapCombinationPoints($gap_combinations)) {
339  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('points_non_numeric_or_negative_msg'));
340  $errors = true;
341  }
342 
343  if ($errors) {
344  $checkonly = false;
345  }
346  }
347 
348  if (!$checkonly) {
349  $modal = ilModalGUI::getInstance();
350  $modal->setHeading($this->lng->txt(''));
351  $modal->setId('ilGapModal');
352  $modal->setBody('');
353  $this->tpl->setVariable("QUESTION_DATA", $form->getHTML() . $modal->getHTML());
354  }
355  return $errors;
356  }
357 
358  private function hasErrorInGapCombinationPoints(array $gap_combinations): bool
359  {
360  foreach ($gap_combinations['points'] as $gaps_points) {
361  foreach ($gaps_points as $points) {
362  $points_standardized = str_replace(',', '.', $points);
363  if (!is_numeric($points_standardized)
364  || (float) $points_standardized < 0) {
365  return true;
366  }
367  }
368  }
369 
370  return false;
371  }
372 
374  {
375  // title
376  $title = new ilTextInputGUI($this->lng->txt("title"), "title");
377  $title->setMaxLength(100);
378  $title->setValue($this->object->getTitle());
379  $title->setRequired(true);
380  $form->addItem($title);
381 
382  if (!$this->object->getSelfAssessmentEditingMode()) {
383  // author
384  $author = new ilTextInputGUI($this->lng->txt("author"), "author");
385  $author->setValue($this->object->getAuthor());
386  $author->setMaxLength(512);
387  $author->setRequired(true);
388  $form->addItem($author);
389 
390  // description
391  $description = new ilTextInputGUI($this->lng->txt("description"), "comment");
392  $description->setValue($this->object->getComment());
393  $description->setRequired(false);
394  $form->addItem($description);
395  } else {
396  // author as hidden field
397  $hi = new ilHiddenInputGUI("author");
398  $author = ilLegacyFormElementsUtil::prepareFormOutput($this->object->getAuthor());
399  if (trim($author) == "") {
400  $author = "-";
401  }
402  $hi->setValue($author);
403  $form->addItem($hi);
404  }
405 
406  // lifecycle
407  $lifecycle = new ilSelectInputGUI($this->lng->txt('qst_lifecycle'), 'lifecycle');
408  $lifecycle->setOptions($this->object->getLifecycle()->getSelectOptions($this->lng));
409  $lifecycle->setValue($this->object->getLifecycle()->getIdentifier());
410  $form->addItem($lifecycle);
411 
412  // questiontext
413  $question = new ilTextAreaInputGUI($this->lng->txt("question"), "question");
414  $question->setValue($this->object->getQuestion());
415  $question->setRequired(true);
416  $question->setRows(10);
417  $question->setCols(80);
418  if (!$this->object->getSelfAssessmentEditingMode()) {
419  if ($this->object->getAdditionalContentEditingMode() == assQuestion::ADDITIONAL_CONTENT_EDITING_MODE_RTE) {
420  $question->setUseRte(true);
421  $question->setRteTags(ilObjAdvancedEditing::_getUsedHTMLTags("assessment"));
422  $question->addPlugin("latex");
423  $question->addButton("latex");
424  $question->addButton("pastelatex");
425  $question->setRTESupport($this->object->getId(), "qpl", "assessment");
426  }
427  } else {
429  $question->setUseTagsForRteOnly(false);
430  }
431  $form->addItem($question);
432  $this->addNumberOfTriesToFormIfNecessary($form);
433  }
434 
436  {
437  // cloze text
438  $cloze_text = new ilTextAreaInputGUI($this->lng->txt("cloze_text"), 'cloze_text');
439  $cloze_text->setRequired(true);
440  $cloze_text->setValue($this->applyIndizesToGapText($this->object->getClozeText()));
441  $cloze_text->setInfo($this->lng->txt("close_text_hint"));
442  $cloze_text->setRows(10);
443  $cloze_text->setCols(80);
444  if (!$this->object->getSelfAssessmentEditingMode()) {
445  if ($this->object->getAdditionalContentEditingMode() == assQuestion::ADDITIONAL_CONTENT_EDITING_MODE_RTE) {
446  $cloze_text->setUseRte(true);
447  $cloze_text->setRteTags(ilObjAdvancedEditing::_getUsedHTMLTags("assessment"));
448  $cloze_text->addPlugin("latex");
449  $cloze_text->addButton("latex");
450  $cloze_text->addButton("pastelatex");
451  }
452  } else {
454  $cloze_text->setUseTagsForRteOnly(false);
455  }
456  $cloze_text->setRTESupport($this->object->getId(), "qpl", "assessment");
457  $form->addItem($cloze_text);
458 
459  $tpl = new ilTemplate("tpl.il_as_qpl_cloze_gap_button_code.html", true, true, "Modules/TestQuestionPool");
460 
461  $button = new ilCustomInputGUI('&nbsp;', '');
462 
463  $button_text_gap = $this->ui_factory->button()->standard($this->lng->txt('text_gap'), '')
465  $this->getAddGapButtonClickClosure('text')
466  );
467  $button_select_gap = $this->ui_factory->button()->standard($this->lng->txt('select_gap'), '')
469  $this->getAddGapButtonClickClosure('select')
470  );
471  $button_numeric_gap = $this->ui_factory->button()->standard($this->lng->txt('numeric_gap'), '')
473  $this->getAddGapButtonClickClosure('numeric')
474  );
475 
476  $tpl->setVariable('BUTTON', $this->ui_renderer->render([
477  $button_text_gap,
478  $button_select_gap,
479  $button_numeric_gap
480  ]));
482 
483  $button->setHtml($tpl->get());
484  $this->tpl->addOnloadCode(self::JS_INSERT_GAP_CODE_AT_CARET);
485  $form->addItem($button);
486 
487  // text rating
488  if (!$this->object->getSelfAssessmentEditingMode()) {
489  $textrating = new ilSelectInputGUI($this->lng->txt("text_rating"), "textgap_rating");
490  $text_options = [
491  "ci" => $this->lng->txt("cloze_textgap_case_insensitive"),
492  "cs" => $this->lng->txt("cloze_textgap_case_sensitive"),
493  "l1" => sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "1"),
494  "l2" => sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "2"),
495  "l3" => sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "3"),
496  "l4" => sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "4"),
497  "l5" => sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "5")
498  ];
499  $textrating->setOptions($text_options);
500  $textrating->setValue($this->object->getTextgapRating());
501  $form->addItem($textrating);
502 
503  // text field length
504  $fixedTextLength = new ilNumberInputGUI($this->lng->txt("cloze_fixed_textlength"), "fixedTextLength");
505  $ftl = $this->object->getFixedTextLength();
506 
507  $fixedTextLength->setValue($ftl > 0 ? $ftl : '');
508  $fixedTextLength->setMinValue(0);
509  $fixedTextLength->setSize(3);
510  $fixedTextLength->setMaxLength(6);
511  $fixedTextLength->setInfo($this->lng->txt('cloze_fixed_textlength_description'));
512  $fixedTextLength->setRequired(false);
513  $form->addItem($fixedTextLength);
514 
515  // identical scoring
516  $identical_scoring = new ilCheckboxInputGUI($this->lng->txt("identical_scoring"), "identical_scoring");
517  $identical_scoring->setValue(1);
518  $identical_scoring->setChecked($this->object->getIdenticalScoring());
519  $identical_scoring->setInfo($this->lng->txt('identical_scoring_desc'));
520  $identical_scoring->setRequired(false);
521  $form->addItem($identical_scoring);
522  }
523  return $form;
524  }
525 
526  private function getAddGapButtonClickClosure(string $gap_type): Closure
527  {
528  return fn($id) => "var el = document.getElementById('{$id}').addEventListener('click', "
529  . '(e) => {'
530  . ' e.preventDefault();'
531  . " ClozeQuestionGapBuilder.addGapClickFunction('{$gap_type}');"
532  . '});';
533  }
534 
536  {
537  if (self::OLD_CLOZE_TEST_UI) {
538  for ($gapCounter = 0; $gapCounter < $this->object->getGapCount(); $gapCounter++) {
539  $this->populateGapFormPart($form, $gapCounter);
540  }
541  return $form;
542  } else {
543  $json = $this->populateJSON();
544  $assClozeGapCombinationObject = new assClozeGapCombination();
545  $combination_exists = $assClozeGapCombinationObject->combinationExistsForQid($this->object->getId());
546  $combinations = [];
547  if ($combination_exists) {
548  $combinations = $assClozeGapCombinationObject->loadFromDb($this->object->getId());
549  }
550  $new_builder = new ilClozeGapInputBuilderGUI();
551  $header = new ilFormSectionHeaderGUI();
552  $form->addItem($header);
553  $new_builder->setValueByArray($json);
554  $new_builder->setValueCombinationFromDb($combinations);
555  $form->addItem($new_builder);
556  return $form;
557  }
558  }
559 
560  protected function populateJSON(): array
561  {
562  $gap = $this->object->getGaps();
563  $array = [];
564  if ($gap == null) {
565  return $array;
566  }
567  $translate_type = ['text','select','numeric'];
568  $i = 0;
569  foreach ($gap as $content) {
570  $shuffle = false;
571  $value = $content->getItemsRaw();
572  $items = [];
573  for ($j = 0, $jMax = count($value); $j < $jMax; $j++) {
574  if ($content->isNumericGap()) {
575  $items[$j] = [
576  'answer' => $value[$j]->getAnswerText(),
577  'lower' => $value[$j]->getLowerBound(),
578  'upper' => $value[$j]->getUpperBound(),
579  'points' => $value[$j]->getPoints(),
580  'error' => false
581  ];
582  } else {
583  $items[$j] = [
584  'answer' => $this->escapeTemplatePlaceholders($value[$j]->getAnswerText()),
585  'points' => $value[$j]->getPoints(),
586  'error' => false
587  ];
588 
589  if ($content->isSelectGap()) {
590  $shuffle = $content->getShuffle();
591  }
592  }
593  }
594  $answers[$i] = [
595  'type' => $translate_type[$content->getType()] ,
596  'values' => $items ,
597  'shuffle' => $shuffle,
598  'text_field_length' => $content->getGapSize() > 0 ? $content->getGapSize() : '',
599  'used_in_gap_combination' => true];
600  $i++;
601  }
602  return $answers;
603  }
615  protected function populateGapFormPart($form, $gapCounter): ilPropertyFormGUI
616  {
617  $gap = $this->object->getGap($gapCounter);
618 
619  if ($gap == null) {
620  return $form;
621  }
622 
623  $header = new ilFormSectionHeaderGUI();
624  $header->setTitle($this->lng->txt("gap") . " " . ($gapCounter + 1));
625  $form->addItem($header);
626 
627  $gapcounter = new ilHiddenInputGUI("gap[$gapCounter]");
628  $gapcounter->setValue($gapCounter);
629  $form->addItem($gapcounter);
630 
631  $gaptype = new ilSelectInputGUI($this->lng->txt('type'), "clozetype_$gapCounter");
632  $options = [
633  0 => $this->lng->txt("text_gap"),
634  1 => $this->lng->txt("select_gap"),
635  2 => $this->lng->txt("numeric_gap")
636  ];
637  $gaptype->setOptions($options);
638  $gaptype->setValue($gap->getType());
639  $form->addItem($gaptype);
640 
641  if ($gap->getType() == CLOZE_TEXT) {
642  $this->populateGapSizeFormPart($form, $gap, $gapCounter);
643 
644  if (count($gap->getItemsRaw()) == 0) {
645  $gap->addItem(new assAnswerCloze("", 0, 0));
646  }
647  $this->populateTextGapFormPart($form, $gap, $gapCounter);
648  } elseif ($gap->getType() == CLOZE_SELECT) {
649  if (count($gap->getItemsRaw()) == 0) {
650  $gap->addItem(new assAnswerCloze("", 0, 0));
651  }
652  $this->populateSelectGapFormPart($form, $gap, $gapCounter);
653  } elseif ($gap->getType() == CLOZE_NUMERIC) {
654  $this->populateGapSizeFormPart($form, $gap, $gapCounter);
655 
656  if (count($gap->getItemsRaw()) == 0) {
657  $gap->addItem(new assAnswerCloze("", 0, 0));
658  }
659  foreach ($gap->getItemsRaw() as $item) {
660  $this->populateNumericGapFormPart($form, $item, $gapCounter);
661  }
662  }
663  return $form;
664  }
665 
671  protected function populateGapSizeFormPart($form, $gap, $gapCounter): ilPropertyFormGUI
672  {
673  $gapSizeFormItem = new ilNumberInputGUI($this->lng->txt('cloze_fixed_textlength'), "gap_" . $gapCounter . '_gapsize');
674 
675  $gapSizeFormItem->allowDecimals(false);
676  $gapSizeFormItem->setMinValue(0);
677  $gapSizeFormItem->setSize(3);
678  $gapSizeFormItem->setMaxLength(6);
679  $gapSizeFormItem->setInfo($this->lng->txt('cloze_gap_size_info'));
680  $gapSizeFormItem->setValue($gap->getGapSize());
681  $form->addItem($gapSizeFormItem);
682 
683  return $form;
684  }
685 
698  protected function populateSelectGapFormPart($form, $gap, $gapCounter): ilPropertyFormGUI
699  {
700  $values = new ilAnswerWizardInputGUI($this->lng->txt("values"), "gap_" . $gapCounter . "");
701  $values->setRequired(true);
702  $values->setQuestionObject($this->object);
703  $values->setSingleline(true);
704  $values->setAllowMove(false);
705 
706  $values->setValues($gap->getItemsRaw());
707  $form->addItem($values);
708 
709  // shuffle
710  $shuffle = new ilCheckboxInputGUI($this->lng->txt("shuffle_answers"), "shuffle_" . $gapCounter . "");
711  $shuffle->setValue(1);
712  $shuffle->setChecked($gap->getShuffle());
713  $shuffle->setRequired(false);
714  $form->addItem($shuffle);
715  return $form;
716  }
717 
729  protected function populateTextGapFormPart($form, $gap, $gapCounter): ilPropertyFormGUI
730  {
731  $values = new ilAnswerWizardInputGUI($this->lng->txt("values"), "gap_" . $gapCounter . "");
732  $values->setRequired(true);
733  $values->setQuestionObject($this->object);
734  $values->setSingleline(true);
735  $values->setAllowMove(false);
736  $values->setValues($gap->getItemsRaw());
737  $form->addItem($values);
738 
739  if ($this->object->getFixedTextLength() > 0) {
740  $values->setSize($this->object->getFixedTextLength());
741  $values->setMaxLength($this->object->getFixedTextLength());
742  }
743 
744  return $form;
745  }
746 
758  protected function populateNumericGapFormPart($form, $gap, $gapCounter): ilPropertyFormGUI
759  {
760  // #8944: the js-based ouput in self-assessment cannot support formulas
761  if (!$this->object->getSelfAssessmentEditingMode()) {
762  $value = new ilFormulaInputGUI($this->lng->txt('value'), "gap_" . $gapCounter . "_numeric");
763  $value->setInlineStyle('text-align: right;');
764 
765  $lowerbound = new ilFormulaInputGUI($this->lng->txt('range_lower_limit'), "gap_" . $gapCounter . "_numeric_lower");
766  $lowerbound->setInlineStyle('text-align: right;');
767 
768  $upperbound = new ilFormulaInputGUI($this->lng->txt('range_upper_limit'), "gap_" . $gapCounter . "_numeric_upper");
769  $upperbound->setInlineStyle('text-align: right;');
770  } else {
771  $value = new ilNumberInputGUI($this->lng->txt('value'), "gap_" . $gapCounter . "_numeric");
772  $value->allowDecimals(true);
773 
774  $lowerbound = new ilNumberInputGUI($this->lng->txt('range_lower_limit'), "gap_" . $gapCounter . "_numeric_lower");
775  $lowerbound->allowDecimals(true);
776 
777  $upperbound = new ilNumberInputGUI($this->lng->txt('range_upper_limit'), "gap_" . $gapCounter . "_numeric_upper");
778  $upperbound->allowDecimals(true);
779  }
780 
781  $value->setSize(10);
782  $value->setValue(ilLegacyFormElementsUtil::prepareFormOutput($gap->getAnswertext()));
783  $value->setRequired(true);
784  $form->addItem($value);
785 
786  $lowerbound->setSize(10);
787  $lowerbound->setRequired(true);
788  $lowerbound->setValue(ilLegacyFormElementsUtil::prepareFormOutput($gap->getLowerBound()));
789  $form->addItem($lowerbound);
790 
791  $upperbound->setSize(10);
792  $upperbound->setRequired(true);
793  $upperbound->setValue(ilLegacyFormElementsUtil::prepareFormOutput($gap->getUpperBound()));
794  $form->addItem($upperbound);
795 
796  if ($this->object->getFixedTextLength() > 0) {
797  $value->setSize($this->object->getFixedTextLength());
798  $value->setMaxLength($this->object->getFixedTextLength());
799  $lowerbound->setSize($this->object->getFixedTextLength());
800  $lowerbound->setMaxLength($this->object->getFixedTextLength());
801  $upperbound->setSize($this->object->getFixedTextLength());
802  $upperbound->setMaxLength($this->object->getFixedTextLength());
803  }
804 
805  $points = new ilNumberInputGUI($this->lng->txt('points'), "gap_" . $gapCounter . "_numeric_points");
806  $points->allowDecimals(true);
807  $points->setSize(3);
808  $points->setRequired(true);
809  $points->setValue(ilLegacyFormElementsUtil::prepareFormOutput($gap->getPoints()));
810  $form->addItem($points);
811  return $form;
812  }
813 
817  public function createGaps(): void
818  {
819  $this->writePostData(true);
820  $this->object->saveToDb();
821  $this->editQuestion();
822  }
823 
827  public function removegap(): void
828  {
829  $this->writePostData(true);
830  $this->object->deleteAnswerText($this->gapIndex, key($_POST['cmd']['removegap_' . $this->gapIndex]));
831  $this->editQuestion();
832  }
833 
837  public function addgap(): void
838  {
839  $this->writePostData(true);
840  $this->object->addGapAnswer($this->gapIndex, key($_POST['cmd']['addgap_' . $this->gapIndex]) + 1, "");
841  $this->editQuestion();
842  }
843 
852  public function getPreview($show_question_only = false, $showInlineFeedback = false): string
853  {
854  $user_solution = is_object($this->getPreviewSession()) ? (array) $this->getPreviewSession()->getParticipantsSolution() : [];
855 
856  $template = new ilTemplate("tpl.il_as_qpl_cloze_question_output.html", true, true, "Modules/TestQuestionPool");
857  $output = $this->object->getClozeTextForHTMLOutput();
858  foreach ($this->object->getGaps() as $gap_index => $gap) {
859  switch ($gap->getType()) {
860  case CLOZE_TEXT:
861  $gaptemplate = new ilTemplate("tpl.il_as_qpl_cloze_question_gap_text.html", true, true, "Modules/TestQuestionPool");
862 
863  $gap_size = $gap->getGapSize() > 0 ? $gap->getGapSize() : $this->object->getFixedTextLength();
864  if ($gap_size > 0) {
865  $gaptemplate->setCurrentBlock('size_and_maxlength');
866  $gaptemplate->setVariable("TEXT_GAP_SIZE", $gap_size);
867  $gaptemplate->parseCurrentBlock();
868  }
869  $gaptemplate->setVariable("GAP_COUNTER", $gap_index);
870  foreach ($user_solution as $val1 => $val2) {
871  if (strcmp($val1, $gap_index) == 0) {
872  $gaptemplate->setVariable("VALUE_GAP", " value=\"" . ilLegacyFormElementsUtil::prepareFormOutput(
873  $val2
874  ) . "\"");
875  }
876  }
877  // fau: fixGapReplace - use replace function
878  $output = $this->object->replaceFirstGap($output, $gaptemplate->get());
879  // fau.
880  break;
881  case CLOZE_SELECT:
882  $gaptemplate = new ilTemplate("tpl.il_as_qpl_cloze_question_gap_select.html", true, true, "Modules/TestQuestionPool");
883  foreach ($gap->getItems($this->object->getShuffler(), $gap_index) as $item) {
884  $gaptemplate->setCurrentBlock("select_gap_option");
885  $gaptemplate->setVariable("SELECT_GAP_VALUE", $item->getOrder());
886  $gaptemplate->setVariable(
887  "SELECT_GAP_TEXT",
888  ilLegacyFormElementsUtil::prepareFormOutput($item->getAnswerText())
889  );
890  foreach ($user_solution as $val1 => $val2) {
891  if (strcmp($val1, $gap_index) == 0) {
892  if (strcmp($val2, $item->getOrder()) == 0) {
893  $gaptemplate->setVariable("SELECT_GAP_SELECTED", " selected=\"selected\"");
894  }
895  }
896  }
897  $gaptemplate->parseCurrentBlock();
898  }
899  $gaptemplate->setVariable("PLEASE_SELECT", $this->lng->txt("please_select"));
900  $gaptemplate->setVariable("GAP_COUNTER", $gap_index);// fau: fixGapReplace - use replace function
901  $output = $this->object->replaceFirstGap($output, $gaptemplate->get());
902  // fau.
903  break;
904  case CLOZE_NUMERIC:
905  $gaptemplate = new ilTemplate("tpl.il_as_qpl_cloze_question_gap_numeric.html", true, true, "Modules/TestQuestionPool");
906  $gap_size = $gap->getGapSize() > 0 ? $gap->getGapSize() : $this->object->getFixedTextLength();
907  if ($gap_size > 0) {
908  $gaptemplate->setCurrentBlock('size_and_maxlength');
909  $gaptemplate->setVariable("TEXT_GAP_SIZE", $gap_size);
910  $gaptemplate->parseCurrentBlock();
911  }
912  $gaptemplate->setVariable("GAP_COUNTER", $gap_index);
913  foreach ($user_solution as $val1 => $val2) {
914  if (strcmp($val1, $gap_index) == 0) {
915  $gaptemplate->setVariable("VALUE_GAP", " value=\"" . ilLegacyFormElementsUtil::prepareFormOutput(
916  $val2
917  ) . "\"");
918  }
919  }
920  // fau: fixGapReplace - use replace function
921  $output = $this->object->replaceFirstGap($output, $gaptemplate->get());
922  // fau.
923  break;
924  }
925  }
926  $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
927  $template->setVariable("CLOZETEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($output, true));
928  $questionoutput = $template->get();
929  if (!$show_question_only) {
930  // get page object output
931  $questionoutput = $this->getILIASPage($questionoutput);
932  }
933  return $questionoutput;
934  }
935 
949  public function getSolutionOutput(
950  $active_id,
951  $pass = null,
952  $graphicalOutput = false,
953  $result_output = false,
954  $show_question_only = true,
955  $show_feedback = false,
956  $show_correct_solution = false,
957  $show_manual_scoring = false,
958  $show_question_text = true
959  ): string {
960  // get the solution of the user for the active pass or from the last pass if allowed
961  $user_solution = [];
962  if (($active_id > 0) && (!$show_correct_solution)) {
963  // get the solutions of a user
964  $user_solution = $this->object->getSolutionValues($active_id, $pass);
965  if (!is_array($user_solution)) {
966  $user_solution = [];
967  }
968  }
969 
970  return $this->renderSolutionOutput(
971  $user_solution,
972  $active_id,
973  $pass,
974  $graphicalOutput,
975  $result_output,
976  $show_question_only,
977  $show_feedback,
978  $show_correct_solution,
979  $show_manual_scoring,
980  $show_question_text,
981  false,
982  false
983  );
984  }
985 
986  public function renderSolutionOutput(
987  mixed $user_solutions,
988  int $active_id,
989  ?int $pass,
990  bool $graphical_output = false,
991  bool $result_output = false,
992  bool $show_question_only = true,
993  bool $show_feedback = false,
994  bool $show_correct_solution = false,
995  bool $show_manual_scoring = false,
996  bool $show_question_text = true,
997  bool $show_autosave_title = false,
998  bool $show_inline_feedback = false,
999  ): ?string {
1000 
1001  $template = new ilTemplate("tpl.il_as_qpl_cloze_question_output_solution.html", true, true, "Modules/TestQuestionPool");
1002  $output = $this->object->getClozeTextForHTMLOutput();
1003  $assClozeGapCombinationObject = new assClozeGapCombination();
1004  $check_for_gap_combinations = $assClozeGapCombinationObject->loadFromDb($this->object->getId());
1005 
1006  foreach ($this->object->getGaps() as $gap_index => $gap) {
1007  $gaptemplate = new ilTemplate("tpl.il_as_qpl_cloze_question_output_solution_gap.html", true, true, "Modules/TestQuestionPool");
1008  $found = [];
1009  foreach ($user_solutions as $solutionarray) {
1010  if ($solutionarray['value1'] == $gap_index) {
1011  $found = $solutionarray;
1012  }
1013  }
1014 
1015  if ($active_id) {
1016  if ($graphical_output) {
1017  // output of ok/not ok icons for user entered solutions
1018  $details = $this->object->calculateReachedPoints($active_id, $pass, true, true);
1019  $check = $details[$gap_index] ?? [];
1020 
1021  if (count($check_for_gap_combinations) != 0) {
1022  $gaps_used_in_combination = $assClozeGapCombinationObject->getGapsWhichAreUsedInCombination($this->object->getId());
1023  $custom_user_solution = [];
1024  if (array_key_exists($gap_index, $gaps_used_in_combination)) {
1025  $combination_id = $gaps_used_in_combination[$gap_index];
1026  foreach ($gaps_used_in_combination as $key => $value) {
1027  $a = 0;
1028  if ($value == $combination_id) {
1029  foreach ($user_solutions as $solution_key => $solution_value) {
1030  if ($solution_value['value1'] == $key) {
1031  $result_row = [];
1032  $result_row['gap_id'] = $solution_value['value1'];
1033  $result_row['value'] = $solution_value['value2'];
1034  array_push($custom_user_solution, $result_row);
1035  }
1036  }
1037  }
1038  }
1039  $points_array = $this->object->calculateCombinationResult($custom_user_solution);
1040  $max_combination_points = $assClozeGapCombinationObject->getMaxPointsForCombination($this->object->getId(), $combination_id);
1041  if ($points_array[0] == $max_combination_points) {
1042  $gaptemplate->setVariable("ICON_OK", $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_OK));
1043  } elseif ($points_array[0] > 0) {
1044  $gaptemplate->setVariable("ICON_OK", $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_MOSTLY_OK));
1045  } else {
1046  $gaptemplate->setVariable("ICON_OK", $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_NOT_OK));
1047  }
1048  } else {
1049  if (array_key_exists('best', $check) && $check["best"]) {
1050  $gaptemplate->setCurrentBlock("icon_ok");
1051  $gaptemplate->setVariable("ICON_OK", $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_OK));
1052  $gaptemplate->parseCurrentBlock();
1053  } else {
1054  $gaptemplate->setCurrentBlock("icon_ok");
1055  if (array_key_exists('positive', $check) && $check["positive"]) {
1056  $gaptemplate->setVariable("ICON_OK", $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_MOSTLY_OK));
1057  } else {
1058  $gaptemplate->setVariable("ICON_OK", $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_NOT_OK));
1059  }
1060  $gaptemplate->parseCurrentBlock();
1061  }
1062  }
1063  } else {
1064  if (array_key_exists('best', $check) && $check["best"]) {
1065  $gaptemplate->setCurrentBlock("icon_ok");
1066  $gaptemplate->setVariable("ICON_OK", $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_OK));
1067  $gaptemplate->parseCurrentBlock();
1068  } else {
1069  $gaptemplate->setCurrentBlock("icon_ok");
1070  if (array_key_exists('positive', $check) && $check["positive"]) {
1071  $gaptemplate->setVariable("ICON_OK", $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_MOSTLY_OK));
1072  } else {
1073  $gaptemplate->setVariable("ICON_OK", $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_NOT_OK));
1074  }
1075  $gaptemplate->parseCurrentBlock();
1076  }
1077  }
1078  }
1079  }
1080  $combination = null;
1081  switch ($gap->getType()) {
1082  case CLOZE_NUMERIC:
1083  case CLOZE_TEXT:
1084  $solutiontext = "";
1085  if (($active_id > 0) && (!$show_correct_solution)) {
1086  if ((count($found) == 0) || (strlen(trim($found['value2'])) == 0)) {
1087  for ($chars = 0; $chars < $gap->getMaxWidth(); $chars++) {
1088  $solutiontext .= "&nbsp;";
1089  }
1090  } else {
1091  $solutiontext = ilLegacyFormElementsUtil::prepareFormOutput($found['value2']);
1092  }
1093  } else {
1094  $solutiontext = $this-> getBestSolutionText($gap, $gap_index, $check_for_gap_combinations);
1095  }
1096  $this->populateSolutiontextToGapTpl($gaptemplate, $gap, $solutiontext);
1097  // fau: fixGapReplace - use replace function
1098  $output = $this->object->replaceFirstGap($output, $gaptemplate->get());
1099  // fau.
1100  break;
1101  case CLOZE_SELECT:
1102  $solutiontext = "";
1103  if (($active_id > 0) && (!$show_correct_solution)) {
1104  if ((count($found) == 0) || (strlen(trim($found['value2'])) == 0)) {
1105  for ($chars = 0; $chars < $gap->getMaxWidth(); $chars++) {
1106  $solutiontext .= "&nbsp;";
1107  }
1108  } else {
1109  $item = $gap->getItem($found['value2']);
1110  if (is_object($item)) {
1111  $solutiontext = ilLegacyFormElementsUtil::prepareFormOutput($item->getAnswertext());
1112  } else {
1113  for ($chars = 0; $chars < $gap->getMaxWidth(); $chars++) {
1114  $solutiontext .= "&nbsp;";
1115  }
1116  }
1117  }
1118  } else {
1119  $solutiontext = $this-> getBestSolutionText($gap, $gap_index, $check_for_gap_combinations);
1120  }
1121  $this->populateSolutiontextToGapTpl($gaptemplate, $gap, $solutiontext);
1122  // fau: fixGapReplace - use replace function
1123  $output = $this->object->replaceFirstGap($output, $gaptemplate->get());
1124  // fau.
1125  break;
1126  }
1127  }
1128 
1129  if ($show_question_text) {
1130  $template->setVariable(
1131  "QUESTIONTEXT",
1132  $this->object->getQuestionForHTMLOutput()
1133  );
1134  }
1135 
1136  $template->setVariable("CLOZETEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($output, true));
1137  // generate the question output
1138  $solutiontemplate = new ilTemplate("tpl.il_as_tst_solution_output.html", true, true, "Modules/TestQuestionPool");
1139  $questionoutput = $template->get();
1140 
1141  $feedback = '';
1142  if ($show_feedback) {
1143  if (!$this->isTestPresentationContext()) {
1144  $fb = $this->getGenericFeedbackOutput((int) $active_id, $pass);
1145  $feedback .= strlen($fb) ? $fb : '';
1146  }
1147 
1148  $fb = $this->getSpecificFeedbackOutput(
1149  $this->object->fetchIndexedValuesFromValuePairs($user_solutions)
1150  );
1151  $feedback .= strlen($fb) ? $fb : '';
1152  }
1153  if (strlen($feedback)) {
1154  $cssClass = (
1155  $this->hasCorrectSolution($active_id, $pass) ?
1157  );
1158 
1159  $solutiontemplate->setVariable("ILC_FB_CSS_CLASS", $cssClass);
1160  $solutiontemplate->setVariable("FEEDBACK", ilLegacyFormElementsUtil::prepareTextareaOutput($feedback, true));
1161  }
1162 
1163  $solutiontemplate->setVariable("SOLUTION_OUTPUT", $questionoutput);
1164 
1165  $solutionoutput = $solutiontemplate->get();
1166 
1167  if (!$show_question_only) {
1168  // get page object output
1169  $solutionoutput = $this->getILIASPage($solutionoutput);
1170  }
1171 
1172  return $solutionoutput;
1173  }
1174 
1181  protected function getBestSolutionText($gap, $gap_index, $gap_combinations): string
1182  {
1183  $combination = null;
1184  foreach ((array) $gap_combinations as $combiGapSolRow) {
1185  if ($combiGapSolRow['gap_fi'] == $gap_index && $combiGapSolRow['best_solution']) {
1186  $combination = $combiGapSolRow;
1187  break;
1188  }
1189  }
1190  $best_solution_text = ilLegacyFormElementsUtil::prepareFormOutput(
1191  $gap->getBestSolutionOutput(
1192  $this->object->getShuffler(),
1193  $combination
1194  )
1195  );
1196  return $best_solution_text;
1197  }
1198 
1199  public function getGenericFeedbackOutput(int $active_id, $pass): string
1200  {
1201  $manual_feedback = ilObjTest::getManualFeedback($active_id, $this->object->getId(), $pass);
1202  if (strlen($manual_feedback)) {
1203  return $manual_feedback;
1204  }
1205  $correct_feedback = $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), true);
1206  $incorrect_feedback = $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), false);
1207 
1208  $output = '';
1209  if ($correct_feedback . $incorrect_feedback !== '') {
1210  $output = $this->genericFeedbackOutputBuilder($correct_feedback, $incorrect_feedback, $active_id, $pass);
1211  }
1212  //$test = new ilObjTest($this->object->active_id);
1214  }
1215 
1216  public function getTestOutput(
1217  $active_id,
1218  // hey: prevPassSolutions - will be always available from now on
1219  $pass,
1220  // hey.
1221  $is_postponed = false,
1222  $use_post_solutions = false,
1223  $show_feedback = false
1224  ): string {
1225  // get the solution of the user for the active pass or from the last pass if allowed
1226  $user_solution = [];
1227  if ($use_post_solutions !== false) {
1228  $indexedSolution = $this->object->fetchSolutionSubmit($use_post_solutions);
1229  $user_solution = $this->object->fetchValuePairsFromIndexedValues($indexedSolution);
1230  } elseif ($active_id) {
1231  $user_solution = $this->object->getTestOutputSolutions($active_id, $pass);
1232  // hey.
1233  if (!is_array($user_solution)) {
1234  $user_solution = [];
1235  }
1236  }
1237 
1238  $template = new ilTemplate("tpl.il_as_qpl_cloze_question_output.html", true, true, "Modules/TestQuestionPool");
1239  $output = $this->object->getClozeTextForHTMLOutput();
1240  foreach ($this->object->getGaps() as $gap_index => $gap) {
1241  switch ($gap->getType()) {
1242  case CLOZE_TEXT:
1243  $gaptemplate = new ilTemplate("tpl.il_as_qpl_cloze_question_gap_text.html", true, true, "Modules/TestQuestionPool");
1244  $gap_size = $gap->getGapSize() > 0 ? $gap->getGapSize() : $this->object->getFixedTextLength();
1245 
1246  if ($gap_size > 0) {
1247  $gaptemplate->setCurrentBlock('size_and_maxlength');
1248  $gaptemplate->setVariable("TEXT_GAP_SIZE", $gap_size);
1249  $gaptemplate->parseCurrentBlock();
1250  }
1251 
1252  $gaptemplate->setVariable("GAP_COUNTER", $gap_index);
1253  foreach ($user_solution as $solution) {
1254  if (strcmp($solution['value1'], $gap_index) == 0) {
1255  $gaptemplate->setVariable("VALUE_GAP", " value=\"" . ilLegacyFormElementsUtil::prepareFormOutput(
1256  $solution['value2']
1257  ) . "\"");
1258  }
1259  }
1260  // fau: fixGapReplace - use replace function
1261  $output = $this->object->replaceFirstGap($output, $gaptemplate->get());
1262  // fau.
1263  break;
1264  case CLOZE_SELECT:
1265  $gaptemplate = new ilTemplate("tpl.il_as_qpl_cloze_question_gap_select.html", true, true, "Modules/TestQuestionPool");
1266  foreach ($gap->getItems($this->object->getShuffler(), $gap_index) as $item) {
1267  $gaptemplate->setCurrentBlock("select_gap_option");
1268  $gaptemplate->setVariable("SELECT_GAP_VALUE", $item->getOrder());
1269  $gaptemplate->setVariable(
1270  "SELECT_GAP_TEXT",
1271  ilLegacyFormElementsUtil::prepareFormOutput($item->getAnswerText())
1272  );
1273  foreach ($user_solution as $solution) {
1274  if (strcmp($solution['value1'], $gap_index) == 0) {
1275  if (strcmp($solution['value2'], $item->getOrder()) == 0) {
1276  $gaptemplate->setVariable("SELECT_GAP_SELECTED", " selected=\"selected\"");
1277  }
1278  }
1279  }
1280  $gaptemplate->parseCurrentBlock();
1281  }
1282  $gaptemplate->setVariable("PLEASE_SELECT", $this->lng->txt("please_select"));
1283  $gaptemplate->setVariable("GAP_COUNTER", $gap_index);// fau: fixGapReplace - use replace function
1284  $output = $this->object->replaceFirstGap($output, $gaptemplate->get());
1285  // fau.
1286  break;
1287  case CLOZE_NUMERIC:
1288  $gaptemplate = new ilTemplate("tpl.il_as_qpl_cloze_question_gap_numeric.html", true, true, "Modules/TestQuestionPool");
1289  $gap_size = $gap->getGapSize() > 0 ? $gap->getGapSize() : $this->object->getFixedTextLength();
1290  if ($gap_size > 0) {
1291  $gaptemplate->setCurrentBlock('size_and_maxlength');
1292  $gaptemplate->setVariable("TEXT_GAP_SIZE", $gap_size);
1293  $gaptemplate->parseCurrentBlock();
1294  }
1295 
1296  $gaptemplate->setVariable("GAP_COUNTER", $gap_index);
1297  foreach ($user_solution as $solution) {
1298  if (strcmp($solution['value1'], $gap_index) == 0) {
1299  $gaptemplate->setVariable("VALUE_GAP", " value=\"" . ilLegacyFormElementsUtil::prepareFormOutput(
1300  $solution['value2']
1301  ) . "\"");
1302  }
1303  }
1304  // fau: fixGapReplace - use replace function
1305  $output = $this->object->replaceFirstGap($output, $gaptemplate->get());
1306  // fau.
1307  break;
1308  }
1309  }
1310 
1311  $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
1312  $template->setVariable("CLOZETEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($output, true));
1313  $questionoutput = $template->get();
1314  $pageoutput = $this->outQuestionPage("", $is_postponed, $active_id, $questionoutput);
1315  return $pageoutput;
1316  }
1317 
1318  public function getSpecificFeedbackOutput(array $user_solution): string
1319  {
1320  if (!$this->object->feedbackOBJ->isSpecificAnswerFeedbackAvailable($this->object->getId())) {
1321  return '';
1322  }
1323 
1324  $feedback = '<table class="test_specific_feedback"><tbody>';
1325 
1326  foreach ($this->object->gaps as $gap_index => $gap) {
1327  $answer_value = $this->object->fetchAnswerValueForGap($user_solution, $gap_index);
1328  if ($gap->getType() !== assClozeGap::TYPE_TEXT
1329  && $answer_value === '') {
1330  continue;
1331  }
1332  $answer_index = $this->object->feedbackOBJ->determineAnswerIndexForAnswerValue($gap, $answer_value);
1333  $fb = $this->object->feedbackOBJ->determineTestOutputGapFeedback($gap_index, $answer_index);
1334 
1335  $caption = $this->lng->txt('gap') . ' ' . ($gap_index + 1) . ': ';
1336  $feedback .= '<tr><td>';
1337  $feedback .= $caption . '</td><td>';
1338  $feedback .= $fb . '</td> </tr>';
1339  }
1340  $feedback .= '</tbody></table>';
1341 
1342  return ilLegacyFormElementsUtil::prepareTextareaOutput($feedback, true);
1343  }
1344 
1355  {
1356  return [];
1357  }
1358 
1369  {
1370  return [];
1371  }
1372 
1379  public function getAggregatedAnswersView(array $relevant_answers): string
1380  {
1381  $overview = [];
1382  $aggregation = [];
1383  foreach ($relevant_answers as $answer) {
1384  $overview[$answer['active_fi']][$answer['pass']][$answer['value1']] = $answer['value2'];
1385  }
1386 
1387  foreach ($overview as $active) {
1388  foreach ($active as $answer) {
1389  foreach ($answer as $option => $value) {
1390  $aggregation[$option][$value] = $aggregation[$option][$value] + 1;
1391  }
1392  }
1393  }
1394 
1395  $html = '<div>';
1396  $i = 0;
1397  foreach ($this->object->getGaps() as $gap_index => $gap) {
1398  if ($gap->type == CLOZE_SELECT) {
1399  $html .= '<p>Gap ' . ($i + 1) . ' - SELECT</p>';
1400  $html .= '<ul>';
1401  $j = 0;
1402  foreach ($gap->getItems($this->object->getShuffler(), $gap_index) as $gap_item) {
1403  $aggregate = $aggregation[$i];
1404  $html .= '<li>' . $gap_item->getAnswerText() . ' - ' . ($aggregate[$j] ? $aggregate[$j] : 0) . '</li>';
1405  $j++;
1406  }
1407  $html .= '</ul>';
1408  }
1409 
1410  if ($gap->type == CLOZE_TEXT) {
1411  $present_elements = [];
1412  foreach ($gap->getItems($this->randomGroup->shuffleArray(new Seed\RandomSeed())) as $item) {
1414  $present_elements[] = $item->getAnswertext();
1415  }
1416 
1417  $html .= '<p>Gap ' . ($i + 1) . ' - TEXT</p>';
1418  $html .= '<ul>';
1419  $aggregate = (array) $aggregation[$i];
1420  foreach ($aggregate as $answer => $count) {
1421  $show_mover = '';
1422  if (in_array($answer, $present_elements)) {
1423  $show_mover = ' style="display: none;" ';
1424  }
1425 
1426  $html .= '<li>' . $answer . ' - ' . $count
1427  . '&nbsp;<button class="clone_fields_add btn btn-link" ' . $show_mover . ' data-answer="' . $answer . '" name="add_gap_' . $i . '_0">
1428  <span class="sr-only"></span><span class="glyphicon glyphicon-plus"></span></button>
1429  </li>';
1430  }
1431  $html .= '</ul>';
1432  }
1433 
1434  if ($gap->type == CLOZE_NUMERIC) {
1435  $html .= '<p>Gap ' . ($i + 1) . ' - NUMERIC</p>';
1436  $html .= '<ul>';
1437  $j = 0;
1438  foreach ($gap->getItems($this->object->getShuffler(), $gap_index) as $gap_item) {
1439  $aggregate = (array) $aggregation[$i];
1440  foreach ($aggregate as $answer => $count) {
1441  $html .= '<li>' . $answer . ' - ' . $count . '</li>';
1442  }
1443  $j++;
1444  }
1445  $html .= '</ul>';
1446  }
1447  $i++;
1448  $html .= '<hr />';
1449  }
1450 
1451  $html .= '</div>';
1452  return $html;
1453  }
1454 
1455  public function applyIndizesToGapText($question_text): string
1456  {
1457  $parts = explode('[gap', $question_text);
1458  $i = 0;
1459  $question_text = '';
1460  foreach ($parts as $part) {
1461  if ($i == 0) {
1462  $question_text .= $part;
1463  } else {
1464  $question_text .= '[gap ' . $i . $part;
1465  }
1466  $i++;
1467  }
1468  return $question_text;
1469  }
1470 
1471  public function removeIndizesFromGapText($question_text): string
1472  {
1473  $parts = preg_split('/\[gap \d*\]/', $question_text);
1474  $question_text = implode('[gap]', $parts);
1475  return $question_text;
1476  }
1477 
1482  private function populateSolutiontextToGapTpl($gaptemplate, $gap, $solutiontext): void
1483  {
1484  if ($this->isRenderPurposePrintPdf()) {
1485  $gaptemplate->setCurrentBlock('gap_span');
1486  $gaptemplate->setVariable('SPAN_SOLUTION', $solutiontext);
1487  } elseif ($gap->getType() == CLOZE_SELECT) {
1488  $gaptemplate->setCurrentBlock('gap_select');
1489  $gaptemplate->setVariable('SELECT_SOLUTION', $solutiontext);
1490  } else {
1491  $gap_size = $gap->getGapSize() > 0 ? $gap->getGapSize() : $this->object->getFixedTextLength();
1492 
1493  if ($gap_size > 0) {
1494  $gaptemplate->setCurrentBlock('gap_size');
1495  $gaptemplate->setVariable("GAP_SIZE", $gap_size);
1496  $gaptemplate->parseCurrentBlock();
1497  }
1498 
1499  $gaptemplate->setCurrentBlock('gap_input');
1500  $gaptemplate->setVariable('INPUT_SOLUTION', $solutiontext);
1501  }
1502 
1503 
1504  $gaptemplate->parseCurrentBlock();
1505  }
1506 
1507  protected function hasAddAnswerAction($relevantAnswers, $questionIndex): bool
1508  {
1509  foreach ($this->getAnswersFrequency($relevantAnswers, $questionIndex) as $answer) {
1510  if (isset($answer['actions'])) {
1511  return true;
1512  }
1513  }
1514 
1515  return false;
1516  }
1517 
1518  public function getAnswerFrequencyTableGUI($parentGui, $parentCmd, $relevantAnswers, $questionIndex): ilAnswerFrequencyStatisticTableGUI
1519  {
1520  $table = parent::getAnswerFrequencyTableGUI(
1521  $parentGui,
1522  $parentCmd,
1523  $relevantAnswers,
1524  $questionIndex
1525  );
1526 
1527  $table->setTitle(
1528  sprintf(
1529  $this->lng->txt('tst_corrections_answers_tbl_subindex'),
1530  $this->lng->txt('gap') . ' ' . ($questionIndex + 1)
1531  )
1532  );
1533 
1534  if ($this->hasAddAnswerAction($relevantAnswers, $questionIndex)) {
1535  $table->addColumn('', '', '200');
1536  }
1537 
1538  return $table;
1539  }
1540 
1541  public function getSubQuestionsIndex(): array
1542  {
1543  return array_keys($this->object->getGaps());
1544  }
1545 
1546  protected function getAnswerTextLabel($gapIndex, $answer)
1547  {
1548  $gap = $this->object->getGap($gapIndex);
1549 
1550  switch ($gap->type) {
1551  case CLOZE_NUMERIC:
1552  case CLOZE_TEXT:
1553  return $answer;
1554 
1555  case CLOZE_SELECT:
1556  default:
1557  $items = $gap->getItems($this->randomGroup->dontShuffle());
1558  return $items[$answer]->getAnswertext();
1559  }
1560  }
1561 
1562  protected function completeAddAnswerAction($answers, $gap_index): array
1563  {
1564  $gap = $this->object->getGap($gap_index);
1565 
1566  if ($gap->type != CLOZE_TEXT ||
1567  $this->isUsedInCombinations($gap_index)) {
1568  return $answers;
1569  }
1570 
1571  foreach ($answers as $key => $ans) {
1572  $found = false;
1573 
1574  foreach ($gap->getItems($this->randomGroup->dontShuffle()) as $item) {
1575  if ($ans['answer'] !== $item->getAnswerText()) {
1576  continue;
1577  }
1578 
1579  $found = true;
1580  break;
1581  }
1582 
1583  if (!$found) {
1584  $answers[$key]['addable'] = true;
1585  }
1586  }
1587 
1588  return $answers;
1589  }
1590 
1591  public function getAnswersFrequency($relevantAnswers, $questionIndex): array
1592  {
1593  $answers = [];
1594 
1595  foreach ($relevantAnswers as $row) {
1596  if ($row['value1'] != $questionIndex) {
1597  continue;
1598  }
1599 
1600  if (!isset($answers[$row['value2']])) {
1601  $label = $this->getAnswerTextLabel($row['value1'], $row['value2']);
1602 
1603  $answers[$row['value2']] = [
1604  'answer' => $label, 'frequency' => 0
1605  ];
1606  }
1607 
1608  $answers[$row['value2']]['frequency']++;
1609  }
1610 
1611  $answers = $this->completeAddAnswerAction($answers, $questionIndex);
1612 
1613  return $answers;
1614  }
1615 
1616  protected function isUsedInCombinations($gapIndex): bool
1617  {
1618  foreach ($this->object->getGapCombinations() as $combination) {
1619  if ($combination['gap_fi'] != $gapIndex) {
1620  continue;
1621  }
1622 
1623  return true;
1624  }
1625 
1626  return false;
1627  }
1628 
1629  protected function getGapCombinations(): array
1630  {
1631  $combinations = [];
1632 
1633  foreach ($this->object->getGapCombinations() as $c) {
1634  if (!isset($combinations[$c['cid']])) {
1635  $combinations[$c['cid']] = [];
1636  }
1637 
1638  if (!isset($combinations[$c['cid']][$c['row_id']])) {
1639  $combinations[$c['cid']][$c['row_id']] = [
1640  'gaps' => [], 'points' => $c['points'],
1641  ];
1642  }
1643 
1644  if (!isset($combinations[$c['cid']][$c['row_id']]['gaps'][$c['gap_fi']])) {
1645  $combinations[$c['cid']][$c['row_id']]['gaps'][$c['gap_fi']] = [];
1646  }
1647 
1648  $combinations[$c['cid']][$c['row_id']]['gaps'][$c['gap_fi']] = $c['answer'];
1649  }
1650 
1651  return $combinations;
1652  }
1653 
1655  {
1656  foreach ($this->object->getGaps() as $gapIndex => $gap) {
1658  $form,
1659  $gap,
1660  $gapIndex,
1661  $this->isUsedInCombinations($gapIndex)
1662  );
1663  }
1664 
1665  if ($this->object->getGapCombinationsExists()) {
1666  foreach ($this->getGapCombinations() as $combiIndex => $gapCombination) {
1667  $this->populateGapCombinationCorrectionFormProperty($form, $gapCombination, $combiIndex);
1668  }
1669  }
1670  }
1671 
1672  protected function populateGapCombinationCorrectionFormProperty(ilPropertyFormGUI $form, $gapCombi, $combiIndex): void
1673  {
1674  $header = new ilFormSectionHeaderGUI();
1675  $header->setTitle("Gap Combination " . ($combiIndex + 1));
1676  $form->addItem($header);
1677 
1678  $inp = new ilAssClozeTestCombinationVariantsInputGUI('Answers', 'combination_' . $combiIndex);
1679  $inp->setValues($gapCombi);
1680  $form->addItem($inp);
1681  }
1682 
1688  protected function populateGapCorrectionFormProperties($form, $gap, $gapIndex, $hidePoints): void
1689  {
1690  $header = new ilFormSectionHeaderGUI();
1691  $header->setTitle($this->lng->txt("gap") . " " . ($gapIndex + 1));
1692  $form->addItem($header);
1693 
1694  if ($gap->getType() == CLOZE_TEXT || $gap->getType() == CLOZE_SELECT) {
1695  $this->populateTextOrSelectGapCorrectionFormProperty($form, $gap, $gapIndex, $hidePoints);
1696  } elseif ($gap->getType() == CLOZE_NUMERIC) {
1697  foreach ($gap->getItemsRaw() as $item) {
1698  $this->populateNumericGapCorrectionFormProperty($form, $item, $gapIndex, $hidePoints);
1699  }
1700  }
1701  }
1702 
1703  protected function populateTextOrSelectGapCorrectionFormProperty($form, $gap, $gapIndex, $hidePoints): void
1704  {
1705  $values = new ilAssAnswerCorrectionsInputGUI($this->lng->txt("values"), "gap_" . $gapIndex);
1706  $values->setHidePointsEnabled($hidePoints);
1707  $values->setRequired(true);
1708  $values->setQuestionObject($this->object);
1709  $values->setValues($gap->getItemsRaw());
1710  $form->addItem($values);
1711  }
1712 
1713  protected function populateNumericGapCorrectionFormProperty($form, $item, $gapIndex, $hidePoints): void
1714  {
1715  $value = new ilNumberInputGUI($this->lng->txt('value'), "gap_" . $gapIndex . "_numeric");
1716  $value->allowDecimals(true);
1717  $value->setSize(10);
1718  $value->setValue(ilLegacyFormElementsUtil::prepareFormOutput($item->getAnswertext()));
1719  $value->setRequired(true);
1720  $form->addItem($value);
1721 
1722  $lowerbound = new ilNumberInputGUI($this->lng->txt('range_lower_limit'), "gap_" . $gapIndex . "_numeric_lower");
1723  $lowerbound->allowDecimals(true);
1724  $lowerbound->setSize(10);
1725  $lowerbound->setRequired(true);
1726  $lowerbound->setValue(ilLegacyFormElementsUtil::prepareFormOutput($item->getLowerBound()));
1727  $form->addItem($lowerbound);
1728 
1729  $upperbound = new ilNumberInputGUI($this->lng->txt('range_upper_limit'), "gap_" . $gapIndex . "_numeric_upper");
1730  $upperbound->allowDecimals(true);
1731  $upperbound->setSize(10);
1732  $upperbound->setRequired(true);
1733  $upperbound->setValue(ilLegacyFormElementsUtil::prepareFormOutput($item->getUpperBound()));
1734  $form->addItem($upperbound);
1735 
1736  if (!$hidePoints) {
1737  $points = new ilNumberInputGUI($this->lng->txt('points'), "gap_" . $gapIndex . "_numeric_points");
1738  $points->allowDecimals(true);
1739  $points->setSize(3);
1740  $points->setRequired(true);
1741  $points->setValue(ilLegacyFormElementsUtil::prepareFormOutput($item->getPoints()));
1742  $form->addItem($points);
1743  }
1744  }
1745 
1750  {
1751  foreach ($this->object->getGaps() as $gapIndex => $gap) {
1752  if ($this->isUsedInCombinations($gapIndex)) {
1753  continue;
1754  }
1755 
1756  $this->saveGapCorrectionFormProperty($form, $gap, $gapIndex);
1757  }
1758 
1759  if ($this->object->getGapCombinationsExists()) {
1761  }
1762  }
1763 
1765  {
1766  if ($gap->getType() == CLOZE_TEXT || $gap->getType() == CLOZE_SELECT) {
1768  } elseif ($gap->getType() == CLOZE_NUMERIC) {
1769  foreach ($gap->getItemsRaw() as $item) {
1770  $this->saveNumericGapCorrectionFormProperty($form, $item, $gapIndex);
1771  }
1772  }
1773  }
1774 
1776  {
1777  $answers = $form->getItemByPostVar('gap_' . $gapIndex)->getValues();
1778 
1779  foreach ($gap->getItemsRaw() as $index => $item) {
1780  $item->setPoints((float) str_replace(',', '.', $answers[$index]->getPoints()));
1781  }
1782  }
1783 
1785  {
1786  $item->setAnswertext($form->getInput('gap_' . $gapIndex . '_numeric'));
1787  $item->setLowerBound($form->getInput('gap_' . $gapIndex . '_numeric_lower'));
1788  $item->setUpperBound($form->getInput('gap_' . $gapIndex . '_numeric_upper'));
1789  $item->setPoints((float) str_replace(',', '.', $form->getInput('gap_' . $gapIndex . '_numeric_points')));
1790  }
1791 
1793  {
1794  // please dont ask (!) -.-
1795 
1796  $combinationPoints = ['points' => [], 'select' => []];
1797  $combinationValues = [];
1798 
1799  foreach ($this->getGapCombinations() as $combiId => $combi) {
1800  $values = $form->getItemByPostVar('combination_' . $combiId)->getValues();
1801 
1802  if (!isset($combinationPoints['points'][$combiId])) {
1803  $combinationPoints['points'][$combiId] = [];
1804  $combinationPoints['select'][$combiId] = [];
1805  $combinationValues[$combiId] = [];
1806  }
1807 
1808  foreach ($combi as $varId => $variant) {
1809  $combinationPoints['points'][$combiId][$varId] = (float) str_replace(',', '.', $values[$varId]['points']);
1810  $combinationPoints['select'][$combiId] = array_keys($values[$varId]['gaps']);
1811  $combinationValues[$combiId][$varId] = array_values($values[$varId]['gaps']);
1812  }
1813  }
1814 
1815  $combinationPoints = $combinationPoints;
1816  $combinationValues = $combinationValues;
1817 
1818  $assClozeGapCombinationObject = new assClozeGapCombination();
1819  $assClozeGapCombinationObject->clearGapCombinationsFromDb($this->object->getId());
1820 
1821  $assClozeGapCombinationObject->saveGapCombinationToDb(
1822  $this->object->getId(),
1823  $combinationPoints,
1824  $combinationValues
1825  );
1826  $this->object->setGapCombinationsExists(true);
1827  }
1828 }
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getSelfAssessmentTags()
Get tags allowed in question tags in self assessment mode.
hasCorrectSolution($activeId, $passIndex)
populateNumericGapCorrectionFormProperty($form, $item, $gapIndex, $hidePoints)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
genericFeedbackOutputBuilder(string $feedback_correct, string $feedback_incorrect, int $active_id, ?int $pass)
generateCorrectnessIconsForCorrectness(int $correctness)
getAnswersFrequency($relevantAnswers, $questionIndex)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getAfterParticipationSuppressionAnswerPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
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:64
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
getAggregatedAnswersView(array $relevant_answers)
Returns an html string containing a question specific representation of the answers so far given in t...
const CLOZE_TEXT
Cloze question constants.
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
saveGapCorrectionFormProperty(ilPropertyFormGUI $form, assClozeGap $gap, $gapIndex)
setPoints($points=0.0)
Sets the points.
getTestOutput( $active_id, $pass, $is_postponed=false, $use_post_solutions=false, $show_feedback=false)
parseCurrentBlock(string $blockname=self::DEFAULT_BLOCK)
Parses the given block.
escapeTemplatePlaceholders(string $text)
getAnswerFrequencyTableGUI($parentGui, $parentCmd, $relevantAnswers, $questionIndex)
saveGapCombinationCorrectionFormProperties(ilPropertyFormGUI $form)
Class for cloze question gaps.
setOptions(array $a_options)
populateTextGapFormPart($form, $gap, $gapCounter)
Populates the form-part for a text gap.
populateSolutiontextToGapTpl($gaptemplate, $gap, $solutiontext)
writeQuestionSpecificPostData(ilPropertyFormGUI $form)
Extracts the question specific values from $_POST and applies them to the data object.
ilGlobalPageTemplate $tpl
static prepareFormOutput($a_str, bool $a_strip=false)
populateTaxonomyFormSection(ilPropertyFormGUI $form)
getInput(string $a_post_var, bool $ensureValidation=true)
Returns the input of an item, if item provides getInput method and as fallback the value of the HTTP-...
Customizing of pimple-DIC for ILIAS.
Definition: Container.php:35
getBestSolutionText($gap, $gap_index, $gap_combinations)
addQuestionFormCommandButtons(ilPropertyFormGUI $form)
getAfterParticipationSuppressionQuestionPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
This class represents a single choice wizard property in a property form.
global $DIC
Definition: feed.php:28
array $details
Details for error message relating to last request processed.
Definition: System.php:109
populateGapCombinationCorrectionFormProperty(ilPropertyFormGUI $form, $gapCombi, $combiIndex)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getItemsRaw()
Gets the items of a cloze gap.
Cloze test question GUI representation.
writeAnswerSpecificPostData(ilPropertyFormGUI $form)
Extracts the answer specific values from $_POST and applies them to the data object.
populateQuestionSpecificFormPart(ilPropertyFormGUI $form)
Adds the question specific forms parts to a question property form gui.
const CLOZE_SELECT
createGaps()
Create gaps from cloze text.
__construct(VocabulariesInterface $vocabularies)
get(string $part=self::DEFAULT_BLOCK)
Renders the given block and returns the html string.
hasAddAnswerAction($relevantAnswers, $questionIndex)
populateGapFormPart($form, $gapCounter)
Populates a gap form-part.
populateAnswerSpecificFormPart(ilPropertyFormGUI $form)
Adds the answer specific form parts to a question property form gui.
ILIAS UI Factory $ui_factory
populateTextOrSelectGapCorrectionFormProperty($form, $gap, $gapIndex, $hidePoints)
string $key
Consumer key/client ID value.
Definition: System.php:193
saveTextOrSelectGapCorrectionFormProperty(ilPropertyFormGUI $form, assClozeGap $gap, $gapIndex)
hasErrorInGapCombinationPoints(array $gap_combinations)
populateGapSizeFormPart($form, $gap, $gapCounter)
writePostData(bool $always=false)
{}
$lifecycle
Basic GUI class for assessment questions.
setRequired(bool $a_required)
saveNumericGapCorrectionFormProperty(ilPropertyFormGUI $form, assAnswerCloze $item, $gapIndex)
setUpperBound(string $bound)
Sets the upper bound.
removegap()
Remove a gap answer.
populateGapCorrectionFormProperties($form, $gap, $gapIndex, $hidePoints)
addgap()
Add a gap answer.
populateNumericGapFormPart($form, $gap, $gapCounter)
Populates the form-part for a numeric gap.
static getInstance()
populateSelectGapFormPart($form, $gap, $gapCounter)
Populates the form-part for a select gap.
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.
ILIAS UI Renderer $ui_renderer
getILIASPage(string $html="")
Returns the ILIAS Page around a question.
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,)
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="", $inlineFeedbackEnabled=false)
completeAddAnswerAction($answers, $gap_index)
applyIndizesToGapText($question_text)
This class represents a text area property in a property form.
getGenericFeedbackOutput(int $active_id, $pass)
const ADDITIONAL_CONTENT_EDITING_MODE_RTE
setAnswertext($answertext="")
Sets the answer text.
addBasicQuestionFormProperties(ilPropertyFormGUI $form)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
setLowerBound(string $bound)
Sets the lower boind.
$check
Definition: buildRTE.php:81
getAddGapButtonClickClosure(string $gap_type)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
addNumberOfTriesToFormIfNecessary(ilPropertyFormGUI $form)
const CLOZE_NUMERIC
$gapIndex
A temporary variable to store gap indexes of ilCtrl commands in the getCommand method.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setInlineStyle(string $a_style)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getManualFeedback(int $active_id, int $question_id, ?int $pass)
Retrieves the feedback comment for a question in a test if it is finalized.
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...
getPreview($show_question_only=false, $showInlineFeedback=false)
Creates a preview output of the question.
setVariable(string $variable, $value='')
Sets the given variable to the given value.
getSpecificFeedbackOutput(array $user_solution)
editQuestion($checkonly=false)
Creates an output of the edit form for the question.
removeIndizesFromGapText($question_text)
static _getUsedHTMLTags(string $a_module="")
Returns an array of all allowed HTML tags for text editing.
ArrayBasedRequestWrapper $post
__construct(int $id=-1)
assClozeTestGUI constructor
getAnswerTextLabel($gapIndex, $answer)