ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.assFormulaQuestionGUI.php
Go to the documentation of this file.
1 <?php
2 
19 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
20 
31 {
32  protected const HAS_SPECIAL_QUESTION_COMMANDS = true;
33 
40  public function __construct($id = -1)
41  {
43  $this->object = new assFormulaQuestion();
44  if ($id >= 0) {
45  $this->object->loadFromDb($id);
46  }
47  }
48 
49  protected function setQuestionSpecificTabs(ilTabsGUI $ilTabs): void
50  {
51  $ilTabs->addTarget('units', $this->ctrl->getLinkTargetByClass('ilLocalUnitConfigurationGUI', ''), '', 'illocalunitconfigurationgui');
52  }
53 
54  protected function callSpecialQuestionCommands(string $cmd): void
55  {
56  if (preg_match('/suggestrange_(\$r\d+)/', $cmd, $matches)) {
57  $this->addSaveOnEnterOnLoadCode();
58  $this->suggestRange($matches[1]);
59  }
60  }
61 
62  public function suggestRange(string $suggest_range_for_result): void
63  {
64  if ($this->writePostData()) {
65  $this->tpl->setOnScreenMessage('info', $this->getErrorMessage());
66  }
67  $this->editQuestion(false, $suggest_range_for_result);
68  }
69 
73  protected function writePostData(bool $always = false): int
74  {
75  $hasErrors = (!$always) ? $this->editQuestion(true) : false;
76  $checked = true;
77  if (!$hasErrors) {
78  $this->object->setTitle($this->request->string('title'));
79  $this->object->setAuthor($this->request->string('author'));
80  $this->object->setComment($this->request->string('comment'));
81  $this->object->setQuestion($this->request->string('question'));
82 
83  $this->object->parseQuestionText();
84  $found_vars = [];
85  $found_results = [];
86 
87  foreach ($this->request->getParsedBody() as $key => $value) {
88  if (preg_match("/^unit_(\\\$v\d+)$/", $key, $matches)) {
89  array_push($found_vars, $matches[1]);
90  }
91  if (preg_match("/^unit_(\\\$r\d+)$/", $key, $matches)) {
92  array_push($found_results, $matches[1]);
93  }
94  }
95 
96  try {
97  $lifecycle = ilAssQuestionLifecycle::getInstance($_POST['lifecycle']);
98  $this->object->setLifecycle($lifecycle);
100  }
101 
102  if (!$this->object->checkForDuplicateResults()) {
103  $this->addErrorMessage($this->lng->txt("err_duplicate_results"));
104  $checked = false;
105  }
106 
107  foreach ($found_vars as $variable) {
108  if ($this->object->getVariable($variable) != null) {
109  $varObj = new assFormulaQuestionVariable(
110  $variable,
111  str_replace(',', '.', $_POST["range_min_{$variable}"]),
112  str_replace(',', '.', $_POST["range_max_{$variable}"]),
113  isset($_POST["unit_{$variable}"]) ? $this->object->getUnitrepository()->getUnit(
114  $_POST["unit_{$variable}"]
115  ) : null,
116  (int) $this->request->float("precision_{$variable}"),
117  (int) $this->request->float("intprecision_{$variable}")
118  );
119  $this->object->addVariable($varObj);
120  }
121  }
122 
123  $tmp_form_vars = [];
124  $tmp_quest_vars = [];
125  foreach ($found_results as $result) {
126  $tmp_res_match = preg_match_all('/([$][v][0-9]*)/', $_POST["formula_{$result}"], $form_vars);
127  $tmp_form_vars = array_merge($tmp_form_vars, $form_vars[0]);
128 
129  $tmp_que_match = preg_match_all('/([$][v][0-9]*)/', $_POST['question'], $quest_vars);
130  $tmp_quest_vars = array_merge($tmp_quest_vars, $quest_vars[0]);
131  }
132  $result_has_undefined_vars = array_diff($tmp_form_vars, $found_vars);
133  $question_has_unused_vars = array_diff($tmp_quest_vars, $tmp_form_vars);
134 
135  if (count($result_has_undefined_vars) > 0 || count($question_has_unused_vars) > 0) {
136  $error_message = '';
137  if (count($result_has_undefined_vars) > 0) {
138  $error_message .= $this->lng->txt("res_contains_undef_var") . '<br>';
139  }
140  if (count($question_has_unused_vars) > 0) {
141  $error_message .= $this->lng->txt("que_contains_unused_var");
142  }
143  $checked = false;
144  if ($this->isSaveCommand()) {
145  $this->tpl->setOnScreenMessage('failure', $error_message);
146  }
147  }
148  foreach ($found_results as $result) {
149  if ($this->object->getResult($result) != null) {
150  $resObj = new assFormulaQuestionResult(
151  $result,
152  str_replace(',', '.', $_POST["range_min_{$result}"]),
153  str_replace(',', '.', $_POST["range_max_{$result}"]),
154  $this->request->float("tolerance_{$result}"),
155  isset($_POST["unit_{$result}"]) ? $this->object->getUnitrepository()->getUnit(
156  $_POST["unit_{$result}"]
157  ) : null,
158  $this->request->string("formula_{$result}"),
159  $this->request->float("points_{$result}"),
160  (int) $this->request->float("precision_{$result}"),
161  $this->request->int("rating_advanced_{$result}") !== 1,
162  $this->request->int("rating_advanced_{$result}") === 1 ? $this->request->float("rating_sign_{$result}") : null,
163  $this->request->int("rating_advanced_{$result}") === 1 ? $this->request->float("rating_value_{$result}") : null,
164  $this->request->int("rating_advanced_{$result}") === 1 ? $this->request->float("rating_unit_{$result}") : null,
165  $this->request->int("result_type_{$result}")
166  );
167  $this->object->addResult($resObj);
168  $this->object->addResultUnits($resObj, $_POST["units_{$result}"] ?? []);
169  }
170  }
171  if ($checked == false) {
172  $this->editQuestion();
173  return 1;
174  } else {
175  $this->saveTaxonomyAssignments();
176  $this->resetSavedPreviewSession();
177  return 0;
178  }
179  } else {
180  return 1;
181  }
182  }
183 
184  public function resetSavedPreviewSession(): void
185  {
186  global $DIC;
187  $ilUser = $DIC['ilUser'];
188  $user_id = $ilUser->getId();
189  $question_id = $this->object->getId();
190  $ilAssQuestionPreviewSession = new ilAssQuestionPreviewSession($user_id, $question_id);
191  $ilAssQuestionPreviewSession->setParticipantsSolution([]);
192  }
193 
194  public function isSaveCommand(): bool
195  {
196  return in_array($this->ctrl->getCmd(), array('save', 'saveEdit', 'saveReturn'));
197  }
198 
202  public function editQuestion($checkonly = false, string $suggest_range_for_result = ''): bool
203  {
204  $save = $this->isSaveCommand();
205 
206  $this->getQuestionTemplate();
207 
208  $form = new ilPropertyFormGUI();
209  $this->editForm = $form;
210 
211  $form->setFormAction($this->ctrl->getFormAction($this));
212  $form->setTitle($this->outQuestionType());
213  $form->setMultipart(false);
214  $form->setTableWidth('100%');
215  $form->setId('assformulaquestion');
216 
217  $this->addBasicQuestionFormProperties($form);
218 
219  // Add info text
220  $question = $form->getItemByPostVar('question');
221  $question->setInfo($this->lng->txt('fq_question_desc'));
222 
223  $variables = $this->object->getVariables();
224  $categorized_units = $this->object->getUnitrepository()->getCategorizedUnits();
225  $result_units = $this->object->getAllResultUnits();
226 
227  $unit_options = [];
228  $category_name = '';
229  $new_category = false;
230  foreach ($categorized_units as $item) {
234  if ($item instanceof assFormulaQuestionUnitCategory) {
235  if ($category_name != $item->getDisplayString()) {
236  $new_category = true;
237  $category_name = $item->getDisplayString();
238  }
239  continue;
240  }
241  $unit_options[$item->getId()] = $item->getDisplayString() . ($new_category ? ' (' . $category_name . ')' : '');
242  $new_category = false;
243  }
244 
245  if (count($variables)) {
246  uasort($variables, function (assFormulaQuestionVariable $v1, assFormulaQuestionVariable $v2) {
247  $num_v1 = (int) substr($v1->getVariable(), 2);
248  $num_v2 = (int) substr($v2->getVariable(), 2);
249  if ($num_v1 > $num_v2) {
250  return 1;
251  } elseif ($num_v1 < $num_v2) {
252  return -1;
253  }
254 
255  return 0;
256  });
257 
258  foreach ($variables as $variable) {
262  $variable_header = new ilFormSectionHeaderGUI();
263  $variable_header->setTitle(sprintf($this->lng->txt('variable_x'), $variable->getVariable()));
264 
265  $range_min = new ilNumberInputGUI($this->lng->txt('range_min'), 'range_min_' . $variable->getVariable());
266  $range_min->allowDecimals(true);
267  $range_min->setSize(3);
268  $range_min->setRequired(true);
269  $range_min->setValue($variable->getRangeMin());
270 
271  $range_max = new ilNumberInputGUI($this->lng->txt('range_max'), 'range_max_' . $variable->getVariable());
272  $range_max->allowDecimals(true);
273  $range_max->setSize(3);
274  $range_max->setRequired(true);
275  $range_max->setValue($variable->getRangeMax());
276 
277  $units = new ilSelectInputGUI($this->lng->txt('unit'), 'unit_' . $variable->getVariable());
278  $units->setOptions(array(0 => $this->lng->txt('no_selection')) + $unit_options);
279  if (is_object($variable->getUnit())) {
280  $units->setValue($variable->getUnit()->getId());
281  }
282 
283  $precision = new ilNumberInputGUI($this->lng->txt('precision'), 'precision_' . $variable->getVariable());
284  $precision->setRequired(true);
285  $precision->setSize(3);
286  $precision->setMinValue(0);
287  $precision->setValue($variable->getPrecision());
288  $precision->setInfo($this->lng->txt('fq_precision_info'));
289 
290  $intprecision = new ilNumberInputGUI($this->lng->txt('intprecision'), 'intprecision_' . $variable->getVariable());
291  $intprecision->setSize(3);
292  $intprecision->setMinValue(1);
293  $intprecision->setValue($variable->getIntprecision());
294  $intprecision->setInfo($this->lng->txt('intprecision_info'));
295 
296  $form->addItem($variable_header);
297  $form->addItem($range_min);
298  $form->addItem($range_max);
299  $form->addItem($units);
300  $form->addItem($precision);
301  $form->addItem($intprecision);
302  }
303  }
304  $quest_vars = [];
305  $result_vars = [];
306  $results = $this->object->getResults();
307  if (count($results)) {
308  uasort($results, function (assFormulaQuestionResult $r1, assFormulaQuestionResult $r2) {
309  $num_r1 = (int) substr($r1->getResult(), 2);
310  $num_r2 = (int) substr($r2->getResult(), 2);
311  if ($num_r1 > $num_r2) {
312  return 1;
313  } elseif ($num_r1 < $num_r2) {
314  return -1;
315  }
316 
317  return 0;
318  });
319 
320  foreach ($results as $result) {
324  $result_header = new ilFormSectionHeaderGUI();
325  $result_header->setTitle(sprintf($this->lng->txt('result_x'), $result->getResult()));
326 
327  $formula = new ilTextInputGUI($this->lng->txt('formula'), 'formula_' . $result->getResult());
328  $formula->setInfo($this->lng->txt('fq_formula_desc'));
329  $formula->setRequired(true);
330  $formula->setSize(50);
331  $formula->setValue($result->getFormula());
332  $formula->setSuffix(' = ' . $result->getResult());
333 
334  if (
335  $suggest_range_for_result !== '' &&
336  strcmp($suggest_range_for_result, $result->getResult()) == 0 &&
337  strlen($result->substituteFormula($variables, $results))
338  ) {
339  $result->suggestRange($variables, $results);
340  }
341 
342  $range_min = new ilNumberInputGUI($this->lng->txt('range_min'), 'range_min_' . $result->getResult());
343  $range_min->allowDecimals(true);
344  $range_min->setSize(3);
345  $range_min->setRequired(true);
346  $range_min->setValue($result->getRangeMin());
347 
348  $range_max = new ilNumberInputGUI($this->lng->txt('range_max'), 'range_max_' . $result->getResult());
349  $range_max->allowDecimals(true);
350  $range_max->setSize(3);
351  $range_max->setRequired(true);
352  $range_max->setValue($result->getRangeMax());
353 
354  $matches = [];
355 
356  $precision = new ilNumberInputGUI($this->lng->txt('precision'), 'precision_' . $result->getResult());
357  $precision->setRequired(true);
358  $precision->setSize(3);
359  $precision->setMinValue(0);
360  $precision->setInfo($this->lng->txt('fq_precision_info'));
361  $precision->setValue($result->getPrecision());
362 
363  $tolerance = new ilNumberInputGUI($this->lng->txt('tolerance'), 'tolerance_' . $result->getResult());
364  $tolerance->setSize(3);
365  $tolerance->setMinValue(0);
366  $tolerance->setMaxValue(100);
367  $tolerance->allowDecimals(true);
368  $tolerance->setInfo($this->lng->txt('tolerance_info'));
369  $tolerance->setValue($result->getTolerance());
370 
371  $suggest_range_button = new ilCustomInputGUI('', '');
372  $suggest_range_button->setHtml('<input type="submit" class="btn btn-default" name="cmd[suggestrange_' . $result->getResult() . ']" value="' . $this->lng->txt("suggest_range") . '" />');
373 
374  $sel_result_units = new ilSelectInputGUI($this->lng->txt('unit'), 'unit_' . $result->getResult());
375  $sel_result_units->setOptions(array(0 => $this->lng->txt('no_selection')) + $unit_options);
376  $sel_result_units->setInfo($this->lng->txt('result_unit_info'));
377  if (is_object($result->getUnit())) {
378  $sel_result_units->setValue($result->getUnit()->getId());
379  }
380 
381  $mc_result_units = new ilMultiSelectInputGUI($this->lng->txt('result_units'), 'units_' . $result->getResult());
382  $mc_result_units->setOptions($unit_options);
383  $mc_result_units->setInfo($this->lng->txt('result_units_info'));
384  $selectedvalues = [];
385  foreach ($unit_options as $unit_id => $txt) {
386  if ($this->hasResultUnit($result, $unit_id, $result_units)) {
387  $selectedvalues[] = $unit_id;
388  }
389  }
390  $mc_result_units->setValue($selectedvalues);
391 
392  $result_type = new ilRadioGroupInputGUI($this->lng->txt('result_type_selection'), 'result_type_' . $result->getResult());
393  $result_type->setRequired(true);
394 
395  $no_type = new ilRadioOption($this->lng->txt('no_result_type'), '0');
396  $no_type->setInfo($this->lng->txt('fq_no_restriction_info'));
397 
398  $result_dec = new ilRadioOption($this->lng->txt('result_dec'), '1');
399  $result_dec->setInfo($this->lng->txt('result_dec_info'));
400 
401  $result_frac = new ilRadioOption($this->lng->txt('result_frac'), '2');
402  $result_frac->setInfo($this->lng->txt('result_frac_info'));
403 
404  $result_co_frac = new ilRadioOption($this->lng->txt('result_co_frac'), '3');
405  $result_co_frac->setInfo($this->lng->txt('result_co_frac_info'));
406 
407  $result_type->addOption($no_type);
408  $result_type->addOption($result_dec);
409  $result_type->addOption($result_frac);
410  $result_type->addOption($result_co_frac);
411  $result_type->setValue(strlen($result->getResultType()) ? $result->getResultType() : 0);
412 
413  $points = new ilNumberInputGUI($this->lng->txt('points'), 'points_' . $result->getResult());
414  $points->allowDecimals(true);
415  $points->setRequired(true);
416  $points->setSize(3);
417  $points->setMinValue(0);
418  $points->setValue(strlen($result->getPoints()) ? $result->getPoints() : 1);
419 
420  $rating_type = new ilCheckboxInputGUI($this->lng->txt('advanced_rating'), 'rating_advanced_' . $result->getResult());
421  $rating_type->setValue(1);
422  $rating_type->setInfo($this->lng->txt('advanced_rating_info'));
423 
424  if (!$save) {
425  $advanced_rating = $this->canUseAdvancedRating($result);
426  if (!$advanced_rating) {
427  $rating_type->setDisabled(true);
428  $rating_type->setChecked(false);
429  } else {
430  $rating_type->setChecked(strlen($result->getRatingSimple()) && $result->getRatingSimple() ? false : true);
431  }
432  }
433 
434  $sign = new ilNumberInputGUI($this->lng->txt('rating_sign'), 'rating_sign_' . $result->getResult());
435  $sign->setRequired(true);
436  $sign->setSize(3);
437  $sign->setMinValue(0);
438  $sign->setValue($result->getRatingSign());
439  $rating_type->addSubItem($sign);
440 
441  $value = new ilNumberInputGUI($this->lng->txt('rating_value'), 'rating_value_' . $result->getResult());
442  $value->setRequired(true);
443  $value->setSize(3);
444  $value->setMinValue(0);
445  $value->setValue($result->getRatingValue());
446  $rating_type->addSubItem($value);
447 
448  $unit = new ilNumberInputGUI($this->lng->txt('rating_unit'), 'rating_unit_' . $result->getResult());
449  $unit->setRequired(true);
450  $unit->setSize(3);
451  $unit->setMinValue(0);
452  $unit->setValue($result->getRatingUnit());
453  $rating_type->addSubItem($unit);
454 
455  $info_text = new ilNonEditableValueGUI($this->lng->txt('additional_rating_info'));
456  $rating_type->addSubItem($info_text);
457 
458  $form->addItem($result_header);
459  $form->addItem($formula);
460  $form->addItem($range_min);
461  $form->addItem($range_max);
462  $form->addItem($suggest_range_button);
463  $form->addItem($precision);
464  $form->addItem($tolerance);
465  $form->addItem($sel_result_units);
466  $form->addItem($mc_result_units);
467  $form->addItem($result_type);
468  $form->addItem($points);
469  $form->addItem($rating_type);
470  }
471 
472  $defined_result_vars = [];
473 
474  $defined_result_res = [];
475 
476  foreach ($variables as $key => $object) {
477  $quest_vars[$key] = $key;
478  }
479 
480  foreach ($results as $key => $object) {
481  $result_vars[$key] = $key;
482  }
483 
484  foreach ($results as $tmp_result) {
488  $formula = $tmp_result->getFormula() ?? '';
489 
490  preg_match_all("/([$][v][0-9]*)/", $formula, $form_vars);
491  preg_match_all("/([$][r][0-9]*)/", $formula, $form_res);
492  foreach ($form_vars[0] as $res_var) {
493  $defined_result_vars[$res_var] = $res_var;
494  }
495 
496  foreach ($form_res[0] as $res_res) {
497  $defined_result_res[$res_res] = $res_res;
498  }
499  }
500  }
501 
502  $result_has_undefined_vars = [];
503  $question_has_unused_vars = [];
504  $result_has_undefined_res = [];
505 
506  if (is_array($quest_vars) && count($quest_vars) > 0) {
507  $result_has_undefined_vars = array_diff($defined_result_vars, $quest_vars);
508  $question_has_unused_vars = array_diff($quest_vars, $defined_result_vars);
509  }
510 
511  if (is_array($result_vars) && count($result_vars) > 0) {
512  $result_has_undefined_res = array_diff($defined_result_res, $result_vars);
513  }
514  $error_message = '';
515  $checked = false;
516  if (count($result_has_undefined_vars) > 0 || count($question_has_unused_vars) > 0) {
517  if (count($result_has_undefined_vars) > 0) {
518  $error_message .= $this->lng->txt("res_contains_undef_var") . '<br>';
519  }
520  if (count($question_has_unused_vars) > 0) {
521  $error_message .= $this->lng->txt("que_contains_unused_var") . '<br>';
522  }
523 
524  $checked = false;
525  if ($save) {
526  $this->tpl->setOnScreenMessage('failure', $error_message);
527  }
528  }
529 
530  if (is_array($result_has_undefined_res) && count($result_has_undefined_res) > 0) {
531  $error_message .= $this->lng->txt("res_contains_undef_res") . '<br>';
532  $checked = false;
533  }
534 
535  if ($save && !$checked) {
536  $this->tpl->setOnScreenMessage('failure', $error_message);
537  }
538 
539  if ($this->object->getId()) {
540  $hidden = new ilHiddenInputGUI("", "ID");
541  $hidden->setValue($this->object->getId());
542  $form->addItem($hidden);
543  }
544 
545  $this->populateTaxonomyFormSection($form);
546 
547  $form->addCommandButton('parseQuestion', $this->lng->txt("parseQuestion"));
548  $form->addCommandButton('saveReturn', $this->lng->txt("save_return"));
549  $form->addCommandButton('save', $this->lng->txt("save"));
550 
551  $errors = $checked;
552 
553  if ($save) {
554  $found_vars = [];
555  $found_results = [];
556  foreach ($this->request->getParsedBody() as $key => $value) {
557  if (preg_match("/^unit_(\\\$v\d+)$/", $key, $matches)) {
558  array_push($found_vars, $matches[1]);
559  }
560  if (preg_match("/^unit_(\\\$r\d+)$/", $key, $matches)) {
561  array_push($found_results, $matches[1]);
562  }
563  }
564 
565 
566  foreach ($this->request->getParsedBody() as $key => $value) {
567  $item = $form->getItemByPostVar($key);
568  if ($item !== null) {
569  switch (get_class($item)) {
570  case 'ilDurationInputGUI':
571  $item->setHours($value['hh']);
572  $item->setMinutes($value['mm']);
573  $item->setSeconds($value['ss']);
574  break;
575  default:
576  $item->setValue($value);
577  }
578  }
579  }
580 
581  $check = array_merge($found_vars, $found_results);
582  foreach ((array) $form->getItems() as $item) {
583  $postvar = $item->getPostVar();
584  if (preg_match("/_\\\$[r|v]\d+/", $postvar, $matches)) {
585  $k = substr(array_shift($matches), 1);
586  if (!in_array($k, $check)) {
587  $form->removeItemByPostVar($postvar);
588  }
589  }
590  }
591  $variables = array_filter($variables, fn($k, $v) => in_array($v, $check), ARRAY_FILTER_USE_BOTH);
592  $results = array_filter($results, fn($k, $v) => in_array($k, $check), ARRAY_FILTER_USE_BOTH);
593 
594  $errors = !$form->checkInput();
595 
596  $custom_errors = false;
597  if (count($variables)) {
598  foreach ($variables as $variable) {
602  $min_range = $form->getItemByPostVar('range_min_' . $variable->getVariable());
603  $max_range = $form->getItemByPostVar('range_max_' . $variable->getVariable());
604  if ($min_range->getValue() > $max_range->getValue()) {
605  $min_range->setAlert($this->lng->txt('err_range'));
606  $max_range->setAlert($this->lng->txt('err_range'));
607  $custom_errors = true;
608  }
609  $intPrecision = $form->getItemByPostVar('intprecision_' . $variable->getVariable());
610  $decimal_spots = $form->getItemByPostVar('precision_' . $variable->getVariable());
611  if ($decimal_spots->getValue() == 0
612  && $min_range->getValue() !== null
613  && $max_range->getValue() !== null
614  && !$variable->isIntPrecisionValid(
615  $intPrecision->getValue(),
616  $min_range->getValue(),
617  $max_range->getValue()
618  )
619  ) {
620  $intPrecision->setAlert($this->lng->txt('err_division'));
621  $custom_errors = true;
622  }
623  }
624  }
625 
626  if (count($results)) {
627  foreach ($results as $result) {
631  $min_range = $form->getItemByPostVar('range_min_' . $result->getResult());
632  $max_range = $form->getItemByPostVar('range_max_' . $result->getResult());
633  if ($min_range->getValue() > $max_range->getValue()) {
634  $min_range->setAlert($this->lng->txt('err_range'));
635  $max_range->setAlert($this->lng->txt('err_range'));
636  $custom_errors = true;
637  }
638 
639 
640  $formula = $form->getItemByPostVar('formula_' . $result->getResult());
641  if (strpos($formula->getValue(), $result->getResult()) !== false) {
642  $formula->setAlert($this->lng->txt('errRecursionInResult'));
643  $custom_errors = true;
644  }
645 
646  $result_unit = $form->getItemByPostVar('unit_' . $result->getResult());
647  $rating_advanced = $form->getItemByPostVar('rating_advanced_' . $result->getResult());
648  if (((int) $result_unit->getValue() <= 0) && $rating_advanced->getChecked()) {
649  unset($_POST['rating_advanced_' . $result->getResult()]);
650  $rating_advanced->setDisabled(true);
651  $rating_advanced->setChecked(false);
652  $rating_advanced->setAlert($this->lng->txt('err_rating_advanced_not_allowed'));
653  $custom_errors = true;
654  } elseif ($rating_advanced->getChecked()) {
655  $rating_sign = $form->getItemByPostVar('rating_sign_' . $result->getResult());
656  $rating_value = $form->getItemByPostVar('rating_value_' . $result->getResult());
657  $rating_unit = $form->getItemByPostVar('rating_unit_' . $result->getResult());
658 
659  $percentage = $rating_sign->getValue() + $rating_value->getValue() + $rating_unit->getValue();
660  if ($percentage != 100) {
661  $rating_advanced->setAlert($this->lng->txt('err_wrong_rating_advanced'));
662  $custom_errors = true;
663  }
664  }
665 
666  preg_match_all("/([$][v][0-9]*)/", $formula->getValue(), $form_vars);
667  $result_has_undefined_vars = array_diff($form_vars[0], $found_vars);
668  if (count($result_has_undefined_vars)) {
669  $errors = true;
670  $this->tpl->setOnScreenMessage('info', $this->lng->txt('res_contains_undef_var'));
671  }
672  }
673  }
674 
675  if ($custom_errors && !$errors) {
676  $errors = true;
677  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('form_input_not_valid'));
678  }
679  foreach ($this->request->getParsedBody() as $key => $value) {
680  $item = $form->getItemByPostVar($key);
681  if ($item !== null) {
682  switch (get_class($item)) {
683  case 'ilDurationInputGUI':
684  $item->setHours($value['hh']);
685  $item->setMinutes($value['mm']);
686  $item->setSeconds($value['ss']);
687  break;
688  default:
689  $item->setValue($value);
690  }
691  }
692  } // again, because checkInput now performs the whole stripSlashes handling and we need this if we don't want to have duplication of backslashes
693  if ($errors) {
694  $checkonly = false;
695  }
696  }
697 
698  if (!$checkonly) {
699  $this->tpl->setVariable('QUESTION_DATA', $form->getHTML());
700  }
701  return $errors;
702  }
703 
704  private function hasResultUnit($result, $unit_id, $resultunits): bool
705  {
706  if (array_key_exists($result->getResult(), $resultunits)) {
707  if (array_key_exists($unit_id, $resultunits[$result->getResult()])) {
708  return true;
709  }
710  }
711  return false;
712  }
713 
721  private function canUseAdvancedRating($result): bool
722  {
723  $resultunit = $result->getUnit();
724 
725  /*
726  * if there is a result-unit (unit selectbox) selected it is possible to use advanced rating
727  * if there is no result-unit selected it is NOT possible to use advanced rating, because there is no
728  * definition if the result-value or the unit-value should be the correct solution!!
729  *
730  */
731  if (is_object($resultunit)) {
732  return true;
733  } else {
734  return false;
735  }
736  }
737 
738  public function parseQuestion(): void
739  {
740  $this->writePostData();
741  $this->addSaveOnEnterOnLoadCode();
742  $this->editQuestion();
743  }
744 
748  public function checkInput(): bool
749  {
750  if ((!$_POST["title"]) or (!$_POST["author"]) or (!$_POST["question"])) {
751  $this->addErrorMessage($this->lng->txt("fill_out_all_required_fields"));
752  return false;
753  }
754 
755 
756  return true;
757  }
758 
771  public function getSolutionOutput(
772  $active_id,
773  $pass = null,
774  $graphicalOutput = false,
775  $result_output = false,
776  $show_question_only = true,
777  $show_feedback = false,
778  $show_correct_solution = false,
779  $show_manual_scoring = false,
780  $show_question_text = true
781  ): string {
782 
783  $user_solution = [];
784  if ($pass !== null) {
785  $user_solution = $this->object->getVariableSolutionValuesForPass($active_id, $pass);
786  } else {
787  $user_solution = array_reduce(
788  $this->object->fetchAllVariables($this->object->getQuestion()),
789  static function (array $c, assFormulaQuestionVariable $v): array {
790  $c[$v->getVariable()] = $v->getVariable();
791  return $c;
792  },
793  []
794  );
795  }
796 
797  if (($active_id > 0) && (!$show_correct_solution)) {
798  $user_solution["active_id"] = $active_id;
799  $user_solution["pass"] = $pass;
800  $solutions = $this->object->getSolutionValues($active_id, $pass);
801  foreach ($solutions as $idx => $solution_value) {
802  if (preg_match("/^(\\\$v\\d+)$/", $solution_value["value1"], $matches)) {
803  $user_solution[$matches[1]] = $solution_value["value2"];
804  } elseif (preg_match("/^(\\\$r\\d+)$/", $solution_value["value1"], $matches)) {
805  if (!array_key_exists($matches[1], $user_solution)) {
806  $user_solution[$matches[1]] = array();
807  }
808  $user_solution[$matches[1]]["value"] = $solution_value["value2"];
809  } elseif (preg_match("/^(\\\$r\\d+)_unit$/", $solution_value["value1"], $matches)) {
810  if (!array_key_exists($matches[1], $user_solution)) {
811  $user_solution[$matches[1]] = array();
812  }
813  $user_solution[$matches[1]]["unit"] = $solution_value["value2"];
814  }
815  }
816  } elseif ($active_id) {
817  $user_solution = $this->object->getBestSolution($this->object->getSolutionValues($active_id, $pass));
818  } elseif (is_object($this->getPreviewSession())) {
819  $solution_values = [];
820 
821  $participantsSolution = $this->getPreviewSession()->getParticipantsSolution();
822  if (is_array($participantsSolution)) {
823  foreach ($participantsSolution as $val1 => $val2) {
824  $solution_values[] = array('value1' => $val1, 'value2' => $val2);
825  }
826  }
827 
828  $user_solution = $this->object->getBestSolution($solution_values);
829  }
830 
831  return $this->renderSolutionOutput(
832  $user_solution,
833  $active_id,
834  $pass,
835  $graphicalOutput,
836  $result_output,
837  $show_question_only,
838  $show_feedback,
839  $show_correct_solution,
840  $show_manual_scoring,
841  $show_question_text,
842  false,
843  false
844  );
845  }
846 
847  public function renderSolutionOutput(
848  mixed $user_solutions,
849  int $active_id,
850  ?int $pass,
851  bool $graphical_output = false,
852  bool $result_output = false,
853  bool $show_question_only = true,
854  bool $show_feedback = false,
855  bool $show_correct_solution = false,
856  bool $show_manual_scoring = false,
857  bool $show_question_text = true,
858  bool $show_autosave_title = false,
859  bool $show_inline_feedback = false,
860  ): ?string {
861  $template = new ilTemplate("tpl.il_as_qpl_formulaquestion_output_solution.html", true, true, 'Modules/TestQuestionPool');
862  $correctness_icons = [
863  'correct' => $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_OK),
864  'not_correct' => $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_NOT_OK)
865  ];
866  $questiontext = $this->object->substituteVariables($user_solutions, $graphical_output, true, $result_output, $correctness_icons);
867  $template->setVariable("QUESTIONTEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($questiontext, true));
868  $questionoutput = $template->get();
869  $solutiontemplate = new ilTemplate("tpl.il_as_tst_solution_output.html", true, true, "Modules/TestQuestionPool");
870  $feedback = ($show_feedback) ? $this->getGenericFeedbackOutput((int) $active_id, $pass) : "";
871  if (strlen($feedback)) {
872  $cssClass = (
873  $this->hasCorrectSolution($active_id, $pass) ?
875  );
876 
877  $solutiontemplate->setVariable("ILC_FB_CSS_CLASS", $cssClass);
878  $solutiontemplate->setVariable("FEEDBACK", ilLegacyFormElementsUtil::prepareTextareaOutput($feedback, true));
879  }
880  $solutiontemplate->setVariable("SOLUTION_OUTPUT", $questionoutput);
881 
882  $solutionoutput = $solutiontemplate->get();
883  if (!$show_question_only) {
884  // get page object output
885  $solutionoutput = $this->getILIASPage($solutionoutput);
886  }
887  return $solutionoutput;
888  }
889 
890  public function getPreview($show_question_only = false, $showInlineFeedback = false): string
891  {
892  $user_solution = [];
893 
894  if (is_object($this->getPreviewSession())) {
895  $solutions = (array) $this->getPreviewSession()->getParticipantsSolution();
896 
897  foreach ($solutions as $val1 => $val2) {
898  if (preg_match("/^(\\\$v\\d+)$/", $val1, $matches)) {
899  $user_solution[$matches[1]] = $val2;
900  } elseif (preg_match("/^(\\\$r\\d+)$/", $val1, $matches)) {
901  if (!array_key_exists($matches[1], $user_solution)) {
902  $user_solution[$matches[1]] = [];
903  }
904  $user_solution[$matches[1]]["value"] = $val2;
905  } elseif (preg_match("/^(\\\$r\\d+)_unit$/", $val1, $matches)) {
906  if (!array_key_exists($matches[1], $user_solution)) {
907  $user_solution[$matches[1]] = [];
908  }
909  $user_solution[$matches[1]]["unit"] = $val2;
910  }
911 
912  if (preg_match("/^(\\\$r\\d+)/", $val1, $matches) && !isset($user_solution[$matches[1]]["result_type"])) {
913  $user_solution[$matches[1]]["result_type"] = assFormulaQuestionResult::getResultTypeByQstId($this->object->getId(), $val1);
914  }
915  }
916  }
917 
918  if (!$this->object->hasRequiredVariableSolutionValues($user_solution)) {
919  $user_solution = $this->object->getInitialVariableSolutionValues();
920 
921  if (is_object($this->getPreviewSession())) {
922  $this->getPreviewSession()->setParticipantsSolution($user_solution);
923  }
924  }
925 
926  $template = new ilTemplate("tpl.il_as_qpl_formulaquestion_output.html", true, true, 'Modules/TestQuestionPool');
927  if (is_object($this->getPreviewSession())) {
928  $questiontext = $this->object->substituteVariables($user_solution);
929  } else {
930  $questiontext = $this->object->substituteVariables([]);
931  }
932  $template->setVariable("QUESTIONTEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($questiontext, true));
933  $questionoutput = $template->get();
934  if (!$show_question_only) {
935  // get page object output
936  $questionoutput = $this->getILIASPage($questionoutput);
937  }
938  return $questionoutput;
939  }
940 
941  // hey: prevPassSolutions - pass will be always available from now on
942  public function getTestOutput($active_id, $pass, $is_postponed = false, $use_post_solutions = false, $show_feedback = false): string
943  // hey.
944  {
945  $this->tpl->setOnScreenMessage('info', $this->lng->txt('enter_valid_values'));
946  // get the solution of the user for the active pass or from the last pass if allowed
947  $user_solution = [];
948  if ($active_id) {
949  $solutions = $this->object->getTestOutputSolutions($active_id, $pass);
950  $actualPassIndex = null;
951  if ($this->object->getTestPresentationConfig()->isSolutionInitiallyPrefilled()) {
952  $actualPassIndex = ilObjTest::_getPass($active_id);
953  }
954 
955  foreach ($solutions as $idx => $solution_value) {
956  if (preg_match("/^(\\\$v\\d+)$/", $solution_value['value1'], $matches)) {
957  if ($this->object->getTestPresentationConfig()->isSolutionInitiallyPrefilled()) {
958  $this->object->saveCurrentSolution($active_id, $actualPassIndex, $matches[1], $solution_value['value2'], true);
959  }
960 
961  $user_solution[$matches[1]] = $solution_value['value2'];
962  } elseif (preg_match("/^(\\\$r\\d+)$/", $solution_value['value1'], $matches)) {
963  if (!array_key_exists($matches[1], $user_solution)) {
964  $user_solution[$matches[1]] = [];
965  }
966  $user_solution[$matches[1]]["value"] = $solution_value['value2'];
967  } elseif (preg_match("/^(\\\$r\\d+)_unit$/", $solution_value['value1'], $matches)) {
968  if (!array_key_exists($matches[1], $user_solution)) {
969  $user_solution[$matches[1]] = [];
970  }
971  $user_solution[$matches[1]]["unit"] = $solution_value['value2'];
972  }
973  if (preg_match("/^(\\\$r\\d+)/", $solution_value['value1'], $matches) && !isset($user_solution[$matches[1]]["result_type"])) {
974  $user_solution[$matches[1]]["result_type"] = assFormulaQuestionResult::getResultTypeByQstId($this->object->getId(), $solution_value['value1']);
975  }
976  }
977  }
978 
979  $solutions = $this->object->getSolutionValues($active_id, $pass, true);
980  foreach ($solutions as $idx => $solution_value) {
981  if (preg_match("/^(\\\$v\\d+)$/", $solution_value['value1'], $matches)) {
982  $user_solution[$matches[1]] = $solution_value['value2'];
983  }
984  }
985 
986  if ($user_solution === []) {
987  $user_solution = $this->object->getVariableSolutionValuesForPass($active_id, $pass);
988  }
989 
990  // generate the question output
991  $template = new ilTemplate("tpl.il_as_qpl_formulaquestion_output.html", true, true, 'Modules/TestQuestionPool');
992 
993  $questiontext = $this->object->substituteVariables($user_solution);
994 
995  $template->setVariable("QUESTIONTEXT", ilLegacyFormElementsUtil::prepareTextareaOutput($questiontext, true));
996 
997  $questionoutput = $template->get();
998  $pageoutput = $this->outQuestionPage("", $is_postponed, $active_id, $questionoutput);
999  return $pageoutput;
1000  }
1001 
1002  public function getSpecificFeedbackOutput(array $userSolution): string
1003  {
1004  return '';
1005  }
1006 }
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,)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
hasCorrectSolution($activeId, $passIndex)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
generateCorrectnessIconsForCorrectness(int $correctness)
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
This class represents a selection list property in a property form.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
addTarget(string $a_text, string $a_link, $a_cmd="", $a_cmdClass="", string $a_frame="", bool $a_activate=false, bool $a_dir_text=false)
setInfo(string $a_info)
addBasicQuestionFormProperties(ilPropertyFormGUI $form)
suggestRange(string $suggest_range_for_result)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getPreview($show_question_only=false, $showInlineFeedback=false)
canUseAdvancedRating($result)
Check if advanced rating can be used for a result.
setOptions(array $a_options)
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.
populateTaxonomyFormSection(ilPropertyFormGUI $form)
static getResultTypeByQstId($a_qst_id, $a_result)
writePostData(bool $always=false)
{}
global $DIC
Definition: feed.php:28
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This class represents a property in a property form.
__construct(VocabulariesInterface $vocabularies)
string $key
Consumer key/client ID value.
Definition: System.php:193
getTestOutput($active_id, $pass, $is_postponed=false, $use_post_solutions=false, $show_feedback=false)
$results
$lifecycle
Basic GUI class for assessment questions.
$txt
Definition: error.php:14
setRequired(bool $a_required)
__construct($id=-1)
assFormulaQuestionGUI constructor The constructor takes possible arguments an creates an instance of ...
setQuestionSpecificTabs(ilTabsGUI $ilTabs)
getILIASPage(string $html="")
Returns the ILIAS Page around a question.
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="", $inlineFeedbackEnabled=false)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
getSpecificFeedbackOutput(array $userSolution)
addErrorMessage(string $errormessage)
$check
Definition: buildRTE.php:81
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...
hasResultUnit($result, $unit_id, $resultunits)
getGenericFeedbackOutput(int $active_id, ?int $pass)