ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.assTextSubsetGUI.php
Go to the documentation of this file.
1<?php
2
35{
37
45 public function __construct($id = -1)
46 {
48 $this->object = new assTextSubset();
49 if ($id >= 0) {
50 $this->object->loadFromDb($id);
51 }
52 }
53
57 protected function writePostData(bool $always = false): int
58 {
59 /*
60 * sk 26.09.22: This is horrific but I don't see a better way right now,
61 * without needing to check most questions for side-effects.
62 */
63 $answers = $this->request_data_collector->raw('answers');
64 $this->answers_from_post = $answers['answer'] ?? [];
65
66 if (!(!$always && $this->editQuestion(true))) {
71 return 0;
72 }
73
74 return 1;
75 }
76
77 public function editQuestion(
78 bool $checkonly = false,
79 ?bool $is_save_cmd = null
80 ): bool {
81 $save = $is_save_cmd ?? $this->isSaveCommand();
82
83 $form = new ilPropertyFormGUI();
84 $this->editForm = $form;
85
86 $form->setFormAction($this->ctrl->getFormAction($this));
87 $form->setTitle($this->outQuestionType());
88 $form->setMultipart(false);
89 $form->setTableWidth("100%");
90 $form->setId("asstextsubset");
91
95 $this->populateTaxonomyFormSection($form);
97
98 $errors = false;
99 if ($save) {
100 $form->setValuesByPost();
101 $points = $form->getItemByPostVar('points');
102 $points->setValue($this->object->getMaximumPoints());
103 $errors = !$form->checkInput();
104 $form->setValuesByPost(); // again, because checkInput now performs the whole stripSlashes handling and we need this if we don't want to have duplication of backslashes
105 if ($errors) {
106 $checkonly = false;
107 }
108 }
109
110 if (!$checkonly) {
111 $this->renderEditForm($form);
112 }
113 return $errors;
114 }
115
116 public function addanswers(): void
117 {
118 $this->setAdditionalContentEditingModeFromPost();
119 $this->writePostData(true);
120 $cmd = $this->request_data_collector->raw('cmd') ?? [];
121 $add_answers = in_array('addanswers', $cmd) && is_array($cmd['addanswers']) ? $cmd['addanswers'] : [];
122 $this->object->addAnswer('', 0, key($add_answers) + 1);
123 $this->editQuestion();
124 }
125
126 public function removeanswers(): void
127 {
128 $this->setAdditionalContentEditingModeFromPost();
129 $this->writePostData(true);
130 $cmd = $this->request_data_collector->raw('cmd') ?? [];
131 $remove_answers = in_array('removeanswers', $cmd) && is_array($cmd['removeanswers']) ? $cmd['removeanswers'] : [];
132 $this->object->deleteAnswer(key($remove_answers));
133 $this->editQuestion();
134 }
135
136 public function getSolutionOutput(
137 int $active_id,
138 ?int $pass = null,
139 bool $graphical_output = false,
140 bool $result_output = false,
141 bool $show_question_only = true,
142 bool $show_feedback = false,
143 bool $show_correct_solution = false,
144 bool $show_manual_scoring = false,
145 bool $show_question_text = true,
146 bool $show_inline_feedback = true
147 ): string {
148 // get the solution of the user for the active pass or from the last pass if allowed
149 $solutions = [];
150 if (($active_id > 0) && (!$show_correct_solution)) {
151 $solutions = $this->object->getSolutionValues($active_id, $pass);
152 } else {
153 $rank = [];
154 foreach ($this->object->answers as $answer) {
155 $points_string_for_key = (string) $answer->getPoints();
156 if ($answer->getPoints() > 0) {
157 if (!array_key_exists($points_string_for_key, $rank)) {
158 $rank[$points_string_for_key] = [];
159 }
160 array_push($rank[$points_string_for_key], $answer->getAnswertext());
161 }
162 }
163 krsort($rank, SORT_NUMERIC);
164 foreach ($rank as $index => $bestsolutions) {
165 array_push($solutions, ["value1" => join(",", $bestsolutions), "points" => $index]);
166 }
167 }
168
169 return $this->renderSolutionOutput(
170 $solutions,
171 $active_id,
172 $pass,
173 $graphical_output,
174 $result_output,
175 $show_question_only,
176 $show_feedback,
177 $show_correct_solution,
178 $show_manual_scoring,
179 $show_question_text,
180 false,
181 $show_inline_feedback,
182 );
183 }
184
185 public function renderSolutionOutput(
186 mixed $user_solutions,
187 int $active_id,
188 ?int $pass,
189 bool $graphical_output = false,
190 bool $result_output = false,
191 bool $show_question_only = true,
192 bool $show_feedback = false,
193 bool $show_correct_solution = false,
194 bool $show_manual_scoring = false,
195 bool $show_question_text = true,
196 bool $show_autosave_title = false,
197 bool $show_inline_feedback = false,
198 ): ?string {
199 $template = new ilTemplate("tpl.il_as_qpl_textsubset_output_solution.html", true, true, "components/ILIAS/TestQuestionPool");
200 $solutiontemplate = new ilTemplate("tpl.il_as_tst_solution_output.html", true, true, "components/ILIAS/TestQuestionPool");
201
202 $available_answers = $this->object->getAvailableAnswers();
203 for ($i = 0; $i < $this->object->getCorrectAnswers(); $i++) {
204 if (!array_key_exists($i, $user_solutions) || (strcmp($user_solutions[$i]["value1"], "") == 0)) {
205 } else {
206 if (($active_id > 0) && (!$show_correct_solution)) {
207 if ($graphical_output) {
208 // output of ok/not ok icons for user entered solutions
209 $index = $this->object->isAnswerCorrect($available_answers, $user_solutions[$i]["value1"]);
210 $correct = false;
211 if ($index !== false) {
212 unset($available_answers[$index]);
213 $correct = true;
214 }
215
216 $correctness_icon = $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_NOT_OK);
217 if ($correct) {
218 $correctness_icon = $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_OK);
219 }
220 $template->setCurrentBlock("icon_ok");
221 $template->setVariable("ICON_OK", $correctness_icon);
222 $template->parseCurrentBlock();
223 }
224 }
225 $template->setCurrentBlock("textsubset_row");
226 $template->setVariable(
227 'SOLUTION',
228 $this->escapeTemplatePlaceholders(
229 htmlspecialchars(
230 $user_solutions[$i]['value1'],
231 ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401,
232 null,
233 false
234 )
235 )
236 );
237 $template->setVariable("COUNTER", $i + 1);
238 if ($result_output) {
239 $points = $user_solutions[$i]["points"];
240 $resulttext = ($points == 1) ? "(%s " . $this->lng->txt("point") . ")" : "(%s " . $this->lng->txt("points") . ")";
241 $template->setVariable("RESULT_OUTPUT", sprintf($resulttext, $points));
242 }
243 $template->parseCurrentBlock();
244 }
245 }
246 if ($show_question_text == true) {
247 $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
248 }
249 $questionoutput = $template->get();
250 $feedback = ($show_feedback && !$this->isTestPresentationContext()) ? $this->getGenericFeedbackOutput((int) $active_id, $pass) : "";
251 if (strlen($feedback)) {
252 $cssClass = (
253 $this->hasCorrectSolution($active_id, $pass) ?
255 );
256
257 $solutiontemplate->setVariable("ILC_FB_CSS_CLASS", $cssClass);
258 $solutiontemplate->setVariable("FEEDBACK", ilLegacyFormElementsUtil::prepareTextareaOutput($feedback, true));
259 }
260 $solutiontemplate->setVariable("SOLUTION_OUTPUT", $questionoutput);
261
262 $solutionoutput = $solutiontemplate->get();
263 if (!$show_question_only) {
264 // get page object output
265 $solutionoutput = $this->getILIASPage($solutionoutput);
266 }
267 return $solutionoutput;
268 }
269
270 public function getPreview(
271 bool $show_question_only = false,
272 bool $show_inline_feedback = false
273 ): string {
274 $solutions = is_object($this->getPreviewSession()) ? (array) $this->getPreviewSession()->getParticipantsSolution() : [];
275 $template = new ilTemplate("tpl.il_as_qpl_textsubset_output.html", true, true, "components/ILIAS/TestQuestionPool");
276 $width = $this->object->getMaxTextboxWidth();
277 for ($i = 0; $i < $this->object->getCorrectAnswers(); $i++) {
278 $template->setCurrentBlock("textsubset_row");
279 foreach ($solutions as $idx => $solution_value) {
280 if ($idx == $i) {
281 $template->setVariable("TEXTFIELD_VALUE", " value=\""
282 . $this->escapeTemplatePlaceholders($solution_value)
283 . "\"");
284 }
285 }
286 $template->setVariable("COUNTER", $i + 1);
287 $template->setVariable("TEXTFIELD_ID", $i);
288 $template->setVariable("TEXTFIELD_SIZE", $width);
289 $template->parseCurrentBlock();
290 }
291 $template->setVariable("QUESTIONTEXT", $this->renderLatex($this->object->getQuestionForHTMLOutput()));
292 $questionoutput = $template->get();
293 if (!$show_question_only) {
294 // get page object output
295 $questionoutput = $this->getILIASPage($questionoutput);
296 }
297 return $questionoutput;
298 }
299
300 public function getTestOutput(
301 int $active_id,
302 int $pass,
303 bool $is_question_postponed = false,
304 array|bool $user_post_solutions = false,
305 bool $show_specific_inline_feedback = false
306 ): string {
307 if ($active_id) {
308 $solutions = $this->object->getUserSolutionPreferingIntermediate($active_id, $pass);
309 }
310
311 $template = new ilTemplate("tpl.il_as_qpl_textsubset_output.html", true, true, "components/ILIAS/TestQuestionPool");
312 $width = $this->object->getMaxTextboxWidth();
313 for ($i = 0; $i < $this->object->getCorrectAnswers(); $i++) {
314 $template->setCurrentBlock("textsubset_row");
315 foreach ($solutions as $idx => $solution_value) {
316 if ($idx == $i) {
317 $template->setVariable("TEXTFIELD_VALUE", " value=\""
318 . $this->escapeTemplatePlaceholders($solution_value["value1"])
319 . "\"");
320 }
321 }
322 $template->setVariable("COUNTER", $i + 1);
323 $template->setVariable("TEXTFIELD_ID", $i);
324 $template->setVariable("TEXTFIELD_SIZE", $width);
325 $template->parseCurrentBlock();
326 }
327 $template->setVariable("QUESTIONTEXT", $this->renderLatex($this->object->getQuestionForHTMLOutput()));
328 $questionoutput = $template->get();
329 $pageoutput = $this->outQuestionPage("", $is_question_postponed, $active_id, $questionoutput);
330 return $pageoutput;
331 }
332
333 public function getSpecificFeedbackOutput(array $user_solution): string
334 {
335 $output = "";
337 }
338
340 {
341 $this->object->setCorrectAnswers($this->request_data_collector->int('correctanswers'));
342 $this->object->setTextRating($this->request_data_collector->string('text_rating'));
343 }
344
346 {
347 // Delete all existing answers and create new answers from the form data
348 $this->object->flushAnswers();
349
350 $answers = $this->request_data_collector->raw('answers', 3);
351
352 foreach ($this->answers_from_post as $index => $answertext) {
353 $this->object->addAnswer(
354 assQuestion::extendedTrim($answertext),
355 $this->refinery->kindlyTo()->float()->transform($answers['points'][$index]),
356 $index
357 );
358 }
359 }
360
362 {
363 // number of requested answers
364 $correctanswers = new ilNumberInputGUI($this->lng->txt("nr_of_correct_answers"), "correctanswers");
365 $correctanswers->setMinValue(1);
366 $correctanswers->setDecimals(0);
367 $correctanswers->setSize(3);
368 $correctanswers->setValue($this->object->getCorrectAnswers());
369 $correctanswers->setRequired(true);
370 $form->addItem($correctanswers);
371
372 // maximum available points
373 $points = new ilNumberInputGUI($this->lng->txt("maximum_points"), "points");
374 $points->setMinValue(0.0);
375 $points->setMinvalueShouldBeGreater(true);
376 $points->setSize(6);
377 $points->setDisabled(true);
378 $points->allowDecimals(true);
379 $points->setValue($this->object->getMaximumPoints());
380 $points->setRequired(false);
381 $form->addItem($points);
382
383 // text rating
384 $textrating = new ilSelectInputGUI($this->lng->txt("text_rating"), "text_rating");
385 $text_options = [
386 "ci" => $this->lng->txt("cloze_textgap_case_insensitive"),
387 "cs" => $this->lng->txt("cloze_textgap_case_sensitive")
388 ];
389 if (!$this->object->getSelfAssessmentEditingMode()) {
390 $text_options["l1"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "1");
391 $text_options["l2"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "2");
392 $text_options["l3"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "3");
393 $text_options["l4"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "4");
394 $text_options["l5"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "5");
395 }
396 $textrating->setOptions($text_options);
397 $textrating->setValue($this->object->getTextRating());
398 $form->addItem($textrating);
399 return $form;
400 }
401
403 {
404 $choices = new ilAnswerWizardInputGUI($this->lng->txt("answers"), "answers");
405 $choices->setRequired(true);
406 $choices->setQuestionObject($this->object);
407 $choices->setSingleline(true);
408 $choices->setAllowMove(false);
409 $choices->setMinValue(0.0);
410 if ($this->object->getAnswerCount() == 0) {
411 $this->object->addAnswer("", 0, 0);
412 }
413 $choices->setValues(array_map(
414 function (ASS_AnswerBinaryStateImage $value) {
415 $value->setAnswerText(html_entity_decode($value->getAnswerText()));
416 return $value;
417 },
418 $this->object->getAnswers()
419 ));
420 $form->addItem($choices);
421 return $form;
422 }
423
424
435 {
436 return [];
437 }
438
449 {
450 return [];
451 }
452
453 public function getAnswersFrequency($relevantAnswers, $questionIndex): array
454 {
455 $answers = [];
456
457 foreach ($relevantAnswers as $ans) {
458 if (!isset($answers[$ans['value1']])) {
459 $answers[$ans['value1']] = [
460 'answer' => $ans['value1'], 'frequency' => 0
461 ];
462 }
463
464 $answers[$ans['value1']]['frequency']++;
465 }
466 $answers = $this->completeAddAnswerAction($answers, $questionIndex);
467 return $answers;
468 }
469
470 protected function completeAddAnswerAction($answers, $questionIndex)
471 {
472 foreach ($answers as $key => $ans) {
473 $found = false;
474
475 foreach ($this->object->getAnswers() as $item) {
476 if ($ans['answer'] !== $item->getAnswerText()) {
477 continue;
478 }
479
480 $found = true;
481 break;
482 }
483
484 if (!$found) {
485 $answers[$key]['addable'] = true;
486 }
487 }
488
489 return $answers;
490 }
491
493 {
494 $choices = new ilAssAnswerCorrectionsInputGUI($this->lng->txt("answers"), "answers");
495 $choices->setRequired(true);
496 $choices->setQuestionObject($this->object);
497 $choices->setValues($this->object->getAnswers());
498 $form->addItem($choices);
499 }
500
505 {
506 $input = $form->getItemByPostVar('answers');
507 $values = $input->getValues();
508
509 foreach ($this->object->getAnswers() as $index => $answer) {
510 $points = (float) str_replace(',', '.', $values[$index]->getPoints());
511 $answer->setPoints($points);
512 }
513 }
514}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
Class for answers with a binary state indicator.
return true
populateTaxonomyFormSection(ilPropertyFormGUI $form)
addBasicQuestionFormProperties(ilPropertyFormGUI $form)
renderEditForm(ilPropertyFormGUI $form)
addQuestionFormCommandButtons(ilPropertyFormGUI $form)
static extendedTrim(string $value)
Trim non-printable characters from the beginning and end of a string.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
writePostData(bool $always=false)
{Evaluates a posted edit form and writes the form data in the question object.integer A positive valu...
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
populateAnswerSpecificFormPart(\ilPropertyFormGUI $form)
getTestOutput(int $active_id, int $pass, bool $is_question_postponed=false, array|bool $user_post_solutions=false, bool $show_specific_inline_feedback=false)
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
__construct($id=-1)
assTextSubsetGUI constructor
completeAddAnswerAction($answers, $questionIndex)
editQuestion(bool $checkonly=false, ?bool $is_save_cmd=null)
getSpecificFeedbackOutput(array $user_solution)
Returns the answer specific feedback for the question.
getAfterParticipationSuppressionAnswerPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
writeAnswerSpecificPostData(ilPropertyFormGUI $form)
Extracts the answer specific values from the request and applies them to the data object.
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)
populateQuestionSpecificFormPart(\ilPropertyFormGUI $form)
writeQuestionSpecificPostData(ilPropertyFormGUI $form)
Extracts the question specific values from the request and applies them to the data object.
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,)
getPreview(bool $show_question_only=false, bool $show_inline_feedback=false)
getAnswersFrequency($relevantAnswers, $questionIndex)
getAfterParticipationSuppressionQuestionPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
Class for TextSubset questions.
This class represents a single choice wizard property 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 number property in a property form.
This class represents a property form user interface.
getItemByPostVar(string $a_post_var)
This class represents a selection list property in a property form.
special template class to simplify handling of ITX/PEAR
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...
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
if(!file_exists('../ilias.ini.php'))