ILIAS  trunk Revision v12.0_alpha-1329-g1094ddb0c33
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(
282 "TEXTFIELD_VALUE",
283 " value=\"" . htmlspecialchars(
284 $this->escapeTemplatePlaceholders($solution_value["value1"]),
285 ENT_QUOTES | ENT_SUBSTITUTE,
286 'utf-8'
287 ) . "\""
288 );
289 }
290 }
291 $template->setVariable("COUNTER", $i + 1);
292 $template->setVariable("TEXTFIELD_ID", $i);
293 $template->setVariable("TEXTFIELD_SIZE", $width);
294 $template->parseCurrentBlock();
295 }
296 $template->setVariable("QUESTIONTEXT", $this->renderLatex($this->object->getQuestionForHTMLOutput()));
297 $questionoutput = $template->get();
298 if (!$show_question_only) {
299 // get page object output
300 $questionoutput = $this->getILIASPage($questionoutput);
301 }
302 return $questionoutput;
303 }
304
305 public function getTestOutput(
306 int $active_id,
307 int $pass,
308 bool $is_question_postponed = false,
309 array|bool $user_post_solutions = false,
310 bool $show_specific_inline_feedback = false
311 ): string {
312 if ($active_id) {
313 $solutions = $this->object->getUserSolutionPreferingIntermediate($active_id, $pass);
314 }
315
316 $template = new ilTemplate("tpl.il_as_qpl_textsubset_output.html", true, true, "components/ILIAS/TestQuestionPool");
317 $width = $this->object->getMaxTextboxWidth();
318 for ($i = 0; $i < $this->object->getCorrectAnswers(); $i++) {
319 $template->setCurrentBlock("textsubset_row");
320 foreach ($solutions as $idx => $solution_value) {
321 if ($idx == $i) {
322 $template->setVariable(
323 "TEXTFIELD_VALUE",
324 " value=\"" . htmlspecialchars(
325 $this->escapeTemplatePlaceholders($solution_value["value1"]),
326 ENT_QUOTES | ENT_SUBSTITUTE,
327 'utf-8'
328 ) . "\""
329 );
330 }
331 }
332 $template->setVariable("COUNTER", $i + 1);
333 $template->setVariable("TEXTFIELD_ID", $i);
334 $template->setVariable("TEXTFIELD_SIZE", $width);
335 $template->parseCurrentBlock();
336 }
337 $template->setVariable("QUESTIONTEXT", $this->renderLatex($this->object->getQuestionForHTMLOutput()));
338 $questionoutput = $template->get();
339 $pageoutput = $this->outQuestionPage("", $is_question_postponed, $active_id, $questionoutput);
340 return $pageoutput;
341 }
342
343 public function getSpecificFeedbackOutput(array $user_solution): string
344 {
345 $output = "";
347 }
348
350 {
351 $this->object->setCorrectAnswers($this->request_data_collector->int('correctanswers'));
352 $this->object->setTextRating($this->request_data_collector->string('text_rating'));
353 }
354
356 {
357 // Delete all existing answers and create new answers from the form data
358 $this->object->flushAnswers();
359
360 $answers = $this->request_data_collector->raw('answers', 3);
361
362 foreach ($this->answers_from_post as $index => $answertext) {
363 $this->object->addAnswer(
364 assQuestion::extendedTrim($answertext),
365 $this->refinery->kindlyTo()->float()->transform($answers['points'][$index]),
366 $index
367 );
368 }
369 }
370
372 {
373 // number of requested answers
374 $correctanswers = new ilNumberInputGUI($this->lng->txt("nr_of_correct_answers"), "correctanswers");
375 $correctanswers->setMinValue(1);
376 $correctanswers->setDecimals(0);
377 $correctanswers->setSize(3);
378 $correctanswers->setValue($this->object->getCorrectAnswers());
379 $correctanswers->setRequired(true);
380 $form->addItem($correctanswers);
381
382 // maximum available points
383 $points = new ilNumberInputGUI($this->lng->txt("maximum_points"), "points");
384 $points->setMinValue(0.0);
385 $points->setMinvalueShouldBeGreater(true);
386 $points->setSize(6);
387 $points->setDisabled(true);
388 $points->allowDecimals(true);
389 $points->setValue($this->object->getMaximumPoints());
390 $points->setRequired(false);
391 $form->addItem($points);
392
393 // text rating
394 $textrating = new ilSelectInputGUI($this->lng->txt("text_rating"), "text_rating");
395 $text_options = [
396 "ci" => $this->lng->txt("cloze_textgap_case_insensitive"),
397 "cs" => $this->lng->txt("cloze_textgap_case_sensitive")
398 ];
399 if (!$this->object->getSelfAssessmentEditingMode()) {
400 $text_options["l1"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "1");
401 $text_options["l2"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "2");
402 $text_options["l3"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "3");
403 $text_options["l4"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "4");
404 $text_options["l5"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "5");
405 }
406 $textrating->setOptions($text_options);
407 $textrating->setValue($this->object->getTextRating());
408 $form->addItem($textrating);
409 return $form;
410 }
411
413 {
414 $choices = new ilAnswerWizardInputGUI($this->lng->txt("answers"), "answers");
415 $choices->setRequired(true);
416 $choices->setQuestionObject($this->object);
417 $choices->setSingleline(true);
418 $choices->setAllowMove(false);
419 $choices->setMinValue(0.0);
420 if ($this->object->getAnswerCount() == 0) {
421 $this->object->addAnswer("", 0, 0);
422 }
423 $choices->setValues(array_map(
424 function (ASS_AnswerBinaryStateImage $value) {
425 $value->setAnswerText(html_entity_decode($value->getAnswerText()));
426 return $value;
427 },
428 $this->object->getAnswers()
429 ));
430 $form->addItem($choices);
431 return $form;
432 }
433
434
445 {
446 return [];
447 }
448
459 {
460 return [];
461 }
462
463 public function getAnswersFrequency($relevantAnswers, $questionIndex): array
464 {
465 $answers = [];
466
467 foreach ($relevantAnswers as $ans) {
468 if (!isset($answers[$ans['value1']])) {
469 $answers[$ans['value1']] = [
470 'answer' => $ans['value1'], 'frequency' => 0
471 ];
472 }
473
474 $answers[$ans['value1']]['frequency']++;
475 }
476 $answers = $this->completeAddAnswerAction($answers, $questionIndex);
477 return $answers;
478 }
479
480 protected function completeAddAnswerAction($answers, $questionIndex)
481 {
482 foreach ($answers as $key => $ans) {
483 $found = false;
484
485 foreach ($this->object->getAnswers() as $item) {
486 if ($ans['answer'] !== $item->getAnswerText()) {
487 continue;
488 }
489
490 $found = true;
491 break;
492 }
493
494 if (!$found) {
495 $answers[$key]['addable'] = true;
496 }
497 }
498
499 return $answers;
500 }
501
503 {
504 $choices = new ilAssAnswerCorrectionsInputGUI($this->lng->txt("answers"), "answers");
505 $choices->setRequired(true);
506 $choices->setQuestionObject($this->object);
507 $choices->setValues($this->object->getAnswers());
508 $form->addItem($choices);
509 }
510
515 {
516 $input = $form->getItemByPostVar('answers');
517 $values = $input->getValues();
518
519 foreach ($this->object->getAnswers() as $index => $answer) {
520 $points = (float) str_replace(',', '.', $values[$index]->getPoints());
521 $answer->setPoints($points);
522 }
523 }
524}
$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'))