ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.assTextSubsetGUI.php
Go to the documentation of this file.
1 <?php
2 
19 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
20 
37 {
39 
47  public function __construct($id = -1)
48  {
50  $this->object = new assTextSubset();
51  if ($id >= 0) {
52  $this->object->loadFromDb($id);
53  }
54  }
55 
59  protected function writePostData(bool $always = false): int
60  {
61  /*
62  * sk 26.09.22: This is horrific but I don't see a better way right now,
63  * without needing to check most questions for side-effects.
64  */
65  $this->answers_from_post = $_POST['answers']['answer'];
66  $hasErrors = (!$always) ? $this->editQuestion(true) : false;
67  if (!$hasErrors) {
71  $this->saveTaxonomyAssignments();
72  return 0;
73  }
74  return 1;
75  }
76 
80  public function editQuestion($checkonly = false): bool
81  {
82  $save = $this->isSaveCommand();
83  $this->getQuestionTemplate();
84 
85  $form = new ilPropertyFormGUI();
86  $this->editForm = $form;
87 
88  $form->setFormAction($this->ctrl->getFormAction($this));
89  $form->setTitle($this->outQuestionType());
90  $form->setMultipart(false);
91  $form->setTableWidth("100%");
92  $form->setId("asstextsubset");
93 
94  $this->addBasicQuestionFormProperties($form);
96  $this->populateAnswerSpecificFormPart($form);
97  $this->populateTaxonomyFormSection($form);
98  $this->addQuestionFormCommandButtons($form);
99 
100  $errors = false;
101  if ($save) {
102  $form->setValuesByPost();
103  $points = $form->getItemByPostVar('points');
104  $points->setValue($this->object->getMaximumPoints());
105  $errors = !$form->checkInput();
106  $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
107  if ($errors) {
108  $checkonly = false;
109  }
110  }
111 
112  if (!$checkonly) {
113  $this->tpl->setVariable("QUESTION_DATA", $form->getHTML());
114  }
115  return $errors;
116  }
117 
121  public function addanswers(): void
122  {
123  $this->writePostData(true);
124  $position = key($_POST['cmd']['addanswers']);
125  $this->object->addAnswer("", 0, $position + 1);
126  $this->editQuestion();
127  }
128 
132  public function removeanswers(): void
133  {
134  $this->writePostData(true);
135  $position = key($_POST['cmd']['removeanswers']);
136  $this->object->deleteAnswer($position);
137  $this->editQuestion();
138  }
139 
152  public function getSolutionOutput(
153  $active_id,
154  $pass = null,
155  $graphicalOutput = false,
156  $result_output = false,
157  $show_question_only = true,
158  $show_feedback = false,
159  $show_correct_solution = false,
160  $show_manual_scoring = false,
161  $show_question_text = true
162  ): string {
163  // get the solution of the user for the active pass or from the last pass if allowed
164  $solutions = [];
165  if (($active_id > 0) && (!$show_correct_solution)) {
166  $solutions = $this->object->getSolutionValues($active_id, $pass);
167  } else {
168  $rank = array();
169  foreach ($this->object->answers as $answer) {
170  $points_string_for_key = (string) $answer->getPoints();
171  if ($answer->getPoints() > 0) {
172  if (!array_key_exists($points_string_for_key, $rank)) {
173  $rank[$points_string_for_key] = array();
174  }
175  array_push($rank[$points_string_for_key], $answer->getAnswertext());
176  }
177  }
178  krsort($rank, SORT_NUMERIC);
179  foreach ($rank as $index => $bestsolutions) {
180  array_push($solutions, array("value1" => join(",", $bestsolutions), "points" => $index));
181  }
182  }
183 
184  $show_inline_feedback = false;
185  return $this->renderSolutionOutput(
186  $solutions,
187  $active_id,
188  $pass,
189  $graphicalOutput,
190  $result_output,
191  $show_question_only,
192  $show_feedback,
193  $show_correct_solution,
194  $show_manual_scoring,
195  $show_question_text,
196  false,
197  $show_inline_feedback,
198  );
199  }
200 
201  public function renderSolutionOutput(
202  mixed $user_solutions,
203  int $active_id,
204  ?int $pass,
205  bool $graphical_output = false,
206  bool $result_output = false,
207  bool $show_question_only = true,
208  bool $show_feedback = false,
209  bool $show_correct_solution = false,
210  bool $show_manual_scoring = false,
211  bool $show_question_text = true,
212  bool $show_autosave_title = false,
213  bool $show_inline_feedback = false,
214  ): ?string {
215 
216  $template = new ilTemplate("tpl.il_as_qpl_textsubset_output_solution.html", true, true, "Modules/TestQuestionPool");
217  $solutiontemplate = new ilTemplate("tpl.il_as_tst_solution_output.html", true, true, "Modules/TestQuestionPool");
218  $available_answers = &$this->object->getAvailableAnswers();
219  for ($i = 0; $i < $this->object->getCorrectAnswers(); $i++) {
220  if (!array_key_exists($i, $user_solutions) || (strcmp($user_solutions[$i]['value1'], "") == 0)) {
221  } else {
222  if (($active_id > 0) && (!$show_correct_solution)) {
223  if ($graphical_output) {
224  // output of ok/not ok icons for user entered solutions
225  $index = $this->object->isAnswerCorrect($available_answers, $user_solutions[$i]['value1']);
226  $correct = false;
227  if ($index !== false) {
228  unset($available_answers[$index]);
229  $correct = true;
230  }
231 
232  $correctness_icon = $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_NOT_OK);
233  if ($correct) {
234  $correctness_icon = $this->generateCorrectnessIconsForCorrectness(self::CORRECTNESS_OK);
235  }
236  $template->setCurrentBlock("icon_ok");
237  $template->setVariable("ICON_OK", $correctness_icon);
238  $template->parseCurrentBlock();
239  }
240  }
241  $template->setCurrentBlock("textsubset_row");
242  $template->setVariable(
243  "SOLUTION",
245  htmlspecialchars(
246  $user_solutions[$i]['value1'],
247  ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401,
248  null,
249  false
250  )
251  )
252  );
253  $template->setVariable("COUNTER", $i + 1);
254  if ($result_output) {
255  $points = $user_solutions[$i]["points"];
256  $resulttext = ($points == 1) ? "(%s " . $this->lng->txt("point") . ")" : "(%s " . $this->lng->txt("points") . ")";
257  $template->setVariable("RESULT_OUTPUT", sprintf($resulttext, $points));
258  }
259  $template->parseCurrentBlock();
260  }
261  }
262  if ($show_question_text == true) {
263  $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
264  }
265  $questionoutput = $template->get();
266  $feedback = ($show_feedback && !$this->isTestPresentationContext()) ? $this->getGenericFeedbackOutput((int) $active_id, $pass) : "";
267  if (strlen($feedback)) {
268  $cssClass = (
269  $this->hasCorrectSolution($active_id, $pass) ?
271  );
272 
273  $solutiontemplate->setVariable("ILC_FB_CSS_CLASS", $cssClass);
274  $solutiontemplate->setVariable("FEEDBACK", ilLegacyFormElementsUtil::prepareTextareaOutput($feedback, true));
275  }
276  $solutiontemplate->setVariable("SOLUTION_OUTPUT", $questionoutput);
277 
278  $solutionoutput = $solutiontemplate->get();
279  if (!$show_question_only) {
280  // get page object output
281  $solutionoutput = $this->getILIASPage($solutionoutput);
282  }
283  return $solutionoutput;
284  }
285 
286  public function getPreview($show_question_only = false, $showInlineFeedback = false): string
287  {
288  $solutions = is_object($this->getPreviewSession()) ? (array) $this->getPreviewSession()->getParticipantsSolution() : array();
289  $template = new ilTemplate("tpl.il_as_qpl_textsubset_output.html", true, true, "Modules/TestQuestionPool");
290  $width = $this->object->getMaxTextboxWidth();
291  for ($i = 0; $i < $this->object->getCorrectAnswers(); $i++) {
292  $template->setCurrentBlock("textsubset_row");
293  foreach ($solutions as $idx => $solution_value) {
294  if ($idx == $i) {
295  $template->setVariable("TEXTFIELD_VALUE", " value=\""
296  . $this->escapeTemplatePlaceholders($solution_value)
297  . "\"");
298  }
299  }
300  $template->setVariable("COUNTER", $i + 1);
301  $template->setVariable("TEXTFIELD_ID", $i);
302  $template->setVariable("TEXTFIELD_SIZE", $width);
303  $template->parseCurrentBlock();
304  }
305  $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
306  $questionoutput = $template->get();
307  if (!$show_question_only) {
308  // get page object output
309  $questionoutput = $this->getILIASPage($questionoutput);
310  }
311  return $questionoutput;
312  }
313 
314  public function getTestOutput($active_id, $pass = null, $is_postponed = false, $use_post_solutions = false, $inlineFeedback = false): string
315  {
316  // get the solution of the user for the active pass or from the last pass if allowed
317  $user_solution = "";
318  if ($active_id) {
319  $solutions = $this->object->getUserSolutionPreferingIntermediate($active_id, $pass);
320  }
321 
322  $template = new ilTemplate("tpl.il_as_qpl_textsubset_output.html", true, true, "Modules/TestQuestionPool");
323  $width = $this->object->getMaxTextboxWidth();
324  for ($i = 0; $i < $this->object->getCorrectAnswers(); $i++) {
325  $template->setCurrentBlock("textsubset_row");
326  foreach ($solutions as $idx => $solution_value) {
327  if ($idx == $i) {
328  $template->setVariable("TEXTFIELD_VALUE", " value=\""
329  . $this->escapeTemplatePlaceholders($solution_value['value1'])
330  . "\"");
331  }
332  }
333  $template->setVariable("COUNTER", $i + 1);
334  $template->setVariable("TEXTFIELD_ID", $i);
335  $template->setVariable("TEXTFIELD_SIZE", $width);
336  $template->parseCurrentBlock();
337  }
338  $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
339  $questionoutput = $template->get();
340  $pageoutput = $this->outQuestionPage("", $is_postponed, $active_id, $questionoutput);
341  return $pageoutput;
342  }
343 
344  public function getSpecificFeedbackOutput(array $userSolution): string
345  {
346  $output = "";
348  }
349 
351  {
352  $this->object->setCorrectAnswers((int) $_POST["correctanswers"]);
353  $this->object->setTextRating($_POST["text_rating"]);
354  }
355 
356  public function writeAnswerSpecificPostData(ilPropertyFormGUI $form): void
357  {
358  // Delete all existing answers and create new answers from the form data
359  $this->object->flushAnswers();
360  foreach ($this->answers_from_post as $index => $answertext) {
361  $this->object->addAnswer(assQuestion::extendedTrim($answertext), $_POST['answers']['points'][$index], $index);
362  }
363  }
364 
366  {
367  // number of requested answers
368  $correctanswers = new ilNumberInputGUI($this->lng->txt("nr_of_correct_answers"), "correctanswers");
369  $correctanswers->setMinValue(1);
370  $correctanswers->setDecimals(0);
371  $correctanswers->setSize(3);
372  $correctanswers->setValue($this->object->getCorrectAnswers());
373  $correctanswers->setRequired(true);
374  $form->addItem($correctanswers);
375 
376  // maximum available points
377  $points = new ilNumberInputGUI($this->lng->txt("maximum_points"), "points");
378  $points->setMinValue(0.0);
379  $points->setMinvalueShouldBeGreater(true);
380  $points->setSize(6);
381  $points->setDisabled(true);
382  $points->allowDecimals(true);
383  $points->setValue($this->object->getMaximumPoints());
384  $points->setRequired(false);
385  $form->addItem($points);
386 
387  // text rating
388  $textrating = new ilSelectInputGUI($this->lng->txt("text_rating"), "text_rating");
389  $text_options = array(
390  "ci" => $this->lng->txt("cloze_textgap_case_insensitive"),
391  "cs" => $this->lng->txt("cloze_textgap_case_sensitive")
392  );
393  if (!$this->object->getSelfAssessmentEditingMode()) {
394  $text_options["l1"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "1");
395  $text_options["l2"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "2");
396  $text_options["l3"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "3");
397  $text_options["l4"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "4");
398  $text_options["l5"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "5");
399  }
400  $textrating->setOptions($text_options);
401  $textrating->setValue($this->object->getTextRating());
402  $form->addItem($textrating);
403  return $form;
404  }
405 
407  {
408  $choices = new ilAnswerWizardInputGUI($this->lng->txt("answers"), "answers");
409  $choices->setRequired(true);
410  $choices->setQuestionObject($this->object);
411  $choices->setSingleline(true);
412  $choices->setAllowMove(false);
413  $choices->setMinValue(0.0);
414  if ($this->object->getAnswerCount() == 0) {
415  $this->object->addAnswer("", 0, 0);
416  }
417  $choices->setValues(array_map(
418  function (ASS_AnswerBinaryStateImage $value) {
419  $value->setAnswerText(html_entity_decode($value->getAnswerText()));
420  return $value;
421  },
422  $this->object->getAnswers()
423  ));
424  $form->addItem($choices);
425  return $form;
426  }
427 
428 
439  {
440  return array();
441  }
442 
453  {
454  return array();
455  }
456 
463  public function getAggregatedAnswersView(array $relevant_answers): string
464  {
465  return $this->renderAggregateView(
466  $this->aggregateAnswers($relevant_answers)
467  )->get();
468  }
469 
470  public function aggregateAnswers($relevant_answers_chosen): array
471  {
472  $aggregate = array();
473 
474  foreach ($relevant_answers_chosen as $relevant_answer) {
475  if (array_key_exists($relevant_answer['value1'], $aggregate)) {
476  $aggregate[$relevant_answer['value1']]++;
477  } else {
478  $aggregate[$relevant_answer['value1']] = 1;
479  }
480  }
481  return $aggregate;
482  }
483 
489  public function renderAggregateView($aggregate): ilTemplate
490  {
491  $tpl = new ilTemplate('tpl.il_as_aggregated_answers_table.html', true, true, "Modules/TestQuestionPool");
492 
493  foreach ($aggregate as $key => $value) {
494  $tpl->setCurrentBlock('aggregaterow');
495  $tpl->setVariable('OPTION', $key);
496  $tpl->setVariable('COUNT', $value);
498  }
499  return $tpl;
500  }
501 
502  public function getAnswersFrequency($relevantAnswers, $questionIndex): array
503  {
504  $answers = array();
505 
506  foreach ($relevantAnswers as $ans) {
507  if (!isset($answers[$ans['value1']])) {
508  $answers[$ans['value1']] = array(
509  'answer' => $ans['value1'], 'frequency' => 0
510  );
511  }
512 
513  $answers[$ans['value1']]['frequency']++;
514  }
515  $answers = $this->completeAddAnswerAction($answers, $questionIndex);
516  return $answers;
517  }
518 
519  protected function completeAddAnswerAction($answers, $questionIndex)
520  {
521  foreach ($answers as $key => $ans) {
522  $found = false;
523 
524  foreach ($this->object->getAnswers() as $item) {
525  if ($ans['answer'] !== $item->getAnswerText()) {
526  continue;
527  }
528 
529  $found = true;
530  break;
531  }
532 
533  if (!$found) {
534  $answers[$key]['addable'] = true;
535  }
536  }
537 
538  return $answers;
539  }
540 
542  {
543  $choices = new ilAssAnswerCorrectionsInputGUI($this->lng->txt("answers"), "answers");
544  $choices->setRequired(true);
545  $choices->setQuestionObject($this->object);
546  $choices->setValues($this->object->getAnswers());
547  $form->addItem($choices);
548  }
549 
554  {
555  $input = $form->getItemByPostVar('answers');
556  $values = $input->getValues();
557 
558  foreach ($this->object->getAnswers() as $index => $answer) {
559  $points = (float) str_replace(',', '.', $values[$index]->getPoints());
560  $answer->setPoints($points);
561  }
562  }
563 }
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
hasCorrectSolution($activeId, $passIndex)
editQuestion($checkonly=false)
Creates an output of the edit form for the question.
generateCorrectnessIconsForCorrectness(int $correctness)
setCurrentBlock(string $blockname=self::DEFAULT_BLOCK)
Sets the template to the given block.
getPreview($show_question_only=false, $showInlineFeedback=false)
This class represents a selection list property in a property form.
getSolutionOutput( $active_id, $pass=null, $graphicalOutput=false, $result_output=false, $show_question_only=true, $show_feedback=false, $show_correct_solution=false, $show_manual_scoring=false, $show_question_text=true)
Get the question solution output.
getAfterParticipationSuppressionAnswerPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
getItemByPostVar(string $a_post_var)
getSpecificFeedbackOutput(array $userSolution)
Class for answers with a binary state indicator.
addBasicQuestionFormProperties(ilPropertyFormGUI $form)
parseCurrentBlock(string $blockname=self::DEFAULT_BLOCK)
Parses the given block.
escapeTemplatePlaceholders(string $text)
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setOptions(array $a_options)
ilGlobalPageTemplate $tpl
populateQuestionSpecificFormPart(\ilPropertyFormGUI $form)
populateTaxonomyFormSection(ilPropertyFormGUI $form)
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,)
removeanswers()
Remove an answer.
addQuestionFormCommandButtons(ilPropertyFormGUI $form)
This class represents a single choice wizard property in a property form.
getAnswersFrequency($relevantAnswers, $questionIndex)
__construct(VocabulariesInterface $vocabularies)
string $key
Consumer key/client ID value.
Definition: System.php:193
getAfterParticipationSuppressionQuestionPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
Basic GUI class for assessment questions.
setRequired(bool $a_required)
populateAnswerSpecificFormPart(\ilPropertyFormGUI $form)
getTestOutput($active_id, $pass=null, $is_postponed=false, $use_post_solutions=false, $inlineFeedback=false)
static extendedTrim(string $value)
Trim non-printable characters from the beginning and end of a string.
getILIASPage(string $html="")
Returns the ILIAS Page around a question.
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="", $inlineFeedbackEnabled=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getAggregatedAnswersView(array $relevant_answers)
Returns an html string containing a question specific representation of the answers so far given in t...
completeAddAnswerAction($answers, $questionIndex)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
writeQuestionSpecificPostData(ilPropertyFormGUI $form)
Extracts the question specific values from $_POST and applies them to the data object.
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($id=-1)
assTextSubsetGUI constructor
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...
aggregateAnswers($relevant_answers_chosen)
writeAnswerSpecificPostData(ilPropertyFormGUI $form)
Extracts the answer specific values from $_POST and applies them to the data object.
setVariable(string $variable, $value='')
Sets the given variable to the given value.
addanswers()
Add a new answer.
writePostData(bool $always=false)
{}
getGenericFeedbackOutput(int $active_id, ?int $pass)