41 $this->
object->loadFromDb(
$id);
45 $this->
lng = $DIC[
'lng'];
46 $this->ui_factory =
$DIC[
'ui.factory'];
47 $this->ui_renderer =
$DIC[
'ui.renderer'];
61 $this->editForm->setValuesByPost();
70 $custom_check = $this->
object->checkQuestionCustomPart($this->editForm);
72 $this->tpl->setOnScreenMessage(
'failure', $this->
lng->txt(
'form_input_not_valid'));
83 $hidden_text_files = $this->request_data_collector->string(
'hidden_text_files');
84 $hidden_correct_answers = $this->request_data_collector->string(
'hidden_correct_answers');
85 $long_menu_type = $this->request_data_collector->intArray(
'long_menu_type');
86 $this->
object->setLongMenuTextValue($this->request_data_collector->string(
'longmenu_text'));
88 $this->
object->setCorrectAnswers(
92 json_decode($hidden_correct_answers)
98 $this->
object->setQuestion($this->request_data_collector->string(
'question'));
99 $this->
object->setMinAutoComplete($min_auto_complete);
100 $this->
object->setIdenticalScoring($this->request_data_collector->int(
'identical_scoring'));
107 $longmenu_text = $this->request_data_collector->raw(
'longmenu_text') ??
'';
108 $hidden_text_files = $this->request_data_collector->raw(
'hidden_text_files') ??
'';
109 $answer_options_from_text = preg_split(
115 if (count($answer_options_from_text) - 1 !== count($answer_options_from_files)) {
116 $this->tpl->setOnScreenMessage(
'failure', $this->
lng->txt(
'longmenu_answeroptions_differ'));
120 $correct_answers = $this->
stripSlashesRecursive(json_decode($this->request_data_collector->raw(
'hidden_correct_answers')));
121 foreach ($correct_answers as $answer) {
122 if (!is_numeric(str_replace(
',',
'.', $answer[1]))) {
123 $this->tpl->setOnScreenMessage(
'failure', $this->
lng->txt(
'points_non_numeric_or_negative_msg'));
133 function (
int|
float|
string|array $v):
int|
float|
string|array {
137 if (is_int($v) || is_float($v)) {
149 function (
int|
float|
string|array $v):
int|
float|
string|array {
153 if (is_int($v) || is_float($v)) {
163 array $correct_answers
166 function (array $v): array {
175 bool $checkonly =
false,
176 ?
bool $is_save_cmd =
null
178 $form = $this->editForm;
179 if ($form ===
null) {
180 $form = $this->buildEditForm();
188 if ($this->
object->getId() > 0) {
189 $this->
ctrl->setParameterByClass(self::class,
'q_id', $this->
object->getId());
191 $form->setFormAction($this->
ctrl->getFormActionByClass(self::class));
192 $this->renderEditForm($form);
200 $form = $this->buildBasicEditFormObject();
201 $this->addQuestionFormCommandButtons($form);
202 $this->addBasicQuestionFormProperties($form);
203 $this->populateQuestionSpecificFormPart($form);
204 $this->populateTaxonomyFormSection($form);
212 $long_menu_text->setRequired(
true);
213 $long_menu_text->setRows(10);
214 $long_menu_text->setCols(80);
215 if (!$this->
object->getSelfAssessmentEditingMode()) {
218 $long_menu_text->setRTESupport($this->
object->getId(),
"qpl",
"assessment");
219 $long_menu_text->setUseRte(
true);
223 $long_menu_text->setUseTagsForRteOnly(
false);
226 $long_menu_text->setValue($this->
object->getLongMenuTextValue());
227 $form->
addItem($long_menu_text);
229 $tpl =
new ilTemplate(
'tpl.il_as_qpl_longmenu_question_gap_button_code.html',
true,
true,
'components/ILIAS/TestQuestionPool');
230 $tpl->setVariable(
'INSERT_GAP', $this->
lng->txt(
'insert_gap'));
231 $tpl->parseCurrentBlock();
233 $button->setHtml($tpl->get());
236 $min_auto_complete =
new ilNumberInputGUI($this->
lng->txt(
'min_auto_complete'),
'min_auto_complete');
238 $auto_complete = $this->
object->getMinAutoComplete();
239 if ($auto_complete === 0) {
242 $min_auto_complete->setDecimals(0);
243 $min_auto_complete->setValue($auto_complete);
244 $min_auto_complete->setMinValue(1);
245 $min_auto_complete->setMaxValue(99);
246 $min_auto_complete->setSize(5);
247 $form->
addItem($min_auto_complete);
249 $identical_scoring =
new ilCheckboxInputGUI($this->
lng->txt(
'identical_scoring'),
'identical_scoring');
250 $identical_scoring->setValue(1);
251 $identical_scoring->setChecked($this->
object->getIdenticalScoring());
252 $identical_scoring->setInfo($this->
lng->txt(
'identical_scoring_desc'));
253 $identical_scoring->setRequired(
false);
254 $form->
addItem($identical_scoring);
259 $form->
addItem($hidden_correct);
261 $long_menu_language = [
262 'edit' =>
'[' . $this->
lng->txt(
'edit') .
']',
263 'type' => $this->
lng->txt(
'type'),
264 'answers' => $this->
lng->txt(
'answers'),
265 'answer_options' => $this->
lng->txt(
'answer_options'),
266 'correct_answers' => $this->
lng->txt(
'correct_answers') .
':',
267 'add_answers' =>
'[' . $this->
lng->txt(
'add_answers') .
']',
268 'manual_editing' => $this->
lng->txt(
'manual_editing')
272 'list' => json_decode($this->
object->getJsonStructure()) ?? [],
274 'last_updated_element' => 0,
275 'replacement_word' =>
'',
276 'filereader_usable' =>
false,
279 $answers = $this->
object->getAnswersObject();
281 if ($this->request_data_collector->isset(
'hidden_text_files')) {
282 $question_parts[
'list'] = $this->trimArrayRecursive(
283 $this->stripSlashesRecursive(
285 $this->request_data_collector->string(
'hidden_correct_answers')
289 $answers = $this->request_data_collector->string(
'hidden_text_files');
292 $this->tpl->addJavaScript(
'assets/js/longMenuQuestionGapBuilder.js');
293 $this->tpl->addJavaScript(
'assets/js/longMenuQuestion.js');
294 $tpl =
new ilTemplate(
"tpl.il_as_qpl_longmenu_question_gap.html",
true,
true,
"components/ILIAS/TestQuestionPool");
297 $tpl->setVariable(
'SELECT_BOX', $this->
lng->txt(
'insert_gap'));
298 $tpl->setVariable(
"SELECT", $this->
lng->txt(
'answers_select'));
299 $tpl->setVariable(
"TEXT", $this->
lng->txt(
'answers_text_box'));
300 $tpl->setVariable(
"POINTS", $this->
lng->txt(
'points'));
301 $tpl->setVariable(
"INFO_TEXT_UPLOAD", $this->
lng->txt(
'info_text_upload'));
302 $tpl->setVariable(
"TXT_BROWSE", $this->
lng->txt(
'select_file'));
303 $tpl->setVariable(
'POINTS_ERROR', $this->
lng->txt(
'enter_enough_positive_points'));
304 $tpl->setVariable(
'AUTOCOMPLETE_ERROR', $this->
lng->txt(
'autocomplete_error'));
305 $tpl->setVariable(
'MISSING_VALUE', $this->
lng->txt(
'msg_input_is_required'));
306 $tpl->setVariable(
'SAVE', $this->
lng->txt(
'save'));
307 $tpl->setVariable(
'CANCEL', $this->
lng->txt(
'cancel'));
308 $tpl->setVariable(
'ADD_BUTTON', $this->ui_renderer->render(
309 $this->ui_factory->button()->shy(
'',
'')->withSymbol($this->ui_factory->symbol()->glyph()->add())
311 $tpl->setVariable(
'REMOVE_BUTTON', $this->ui_renderer->render(
312 $this->ui_factory->button()->shy(
'',
'')->withSymbol($this->ui_factory->symbol()->glyph()->remove())
315 $tag_input->setPostVar(
'taggable');
316 $tag_input->setJsSelfInit(
false);
317 $tag_input->setTypeAheadMinLength(1);
318 $tpl->setVariable(
'TAGGING_PROTOTYPE', $tag_input->render(
''));
320 $modal_id = self::DEFAULT_MODAL_ID;
321 $tpl->setVariable(
'MY_MODAL', $this->getModalHtml($modal_id));
322 $tpl->parseCurrentBlock();
323 $this->tpl->addOnLoadCode(
324 'longMenuQuestion.Init(' .
326 json_encode($long_menu_language),
327 json_encode($question_parts),
328 $answers ===
'' ?
'{}' : $answers,
329 json_encode($modal_id)
335 $button->setHtml($tpl->get());
342 $modal = $this->ui_factory->modal()->interruptive(
'',
'',
'');
343 $doc =
new DOMDocument();
344 @$doc->loadHTML($this->ui_renderer->render($modal));
345 $dialogs = $doc->getElementsByTagName(
'dialog');
346 $modal_id = $dialogs->item(0)->attributes->getNamedItem(
'id')->nodeValue ?? self::DEFAULT_MODAL_ID;
347 return $doc->saveHTML();
362 bool $graphical_output =
false,
363 bool $result_output =
false,
364 bool $show_question_only =
true,
365 bool $show_feedback =
false,
366 bool $show_correct_solution =
false,
367 bool $show_manual_scoring =
false,
368 bool $show_question_text =
true,
369 bool $show_inline_feedback =
true
371 if (($active_id > 0) && (!$show_correct_solution)) {
372 $user_solutions = $this->
object->getSolutionValues($active_id, $pass,
true);
374 $user_solutions = [];
375 foreach ($this->
object->getCorrectAnswersForQuestionSolution($this->object->getId()) as $idx => $val) {
376 $user_solutions[] = [
383 return $this->renderSolutionOutput(
391 $show_correct_solution,
392 $show_manual_scoring,
395 $show_inline_feedback
400 mixed $user_solutions,
403 bool $graphical_output =
false,
404 bool $result_output =
false,
405 bool $show_question_only =
true,
406 bool $show_feedback =
false,
407 bool $show_correct_solution =
false,
408 bool $show_manual_scoring =
false,
409 bool $show_question_text =
true,
410 bool $show_autosave_title =
false,
411 bool $show_inline_feedback =
false,
415 foreach ($user_solutions as $idx => $solution_value) {
416 $user_solution[$solution_value[
"value1"]] = $solution_value[
"value2"];
419 $template =
new ilTemplate(
"tpl.il_as_qpl_longmenu_question_output_solution.html",
true,
true,
"components/ILIAS/TestQuestionPool");
420 if ($show_question_text) {
421 $template->setVariable(
"QUESTIONTEXT", $this->renderLatex($this->
object->getQuestionForHTMLOutput()));
423 $template->setVariable(
'LONGMENU_TEXT_SOLUTION', $this->getLongMenuTextWithInputFieldsInsteadOfGaps($user_solution,
true, $graphical_output));
425 $solution_template =
new ilTemplate(
"tpl.il_as_tst_solution_output.html",
true,
true,
"components/ILIAS/TestQuestionPool");
426 $question_output = $template->get();
428 if ($show_feedback) {
429 if (!$this->isTestPresentationContext()) {
430 $fb = $this->getGenericFeedbackOutput((
int) $active_id, $pass);
431 $feedback .= strlen($fb) ? $fb :
'';
434 $fb = $this->getSpecificFeedbackOutput([]);
435 $feedback .= strlen($fb) ? $fb :
'';
437 if (strlen($feedback)) {
439 $this->hasCorrectSolution($active_id, $pass) ?
443 $solution_template->setVariable(
"ILC_FB_CSS_CLASS", $cssClass);
447 $solution_template->setVariable(
"SOLUTION_OUTPUT", $question_output);
449 $solution_output = $solution_template->get();
451 if (!$show_question_only) {
452 $solution_output = $this->getILIASPage($solution_output);
455 return $solution_output;
459 bool $show_question_only =
false,
460 bool $show_inline_feedback =
false
462 $user_solution = is_object($this->getPreviewSession()) ? (array) $this->getPreviewSession()->getParticipantsSolution() : [];
463 $user_solution = array_values($user_solution);
465 $template = $this->getTemplateForPreviewAndTest($user_solution);
467 $question_output = $template->get();
468 if (!$show_question_only) {
469 $question_output = $this->getILIASPage($question_output);
471 return $question_output;
477 bool $is_question_postponed =
false,
478 array|
bool $user_post_solutions =
false,
479 bool $show_specific_inline_feedback =
false
483 $solutions = $this->
object->getUserSolutionPreferingIntermediate($active_id, $pass);
484 foreach ($solutions as $idx => $solution_value) {
485 $user_solution[$solution_value[
'value1']] = $solution_value[
'value2'];
489 $template = $this->getTemplateForPreviewAndTest($user_solution);
491 $question_output = $template->get();
492 $page_output = $this->outQuestionPage(
'', $is_question_postponed, $active_id, $question_output);
498 $template =
new ilTemplate(
'tpl.il_as_qpl_longmenu_question_output.html',
true,
true,
'components/ILIAS/TestQuestionPool');
499 $this->tpl->addJavaScript(
'assets/js/longMenuQuestionPlayer.js');
500 $this->tpl->addOnLoadCode(
'il.test.player.longmenu.init('
501 . $this->
object->getMinAutoComplete() .
', '
502 . json_encode($this->
object->getAvailableAnswerOptions())
505 $template->setVariable(
'QUESTIONTEXT', $this->renderLatex($this->renderLatex($this->
object->getQuestionForHTMLOutput())));
506 $template->setVariable(
'LONGMENU_TEXT', $this->getLongMenuTextWithInputFieldsInsteadOfGaps($user_solution));
512 if (!$this->
object->feedbackOBJ->specificAnswerFeedbackExists()) {
516 $feedback =
'<table class="test_specific_feedback"><tbody>';
517 $gaps = $this->
object->getCorrectAnswers();
518 foreach ($gaps as $index => $answer) {
520 $caption .= $index + 1 .
': ';
522 $feedback .=
'<tr><td>';
524 $feedback .= $caption .
'</td><td>';
525 $feedback .= $this->
object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation(
531 $feedback .=
'</tbody></table>';
554 $correct_answers = $this->
object->getCorrectAnswers();
555 $answers = $this->
object->getAnswers();
556 foreach ($text_array as $key => $value) {
557 $answer_is_correct =
false;
560 if ($key <
sizeof($text_array) - 1) {
561 if (!array_key_exists($key, $correct_answers)) {
562 $this->tpl->setOnScreenMessage(
'failure', $this->
lng->txt(
'longmenu_answeroptions_differ'));
566 if (array_key_exists($key, $user_solution)) {
567 $user_value = $user_solution[$key];
568 if (in_array($user_value, $correct_answers[$key][0])) {
569 $answer_is_correct =
true;
573 $return_value .= $this->getTextGapTemplate($key, $user_value, $solution, $answer_is_correct, $graphical);
575 if (array_key_exists($key, $user_solution)) {
576 $user_value = $user_solution[$key];
577 if (in_array($user_value, $correct_answers[$key][0])) {
578 $answer_is_correct =
true;
581 $return_value .= $this->getSelectGapTemplate($key, $answers[$key], $user_value, $solution, $answer_is_correct, $graphical);
585 return $return_value;
588 private function getTextGapTemplate($key, $value, $solution, $ok =
false, $graphical =
false): string
590 $tpl =
new ilTemplate(
'tpl.il_as_qpl_longmenu_question_text_gap.html',
true,
true,
'components/ILIAS/TestQuestionPool');
592 $tpl->setVariable(
'DISABLED',
'disabled');
593 $tpl->setVariable(
'JS_IGNORE',
'_ignore');
595 $correctness_icon = $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_NOT_OK);
597 $correctness_icon = $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_OK);
599 $tpl->setVariable(
"ICON_OK", $correctness_icon);
603 $tpl->setVariable(
'SIZE',
'size="' . mb_strlen($value) .
'"');
605 $tpl->setVariable(
'VALUE', htmlentities($value));
606 $tpl->setVariable(
'KEY', $key);
611 private function getSelectGapTemplate($key, $answers, $user_value, $solution, $ok =
false, $graphical =
false): string
613 $tpl =
new ilTemplate(
"tpl.il_as_qpl_longmenu_question_select_gap.html",
true,
true,
"components/ILIAS/TestQuestionPool");
614 $tpl->setVariable(
'KEY', $key);
616 $tpl->setVariable(
'DISABLED',
'disabled');
617 $tpl->setVariable(
'JS_IGNORE',
'_ignore');
618 $tpl->setCurrentBlock(
'best_solution');
619 if ($user_value == -1) {
620 $tpl->setVariable(
"SOLUTION", $this->
lng->txt(
"please_select"));
622 $tpl->setVariable(
'SOLUTION', $user_value);
625 $correctness_icon = $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_NOT_OK);
627 $correctness_icon = $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_OK);
629 $tpl->setVariable(
"ICON_OK", $correctness_icon);
631 $tpl->parseCurrentBlock();
633 $tpl->setVariable(
"PLEASE_SELECT", $this->
lng->txt(
"please_select"));
634 foreach ($answers as $value) {
635 $tpl->setCurrentBlock(
'select_option');
636 $tpl->setVariable(
'VALUE', $value);
637 if ($value == $user_value) {
638 $tpl->setVariable(
'SELECTED',
'selected');
640 $tpl->parseCurrentBlock();
648 return array_keys($this->
object->getAnswers());
655 foreach ($relevantAnswers as $row) {
656 if ($row[
'value1'] != $questionIndex) {
660 if (!isset($answers[$row[
'value2']])) {
663 $answers[$row[
'value2']] = [
664 'answer' => $row[
'value2'],
'frequency' => 0
668 $answers[$row[
'value2']][
'frequency']++;
678 $table = parent::getAnswerFrequencyTableGUI(
687 $DIC->language()->txt(
'tst_corrections_answers_tbl_subindex'),
688 $DIC->language()->txt(
'longmenu') .
' ' . ($questionIndex + 1)
697 $correct_answers = $this->
object->getCorrectAnswers();
699 foreach ($this->
object->getAnswers() as $lm_index => $lm) {
701 'answers_all' => [0 => $lm],
702 'answers_all_count' => count($lm),
703 'answers_correct' => $correct_answers[$lm_index][0]
706 $lm_points = $correct_answers[$lm_index][1];
709 $section->setTitle($this->
lng->txt(
'longmenu') .
' ' . ($lm_index + 1));
713 $this->
lng->txt(
'answers'),
714 'longmenu_' . $lm_index
717 $lm_input->setRequired(
true);
719 $lm_input->setValues($lm_values);
724 $points_inp->setRequired(
true);
725 $points_inp->allowDecimals(
true);
726 $points_inp->setSize(4);
727 $points_inp->setMinValue(0);
728 $points_inp->setMinvalueShouldBeGreater(
false);
729 $points_inp->setValue($lm_points);
736 $correct_answers = $this->
object->getCorrectAnswers();
738 foreach ($this->
object->getAnswers() as $lm_index => $lm) {
739 $points_input = $this->
refinery->kindlyTo()->float()->transform(
740 $form->
getInput(
'points_' . $lm_index)
742 $correct_answers_input = (array) $form->
getInput(
'longmenu_' . $lm_index .
'_tags');
744 foreach ($correct_answers_input as $idx => $answer) {
745 if (in_array($answer, $lm)) {
749 unset($correct_answers_input[$idx]);
752 $correct_answers[$lm_index][0] = array_values($correct_answers_input);
753 $correct_answers[$lm_index][1] = $points_input;
756 $this->
object->setCorrectAnswers($correct_answers);
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Builds a Color from either hex- or rgb values.
writeQuestionGenericPostData()
saveTaxonomyAssignments()
const ADDITIONAL_CONTENT_EDITING_MODE_RTE
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static stripSlashesRecursive($a_data, bool $a_strip_html=true, string $a_allow="")
const CSS_CLASS_FEEDBACK_WRONG
const CSS_CLASS_FEEDBACK_CORRECT
static _getUsedHTMLTags(string $module='')
special template class to simplify handling of ITX/PEAR
This class represents a text area property in a property form.
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
An entity that renders components to a string output.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
if(!file_exists('../ilias.ini.php'))