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