ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.assFormulaQuestionGUI.php
Go to the documentation of this file.
1<?php
2
19use ILIAS\UI\Factory as UIFactory;
20use ILIAS\UI\Renderer as UIRenderer;
21
32{
33 protected const HAS_SPECIAL_QUESTION_COMMANDS = true;
34
35 private UIFactory $ui_factory;
36 private UIRenderer $ui_renderer;
37
44 public function __construct($id = -1)
45 {
47 global $DIC;
48 $this->ui_factory = $DIC->ui()->factory();
49 $this->ui_renderer = $DIC->ui()->renderer();
50
51 $this->object = new assFormulaQuestion();
52 if ($id >= 0) {
53 $this->object->loadFromDb($id);
54 }
55 }
56
57 protected function setQuestionSpecificTabs(ilTabsGUI $ilTabs): void
58 {
59 $this->ctrl->setParameterByClass(ilLocalUnitConfigurationGUI::class, 'q_id', $this->object->getId());
60 $ilTabs->addTarget('units', $this->ctrl->getLinkTargetByClass(ilLocalUnitConfigurationGUI::class, ''), '', 'illocalunitconfigurationgui');
61 }
62
63 public function suggestRange(): void
64 {
66 $suggest_range_for_result = $this->request_data_collector->string('suggest_range_for');
67 if ($this->writePostData()) {
68 $this->tpl->setOnScreenMessage('info', $this->getErrorMessage());
69 }
71 $this->editQuestion(false, null, $suggest_range_for_result);
72 }
73
77 protected function writePostData(bool $always = false): int
78 {
79 $hasErrors = (!$always) ? $this->editQuestion(true) : false;
80 $checked = true;
81 if (!$hasErrors) {
82 $this->object->setTitle($this->request_data_collector->string('title'));
83 $this->object->setAuthor($this->request_data_collector->string('author'));
84 $this->object->setComment($this->request_data_collector->string('comment'));
85 $this->object->setQuestion($this->request_data_collector->string('question'));
86
87 $this->object->parseQuestionText();
88 $found_vars = [];
89 $found_results = [];
90
91 foreach ($this->request_data_collector->getParsedBody() as $key => $value) {
92 if (preg_match("/^unit_(\\\$v\d+)$/", $key, $matches)) {
93 array_push($found_vars, $matches[1]);
94 }
95 if (preg_match("/^unit_(\\\$r\d+)$/", $key, $matches)) {
96 array_push($found_results, $matches[1]);
97 }
98 }
99
100 try {
101 $lifecycle = ilAssQuestionLifecycle::getInstance($this->request_data_collector->string('lifecycle'));
102 $this->object->setLifecycle($lifecycle);
104 }
105
106 if (!$this->object->checkForDuplicateResults()) {
107 $this->addErrorMessage($this->lng->txt("err_duplicate_results"));
108 $checked = false;
109 }
110
111 foreach ($found_vars as $variable) {
112 if ($this->object->getVariable($variable) != null) {
113 $unit = $this->request_data_collector->int("unit_{$variable}");
114 $varObj = new assFormulaQuestionVariable(
115 $variable,
116 $this->request_data_collector->float("range_min_{$variable}") ?? 0.0,
117 $this->request_data_collector->float("range_max_{$variable}") ?? 0.0,
118 $unit !== null ? $this->object->getUnitrepository()->getUnit(
119 $unit
120 ) : null,
121 $this->request_data_collector->float("precision_{$variable}"),
122 $this->request_data_collector->float("intprecision_{$variable}")
123 );
124 $this->object->addVariable($varObj);
125 }
126 }
127
128 $tmp_form_vars = [];
129 $tmp_quest_vars = [];
130 foreach ($found_results as $result) {
131 $tmp_res_match = preg_match_all(
132 '/([$][v][0-9]*)/',
133 $this->request_data_collector->string("formula_{$result}"),
134 $form_vars
135 );
136 $tmp_form_vars = array_merge($tmp_form_vars, $form_vars[0]);
137
138 $tmp_que_match = preg_match_all(
139 '/([$][v][0-9]*)/',
140 $this->request_data_collector->string('question'),
141 $quest_vars
142 );
143 $tmp_quest_vars = array_merge($tmp_quest_vars, $quest_vars[0]);
144 }
145 $result_has_undefined_vars = array_diff($tmp_form_vars, $found_vars);
146 $question_has_unused_vars = array_diff($tmp_quest_vars, $tmp_form_vars);
147
148 if ($result_has_undefined_vars !== [] || $question_has_unused_vars !== []) {
149 $error_message = '';
150 if (count($result_has_undefined_vars) > 0) {
151 $error_message .= $this->lng->txt("res_contains_undef_var") . '<br>';
152 }
153 if (count($question_has_unused_vars) > 0) {
154 $error_message .= $this->lng->txt("que_contains_unused_var");
155 }
156 $checked = false;
157 if ($this->isSaveCommand()) {
158 $this->tpl->setOnScreenMessage('failure', $error_message);
159 }
160 }
161 foreach ($found_results as $result) {
162 if ($this->object->getResult($result) != null) {
163 $unit = $this->request_data_collector->int("unit_{$result}");
164 $resObj = new assFormulaQuestionResult(
165 $result,
166 $this->request_data_collector->float("range_min_{$result}") ?? 0.0,
167 $this->request_data_collector->float("range_max_{$result}") ?? 0.0,
168 $this->request_data_collector->float("tolerance_{$result}") ?? 0.0,
169 $unit !== null ? $this->object->getUnitrepository()->getUnit(
170 $unit
171 ) : null,
172 $this->request_data_collector->string("formula_{$result}"),
173 $this->request_data_collector->float("points_{$result}"),
174 $this->request_data_collector->float("precision_{$result}"),
175 $this->request_data_collector->int("rating_advanced_{$result}") !== 1,
176 $this->request_data_collector->int("rating_advanced_{$result}") === 1 ? $this->request_data_collector->float("rating_sign_{$result}") : null,
177 $this->request_data_collector->int("rating_advanced_{$result}") === 1 ? $this->request_data_collector->float("rating_value_{$result}") : null,
178 $this->request_data_collector->int("rating_advanced_{$result}") === 1 ? $this->request_data_collector->float("rating_unit_{$result}") : null,
179 $this->request_data_collector->int("result_type_{$result}")
180 );
181 $this->object->addResult($resObj);
182 $available_units = $this->request_data_collector->intArray("units_{$result}");
183 if (!empty($available_units)) {
184 $this->object->addResultUnits($resObj, $available_units);
185 }
186 }
187 }
188 if ($checked === false) {
189 $this->editQuestion();
190 return 1;
191 } else {
194 return 0;
195 }
196 } else {
197 return 1;
198 }
199 }
200
201 public function editQuestion(
202 bool $checkonly = false,
203 ?bool $is_save_cmd = null,
204 ?string $suggest_range_for_result = null
205 ): bool {
206 $save = $is_save_cmd ?? $this->isSaveCommand();
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 ($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([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 ($results !== []) {
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) {
321 $result_header = new ilFormSectionHeaderGUI();
322 $result_header->setTitle(sprintf($this->lng->txt('result_x'), $result->getResult()));
323
324 $formula = new ilTextInputGUI($this->lng->txt('formula'), 'formula_' . $result->getResult());
325 $formula->setInfo($this->lng->txt('fq_formula_desc'));
326 $formula->setRequired(true);
327 $formula->setSize(50);
328 $formula->setValue($result->getFormula());
329 $formula->setSuffix(' = ' . $result->getResult());
330
331 if (
332 $suggest_range_for_result !== null &&
333 $suggest_range_for_result === $result->getResult() &&
334 strlen($result->substituteFormula($variables, $results))
335 ) {
336 $result->suggestRange($variables, $results);
337 }
338
339 $range_min = new ilNumberInputGUI($this->lng->txt('range_min'), 'range_min_' . $result->getResult());
340 $range_min->allowDecimals(true);
341 $range_min->setSize(3);
342 $range_min->setRequired(true);
343 $range_min->setValue($result->getRangeMin());
344
345 $range_max = new ilNumberInputGUI($this->lng->txt('range_max'), 'range_max_' . $result->getResult());
346 $range_max->allowDecimals(true);
347 $range_max->setSize(3);
348 $range_max->setRequired(true);
349 $range_max->setValue($result->getRangeMax());
350
351 $matches = [];
352
353 $precision = new ilNumberInputGUI($this->lng->txt('precision'), 'precision_' . $result->getResult());
354 $precision->setRequired(true);
355 $precision->setSize(3);
356 $precision->setMinValue(0);
357 $precision->setInfo($this->lng->txt('fq_precision_info'));
358 $precision->setValue($result->getPrecision());
359
360 $tolerance = new ilNumberInputGUI($this->lng->txt('tolerance'), 'tolerance_' . $result->getResult());
361 $tolerance->setSize(3);
362 $tolerance->setMinValue(0);
363 $tolerance->setMaxValue(100);
364 $tolerance->allowDecimals(true);
365 $tolerance->setInfo($this->lng->txt('tolerance_info'));
366 $tolerance->setValue($result->getTolerance());
367
368 $suggest_range_button = new ilCustomInputGUI('', '');
369 $suggest_range_button->setHtml(
370 $this->ui_renderer->render(
371 $this->ui_factory->button()->standard(
372 $this->lng->txt('suggest_range'),
373 ''
374 )->withAdditionalOnLoadCode(
375 $this->getSuggestRangeOnLoadCode($result->getResult())
376 )
377 )
378 );
379
380 $sel_result_units = new ilSelectInputGUI($this->lng->txt('unit'), 'unit_' . $result->getResult());
381 $sel_result_units->setOptions([0 => $this->lng->txt('no_selection')] + $unit_options);
382 $sel_result_units->setInfo($this->lng->txt('result_unit_info'));
383 if (is_object($result->getUnit())) {
384 $sel_result_units->setValue($result->getUnit()->getId());
385 }
386
387 $mc_result_units = new ilMultiSelectInputGUI($this->lng->txt('result_units'), 'units_' . $result->getResult());
388 $mc_result_units->setOptions($unit_options);
389 $mc_result_units->setInfo($this->lng->txt('result_units_info'));
390 $selectedvalues = [];
391 foreach ($unit_options as $unit_id => $txt) {
392 if ($this->hasResultUnit($result, $unit_id, $result_units)) {
393 $selectedvalues[] = $unit_id;
394 }
395 }
396 $mc_result_units->setValue($selectedvalues);
397
398 $result_type = new ilRadioGroupInputGUI($this->lng->txt('result_type_selection'), 'result_type_' . $result->getResult());
399 $result_type->setRequired(true);
400
401 $no_type = new ilRadioOption($this->lng->txt('no_result_type'), '0');
402 $no_type->setInfo($this->lng->txt('fq_no_restriction_info'));
403
404 $result_dec = new ilRadioOption($this->lng->txt('result_dec'), '1');
405 $result_dec->setInfo($this->lng->txt('result_dec_info'));
406
407 $result_frac = new ilRadioOption($this->lng->txt('result_frac'), '2');
408 $result_frac->setInfo($this->lng->txt('result_frac_info'));
409
410 $result_co_frac = new ilRadioOption($this->lng->txt('result_co_frac'), '3');
411 $result_co_frac->setInfo($this->lng->txt('result_co_frac_info'));
412
413 $result_type->addOption($no_type);
414 $result_type->addOption($result_dec);
415 $result_type->addOption($result_frac);
416 $result_type->addOption($result_co_frac);
417 $result_type->setValue(strlen($result->getResultType()) ? $result->getResultType() : 0);
418
419 $points = new ilNumberInputGUI($this->lng->txt('points'), 'points_' . $result->getResult());
420 $points->allowDecimals(true);
421 $points->setRequired(true);
422 $points->setSize(3);
423 $points->setMinValue(0);
424 $points->setValue(strlen($result->getPoints()) ? $result->getPoints() : 1);
425
426 $rating_type = new ilCheckboxInputGUI($this->lng->txt('advanced_rating'), 'rating_advanced_' . $result->getResult());
427 $rating_type->setValue(1);
428 $rating_type->setInfo($this->lng->txt('advanced_rating_info'));
429
430 if (!$save) {
431 $advanced_rating = $this->canUseAdvancedRating($result);
432 if (!$advanced_rating) {
433 $rating_type->setDisabled(true);
434 $rating_type->setChecked(false);
435 } else {
436 $rating_type->setChecked(strlen($result->getRatingSimple()) && $result->getRatingSimple() ? false : true);
437 }
438 }
439
440 $sign = new ilNumberInputGUI($this->lng->txt('rating_sign'), 'rating_sign_' . $result->getResult());
441 $sign->setRequired(true);
442 $sign->setSize(3);
443 $sign->setMinValue(0);
444 $sign->setValue($result->getRatingSign());
445 $rating_type->addSubItem($sign);
446
447 $value = new ilNumberInputGUI($this->lng->txt('rating_value'), 'rating_value_' . $result->getResult());
448 $value->setRequired(true);
449 $value->setSize(3);
450 $value->setMinValue(0);
451 $value->setValue($result->getRatingValue());
452 $rating_type->addSubItem($value);
453
454 $unit = new ilNumberInputGUI($this->lng->txt('rating_unit'), 'rating_unit_' . $result->getResult());
455 $unit->setRequired(true);
456 $unit->setSize(3);
457 $unit->setMinValue(0);
458 $unit->setValue($result->getRatingUnit());
459 $rating_type->addSubItem($unit);
460
461 $info_text = new ilNonEditableValueGUI($this->lng->txt('additional_rating_info'));
462 $rating_type->addSubItem($info_text);
463
464 $form->addItem($result_header);
465 $form->addItem($formula);
466 $form->addItem($range_min);
467 $form->addItem($range_max);
468 $form->addItem($suggest_range_button);
469 $form->addItem($precision);
470 $form->addItem($tolerance);
471 $form->addItem($sel_result_units);
472 $form->addItem($mc_result_units);
473 $form->addItem($result_type);
474 $form->addItem($points);
475 $form->addItem($rating_type);
476 }
477
478 $defined_result_vars = [];
479
480 $defined_result_res = [];
481
482 foreach ($variables as $key => $object) {
483 $quest_vars[$key] = $key;
484 }
485
486 foreach ($results as $key => $object) {
487 $result_vars[$key] = $key;
488 }
489
490 foreach ($results as $tmp_result) {
494 $formula = $tmp_result->getFormula() ?? '';
495
496 preg_match_all("/([$][v][0-9]*)/", $formula, $form_vars);
497 preg_match_all("/([$][r][0-9]*)/", $formula, $form_res);
498 foreach ($form_vars[0] as $res_var) {
499 $defined_result_vars[$res_var] = $res_var;
500 }
501
502 foreach ($form_res[0] as $res_res) {
503 $defined_result_res[$res_res] = $res_res;
504 }
505 }
506 }
507
508 $result_has_undefined_vars = [];
509 $question_has_unused_vars = [];
510 $result_has_undefined_res = [];
511
512 if (is_array($quest_vars) && count($quest_vars) > 0) {
513 $result_has_undefined_vars = array_diff($defined_result_vars, $quest_vars);
514 $question_has_unused_vars = array_diff($quest_vars, $defined_result_vars);
515 }
516
517 if (is_array($result_vars) && count($result_vars) > 0) {
518 $result_has_undefined_res = array_diff($defined_result_res, $result_vars);
519 }
520 $error_message = '';
521 $checked = true;
522 if ($result_has_undefined_vars !== [] || $question_has_unused_vars !== []) {
523 if (count($result_has_undefined_vars) > 0) {
524 $error_message .= $this->lng->txt("res_contains_undef_var") . '<br>';
525 }
526 if (count($question_has_unused_vars) > 0) {
527 $error_message .= $this->lng->txt("que_contains_unused_var") . '<br>';
528 }
529
530 $checked = false;
531 if ($save) {
532 $this->tpl->setOnScreenMessage('failure', $error_message);
533 }
534 }
535
536 if (is_array($result_has_undefined_res) && count($result_has_undefined_res) > 0) {
537 $error_message .= $this->lng->txt("res_contains_undef_res") . '<br>';
538 $checked = false;
539 }
540
541 if ($save && !$checked) {
542 $this->tpl->setOnScreenMessage('failure', $error_message);
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_data_collector->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_data_collector->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 ($variables !== []) {
598 foreach ($variables as $variable) {
599 $min_range = $form->getItemByPostVar('range_min_' . $variable->getVariable());
600 $max_range = $form->getItemByPostVar('range_max_' . $variable->getVariable());
601 if ($min_range->getValue() > $max_range->getValue()) {
602 $min_range->setAlert($this->lng->txt('err_range'));
603 $max_range->setAlert($this->lng->txt('err_range'));
604 $custom_errors = true;
605 }
606 $intPrecision = $form->getItemByPostVar('intprecision_' . $variable->getVariable());
607 $decimal_spots = $form->getItemByPostVar('precision_' . $variable->getVariable());
608 if ($decimal_spots->getValue() == 0
609 && $min_range->getValue() !== null
610 && $max_range->getValue() !== null
611 && !$variable->isIntPrecisionValid(
612 $intPrecision->getValue(),
613 $min_range->getValue(),
614 $max_range->getValue()
615 )
616 ) {
617 $intPrecision->setAlert($this->lng->txt('err_division'));
618 $custom_errors = true;
619 }
620 }
621 }
622
623 if ($results !== []) {
624 foreach ($results as $result) {
628 $min_range = $form->getItemByPostVar('range_min_' . $result->getResult());
629 $max_range = $form->getItemByPostVar('range_max_' . $result->getResult());
630 if ($min_range->getValue() > $max_range->getValue()) {
631 $min_range->setAlert($this->lng->txt('err_range'));
632 $max_range->setAlert($this->lng->txt('err_range'));
633 $custom_errors = true;
634 }
635
636
637 $formula = $form->getItemByPostVar('formula_' . $result->getResult());
638 if (strpos($formula->getValue(), $result->getResult()) !== false) {
639 $formula->setAlert($this->lng->txt('errRecursionInResult'));
640 $custom_errors = true;
641 }
642
643 $result_unit = $form->getItemByPostVar('unit_' . $result->getResult());
644 $rating_advanced = $form->getItemByPostVar('rating_advanced_' . $result->getResult());
645 if (((int) $result_unit->getValue() <= 0) && $rating_advanced->getChecked()) {
646 unset($_POST['rating_advanced_' . $result->getResult()]); // TODO
647 $rating_advanced->setDisabled(true);
648 $rating_advanced->setChecked(false);
649 $rating_advanced->setAlert($this->lng->txt('err_rating_advanced_not_allowed'));
650 $custom_errors = true;
651 } elseif ($rating_advanced->getChecked()) {
652 $rating_sign = $form->getItemByPostVar('rating_sign_' . $result->getResult());
653 $rating_value = $form->getItemByPostVar('rating_value_' . $result->getResult());
654 $rating_unit = $form->getItemByPostVar('rating_unit_' . $result->getResult());
655
656 $percentage = $rating_sign->getValue() + $rating_value->getValue() + $rating_unit->getValue();
657 if ($percentage != 100) {
658 $rating_advanced->setAlert($this->lng->txt('err_wrong_rating_advanced'));
659 $custom_errors = true;
660 }
661 }
662
663 preg_match_all("/([$][v][0-9]*)/", $formula->getValue(), $form_vars);
664 $result_has_undefined_vars = array_diff($form_vars[0], $found_vars);
665 if (count($result_has_undefined_vars)) {
666 $errors = true;
667 $this->tpl->setOnScreenMessage('info', $this->lng->txt('res_contains_undef_var'));
668 }
669 }
670 }
671
672 if ($custom_errors && !$errors) {
673 $errors = true;
674 $this->tpl->setOnScreenMessage('failure', $this->lng->txt('form_input_not_valid'));
675 }
676 foreach ($this->request_data_collector->getParsedBody() as $key => $value) {
677 $item = $form->getItemByPostVar($key);
678 if ($item !== null) {
679 switch (get_class($item)) {
680 case 'ilDurationInputGUI':
681 $item->setHours($value['hh']);
682 $item->setMinutes($value['mm']);
683 $item->setSeconds($value['ss']);
684 break;
685 default:
686 $item->setValue($value);
687 }
688 }
689 } // again, because checkInput now performs the whole stripSlashes handling and we need this if we don't want to have duplication of backslashes
690 if ($errors) {
691 $checkonly = false;
692 }
693 }
694
695 if (!$checkonly) {
696 $this->renderEditForm($form);
697 }
698 return $errors;
699 }
700
701 private function getSuggestRangeOnLoadCode(string $result): Closure
702 {
703 return static function ($id) use ($result): string {
704 return "document.getElementById('$id').addEventListener('click', "
705 . '(e) => {'
706 . ' e.target.setAttribute("name", "cmd[suggestRange]");'
707 . ' let input = document.createElement("input");'
708 . ' input.type = "hidden";'
709 . ' input.name = "suggest_range_for";'
710 . " input.value = '$result';"
711 . ' e.target.form.appendChild(input);'
712 . ' e.target.form.requestSubmit(e.target);'
713 . '});';
714 };
715 }
716
717 private function hasResultUnit($result, $unit_id, $resultunits): bool
718 {
719 if (array_key_exists($result->getResult(), $resultunits)) {
720 if (array_key_exists($unit_id, $resultunits[$result->getResult()])) {
721 return true;
722 }
723 }
724 return false;
725 }
726
734 private function canUseAdvancedRating($result): bool
735 {
736 $resultunit = $result->getUnit();
737
738 /*
739 * if there is a result-unit (unit selectbox) selected it is possible to use advanced rating
740 * if there is no result-unit selected it is NOT possible to use advanced rating, because there is no
741 * definition if the result-value or the unit-value should be the correct solution!!
742 *
743 */
744 if (is_object($resultunit)) {
745 return true;
746 } else {
747 return false;
748 }
749 }
750
751 public function parseQuestion(): void
752 {
753 $this->setAdditionalContentEditingModeFromPost();
754 $this->writePostData();
755 $this->addSaveOnEnterOnLoadCode();
756 $this->setTestSpecificProperties();
757 $this->editQuestion();
758 }
759
763 public function checkInput(): bool
764 {
765 $title = $this->request_data_collector->string('title');
766 $author = $this->request_data_collector->string('author');
767 $question = $this->request_data_collector->string('question');
768
769 if (empty($title) || empty($author) || empty($question)) {
770 $this->addErrorMessage($this->lng->txt('fill_out_all_required_fields'));
771 return false;
772 }
773
774 return true;
775 }
776
777 public function getSolutionOutput(
778 int $active_id,
779 ?int $pass = null,
780 bool $graphical_output = false,
781 bool $result_output = false,
782 bool $show_question_only = true,
783 bool $show_feedback = false,
784 bool $show_correct_solution = false,
785 bool $show_manual_scoring = false,
786 bool $show_question_text = true,
787 bool $show_inline_feedback = true
788 ): string {
789 $user_solution = [];
790 if ($pass !== null) {
791 $user_solution = $this->object->getVariableSolutionValuesForPass($active_id, $pass);
792 } else {
793 $user_solution = array_reduce(
794 $this->object->fetchAllVariables($this->object->getQuestion()),
795 static function (array $c, assFormulaQuestionVariable $v): array {
796 $c[$v->getVariable()] = $v->getVariable();
797 return $c;
798 },
799 []
800 );
801 }
802 if (($active_id > 0) && (!$show_correct_solution)) {
803 $user_solution['active_id'] = $active_id;
804 $user_solution['pass'] = $pass;
805 $solutions = $this->object->getSolutionValues($active_id, $pass);
806 foreach ($solutions as $solution_value) {
807 if (preg_match('/^(\$v\d+)$/', $solution_value['value1'], $matches)) {
808 $user_solution[$matches[1]] = $solution_value['value2'];
809 } elseif (preg_match('/^(\$r\d+)$/', $solution_value['value1'], $matches)) {
810 if (!array_key_exists($matches[1], $user_solution)) {
811 $user_solution[$matches[1]] = [];
812 }
813 $user_solution[$matches[1]]["value"] = $solution_value['value2'];
814 } elseif (preg_match('/^(\$r\d+)_unit$/', $solution_value['value1'], $matches)) {
815 if (!array_key_exists($matches[1], $user_solution)) {
816 $user_solution[$matches[1]] = [];
817 }
818 $user_solution[$matches[1]]['unit'] = $solution_value['value2'];
819 }
820 }
821 } elseif ($active_id !== 0) {
822 $user_solution = $this->object->getBestSolution($this->object->getSolutionValues($active_id, $pass));
823 } elseif (is_object($this->getPreviewSession())) {
824 $solutionValues = [];
825 $participants_solution = $this->getPreviewSession()->getParticipantsSolution();
826 if (is_array($participants_solution)) {
827 foreach ($participants_solution as $val1 => $val2) {
828 $solutionValues[] = ['value1' => $val1, 'value2' => $val2];
829 }
830 }
831 $user_solution = $this->object->getBestSolution($solutionValues);
832 }
833
834 return $this->renderSolutionOutput(
835 $user_solution,
836 $active_id,
837 $pass,
838 $graphical_output,
839 $result_output,
840 $show_question_only,
841 $show_feedback,
842 $show_correct_solution,
843 $show_manual_scoring,
844 $show_question_text,
845 false,
846 $show_inline_feedback
847 );
848 }
849
850 public function renderSolutionOutput(
851 mixed $user_solutions,
852 int $active_id,
853 ?int $pass,
854 bool $graphical_output = false,
855 bool $result_output = false,
856 bool $show_question_only = true,
857 bool $show_feedback = false,
858 bool $show_correct_solution = false,
859 bool $show_manual_scoring = false,
860 bool $show_question_text = true,
861 bool $show_autosave_title = false,
862 bool $show_inline_feedback = false,
863 ): ?string {
864 $template = new ilTemplate("tpl.il_as_qpl_formulaquestion_output_solution.html", true, true, 'components/ILIAS/TestQuestionPool');
865 $correctness_icons = [
866 'correct' => $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_OK),
867 'not_correct' => $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_NOT_OK)
868 ];
869 $questiontext = $this->object->substituteVariables($user_solutions, $graphical_output, true, $result_output, $correctness_icons);
870 $template->setVariable("QUESTIONTEXT", $this->renderLatex(
872 ));
873 $questionoutput = $template->get();
874 $solutiontemplate = new ilTemplate("tpl.il_as_tst_solution_output.html", true, true, "components/ILIAS/TestQuestionPool");
875 $feedback = ($show_feedback) ? $this->getGenericFeedbackOutput((int) $active_id, $pass) : "";
876 if (strlen($feedback)) {
877 $cssClass = (
878 $this->hasCorrectSolution($active_id, $pass) ?
880 );
881 $solutiontemplate->setVariable("ILC_FB_CSS_CLASS", $cssClass);
882 $solutiontemplate->setVariable("FEEDBACK", $this->renderLatex(
884 ));
885 }
886 $solutiontemplate->setVariable("SOLUTION_OUTPUT", $questionoutput);
887
888 $solutionoutput = $solutiontemplate->get();
889 if (!$show_question_only) {
890 $solutionoutput = $this->getILIASPage($solutionoutput);
891 }
892 return $solutionoutput;
893 }
894
895 public function getPreview(
896 bool $show_question_only = false,
897 bool $show_inline_feedback = false
898 ): string {
899 $user_solution = [];
900 if (is_object($this->getPreviewSession())) {
901 $solutions = $this->getPreviewSession()->getParticipantsSolution() ?? [];
902 foreach ($solutions as $val1 => $val2) {
903 if (preg_match('/^(\$v\d+)$/', $val1, $matches)) {
904 $user_solution[$matches[1]] = $val2;
905 } elseif (preg_match('/^(\$r\d+)$/', $val1, $matches)) {
906 if (!array_key_exists($matches[1], $user_solution)) {
907 $user_solution[$matches[1]] = [];
908 }
909 $user_solution[$matches[1]]['value'] = $val2;
910 } elseif (preg_match('/^(\$r\d+)_unit$/', $val1, $matches)) {
911 if (!array_key_exists($matches[1], $user_solution)) {
912 $user_solution[$matches[1]] = [];
913 }
914 $user_solution[$matches[1]]['unit'] = $val2;
915 }
916
917 if (preg_match('/^(\$r\d+)/', $val1, $matches) && !isset($user_solution[$matches[1]]['result_type'])) {
918 $user_solution[$matches[1]]['result_type'] = assFormulaQuestionResult::getResultTypeByQstId($this->object->getId(), $val1);
919 }
920 }
921 }
922
923 if (!$this->object->hasRequiredVariableSolutionValues($user_solution)) {
924 $user_solution = $this->object->getInitialVariableSolutionValues();
925
926 if (is_object($this->getPreviewSession())) {
927 $this->getPreviewSession()->setParticipantsSolution($user_solution);
928 }
929 }
930
931 $template = new ilTemplate("tpl.il_as_qpl_formulaquestion_output.html", true, true, 'components/ILIAS/TestQuestionPool');
932 if (is_object($this->getPreviewSession())) {
933 $questiontext = $this->object->substituteVariables($user_solution);
934 } else {
935 $questiontext = $this->object->substituteVariables([]);
936 }
937 $template->setVariable("QUESTIONTEXT", $this->renderLatex(
939 ));
940 $questionoutput = $template->get();
941 if (!$show_question_only) {
942 // get page object output
943 $questionoutput = $this->getILIASPage($questionoutput);
944 }
945 return $questionoutput;
946 }
947
948 public function getTestOutput(
949 int $active_id,
950 int $pass,
951 bool $is_question_postponed = false,
952 array|bool $user_post_solutions = false,
953 bool $show_specific_inline_feedback = false
954 ): string {
955 $this->tpl->setOnScreenMessage('info', $this->lng->txt('enter_valid_values'));
956 // get the solution of the user for the active pass or from the last pass if allowed
957 $user_solution = [];
958 if ($active_id) {
959 $solutions = $this->object->getTestOutputSolutions($active_id, $pass);
960 $actualPassIndex = null;
961 if ($this->object->getTestPresentationConfig()->isSolutionInitiallyPrefilled()) {
962 $actualPassIndex = ilObjTest::_getPass($active_id);
963 }
964 foreach ($solutions as $idx => $solution_value) {
965 if (preg_match('/^(\$v\d+)$/', $solution_value['value1'], $matches)) {
966 if ($this->object->getTestPresentationConfig()->isSolutionInitiallyPrefilled()) {
967 $this->object->saveCurrentSolution($active_id, $actualPassIndex, $matches[1], $solution_value['value2'], true);
968 }
969 $user_solution[$matches[1]] = $solution_value['value2'];
970 } elseif (preg_match('/^(\$r\d+)$/', $solution_value['value1'], $matches)) {
971 if (!array_key_exists($matches[1], $user_solution)) {
972 $user_solution[$matches[1]] = [];
973 }
974 $user_solution[$matches[1]]['value'] = $solution_value['value2'];
975 } elseif (preg_match('/^(\$r\d+)_unit$/', $solution_value['value1'], $matches)) {
976 if (!array_key_exists($matches[1], $user_solution)) {
977 $user_solution[$matches[1]] = [];
978 }
979 $user_solution[$matches[1]][['unit']] = $solution_value['value2'];
980 }
981 if (preg_match('/^(\$r\d+)/', $solution_value['value1'], $matches) && !isset($user_solution[$matches[1]]['result_type'])) {
982 $user_solution[$matches[1]]['result_type'] = assFormulaQuestionResult::getResultTypeByQstId($this->object->getId(), $solution_value['value1']);
983 }
984 }
985 }
986
987 // fau: testNav - take question variables always from authorized solution because they are saved with this flag, even if an authorized solution is not saved
988 $solutions = $this->object->getSolutionValues($active_id, $pass, true);
989 foreach ($solutions as $idx => $solution_value) {
990 if (preg_match('/^(\$v\d+)$/', $solution_value['value1'], $matches)) {
991 $user_solution[$matches[1]] = $solution_value['value2'];
992 }
993 }
994
995 if ($user_solution === []) {
996 $user_solution = $this->object->getVariableSolutionValuesForPass($active_id, $pass);
997 }
998
999 // generate the question output
1000 $template = new ilTemplate("tpl.il_as_qpl_formulaquestion_output.html", true, true, 'components/ILIAS/TestQuestionPool');
1001
1002 $questiontext = $this->object->substituteVariables($user_solution);
1003
1004 $template->setVariable("QUESTIONTEXT", $this->renderLatex(
1006 ));
1007
1008 $questionoutput = $template->get();
1009 $pageoutput = $this->outQuestionPage("", $is_question_postponed, $active_id, $questionoutput);
1010 return $pageoutput;
1011 }
1012
1013 public function getSpecificFeedbackOutput(array $userSolution): string
1014 {
1015 return '';
1016 }
1017}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$lifecycle
$check
Definition: buildRTE.php:81
setVariable($variable, $value='')
Sets a variable value.
Definition: IT.php:544
Builds a Color from either hex- or rgb values.
Definition: Factory.php:31
return true
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getPreview(bool $show_question_only=false, bool $show_inline_feedback=false)
canUseAdvancedRating($result)
Check if advanced rating can be used for a result.
getSpecificFeedbackOutput(array $userSolution)
Returns the answer specific feedback for the question.
hasResultUnit($result, $unit_id, $resultunits)
setQuestionSpecificTabs(ilTabsGUI $ilTabs)
getTestOutput(int $active_id, int $pass, bool $is_question_postponed=false, array|bool $user_post_solutions=false, bool $show_specific_inline_feedback=false)
getSolutionOutput(int $active_id, ?int $pass=null, 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_inline_feedback=true)
writePostData(bool $always=false)
{Evaluates a posted edit form and writes the form data in the question object.integer A positive valu...
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,)
__construct($id=-1)
assFormulaQuestionGUI constructor The constructor takes possible arguments an creates an instance of ...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getResultTypeByQstId($a_qst_id, $a_result)
Class for single choice questions assFormulaQuestion is a class for single choice questions.
populateTaxonomyFormSection(ilPropertyFormGUI $form)
editQuestion(bool $checkonly=false, ?bool $is_save_cmd=null)
addBasicQuestionFormProperties(ilPropertyFormGUI $form)
renderEditForm(ilPropertyFormGUI $form)
addErrorMessage(string $errormessage)
This class represents a checkbox property in a property form.
This class represents a custom property in a property form.
This class represents a section header in a property form.
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,...
This class represents a multi selection list property in a property form.
This class represents a non editable value in a property form.
This class represents a number property in a property form.
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
This class represents a property form user interface.
This class represents a property in a property form.
This class represents an option in a radio group.
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...
addTarget(string $a_text, string $a_link, $a_cmd="", $a_cmdClass="", string $a_frame="", bool $a_activate=false, bool $a_dir_text=false)
special template class to simplify handling of ITX/PEAR
This class represents a text property in a property form.
$c
Definition: deliver.php:25
$txt
Definition: error.php:31
An entity that renders components to a string output.
Definition: Renderer.php:31
$_POST['cmd']
Definition: lti.php:27
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$results
global $DIC
Definition: shib_login.php:26