ILIAS  release_7 Revision v7.30-3-g800a261c036
class.assTextSubsetGUI.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once './Modules/TestQuestionPool/classes/class.assQuestionGUI.php';
5require_once './Modules/TestQuestionPool/interfaces/interface.ilGuiQuestionScoringAdjustable.php';
6require_once './Modules/TestQuestionPool/interfaces/interface.ilGuiAnswerScoringAdjustable.php';
7require_once './Modules/Test/classes/inc.AssessmentConstants.php';
8
25{
27
35 public function __construct($id = -1)
36 {
38 require_once './Modules/TestQuestionPool/classes/class.assTextSubset.php';
39 $this->object = new assTextSubset();
40 if ($id >= 0) {
41 $this->object->loadFromDb($id);
42 }
43 }
44
48 protected function writePostData($always = false)
49 {
50 /*
51 * sk 26.09.22: This is horrific but I don't see a better way right now,
52 * without needing to check most questions for side-effects.
53 */
54 $this->answers_from_post = $_POST['answers']['answer'];
55 $hasErrors = (!$always) ? $this->editQuestion(true) : false;
56 if (!$hasErrors) {
57 require_once 'Services/Form/classes/class.ilPropertyFormGUI.php';
62 return 0;
63 }
64 return 1;
65 }
66
70 public function editQuestion($checkonly = false)
71 {
72 $save = $this->isSaveCommand();
73 $this->getQuestionTemplate();
74
75 require_once './Services/Form/classes/class.ilPropertyFormGUI.php';
76 $form = new ilPropertyFormGUI();
77 $this->editForm = $form;
78
79 $form->setFormAction($this->ctrl->getFormAction($this));
80 $form->setTitle($this->outQuestionType());
81 $form->setMultipart(false);
82 $form->setTableWidth("100%");
83 $form->setId("asstextsubset");
84
88 $this->populateTaxonomyFormSection($form);
90
91 $errors = false;
92 if ($save) {
93 $form->setValuesByPost();
94 $points = $form->getItemByPostVar('points');
95 $points->setValue($this->object->getMaximumPoints());
96 $errors = !$form->checkInput();
97 $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
98 if ($errors) {
99 $checkonly = false;
100 }
101 }
102
103 if (!$checkonly) {
104 $this->tpl->setVariable("QUESTION_DATA", $form->getHTML());
105 }
106 return $errors;
107 }
108
112 public function addanswers()
113 {
114 $this->writePostData(true);
115 $position = key($_POST['cmd']['addanswers']);
116 $this->object->addAnswer("", 0, $position + 1);
117 $this->editQuestion();
118 }
119
123 public function removeanswers()
124 {
125 $this->writePostData(true);
126 $position = key($_POST['cmd']['removeanswers']);
127 $this->object->deleteAnswer($position);
128 $this->editQuestion();
129 }
130
137 {
138 return true;
139 }
140
154 public function getSolutionOutput(
155 $active_id,
156 $pass = null,
157 $graphicalOutput = false,
158 $result_output = false,
159 $show_question_only = true,
160 $show_feedback = false,
161 $show_correct_solution = false,
162 $show_manual_scoring = false,
163 $show_question_text = true
164 ) {
165 // get the solution of the user for the active pass or from the last pass if allowed
166 $solutions = array();
167 if (($active_id > 0) && (!$show_correct_solution)) {
168 $solutions = $this->object->getSolutionValues($active_id, $pass, !$this->getUseIntermediateSolution());
169 } else {
170 $rank = array();
171 foreach ($this->object->answers as $answer) {
172 if ($answer->getPoints() > 0) {
173 if (!is_array($rank[$answer->getPoints()])) {
174 $rank[$answer->getPoints()] = array();
175 }
176 array_push($rank[$answer->getPoints()], $answer->getAnswertext());
177 }
178 }
179 krsort($rank, SORT_NUMERIC);
180 foreach ($rank as $index => $bestsolutions) {
181 array_push($solutions, array("value1" => join(",", $bestsolutions), "points" => $index));
182 }
183 }
184
185 // generate the question output
186 include_once "./Services/UICore/classes/class.ilTemplate.php";
187 $template = new ilTemplate("tpl.il_as_qpl_textsubset_output_solution.html", true, true, "Modules/TestQuestionPool");
188 $solutiontemplate = new ilTemplate("tpl.il_as_tst_solution_output.html", true, true, "Modules/TestQuestionPool");
189 $available_answers = &$this->object->getAvailableAnswers();
190 for ($i = 0; $i < $this->object->getCorrectAnswers(); $i++) {
191 if ((!$test_id) && (strcmp($solutions[$i]["value1"], "") == 0)) {
192 } else {
193 if (($active_id > 0) && (!$show_correct_solution)) {
194 if ($graphicalOutput) {
195 // output of ok/not ok icons for user entered solutions
196 $index = $this->object->isAnswerCorrect($available_answers, $solutions[$i]["value1"]);
197 $correct = false;
198 if ($index !== false) {
199 unset($available_answers[$index]);
200 $correct = true;
201 }
202 if ($correct) {
203 $template->setCurrentBlock("icon_ok");
204 $template->setVariable("ICON_OK", ilUtil::getImagePath("icon_ok.svg"));
205 $template->setVariable("TEXT_OK", $this->lng->txt("answer_is_right"));
206 $template->parseCurrentBlock();
207 } else {
208 $template->setCurrentBlock("icon_ok");
209 $template->setVariable("ICON_NOT_OK", ilUtil::getImagePath("icon_not_ok.svg"));
210 $template->setVariable("TEXT_NOT_OK", $this->lng->txt("answer_is_wrong"));
211 $template->parseCurrentBlock();
212 }
213 }
214 }
215 $template->setCurrentBlock("textsubset_row");
216 $template->setVariable("SOLUTION", $this->escapeTemplatePlaceholders($solutions[$i]["value1"]));
217 $template->setVariable("COUNTER", $i + 1);
218 if ($result_output) {
219 $points = $solutions[$i]["points"];
220 $resulttext = ($points == 1) ? "(%s " . $this->lng->txt("point") . ")" : "(%s " . $this->lng->txt("points") . ")";
221 $template->setVariable("RESULT_OUTPUT", sprintf($resulttext, $points));
222 }
223 $template->parseCurrentBlock();
224 }
225 }
226 if ($show_question_text == true) {
227 $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
228 }
229 $questionoutput = $template->get();
230 $feedback = ($show_feedback && !$this->isTestPresentationContext()) ? $this->getAnswerFeedbackOutput($active_id, $pass) : "";
231 if (strlen($feedback)) {
232 $cssClass = (
233 $this->hasCorrectSolution($active_id, $pass) ?
235 );
236
237 $solutiontemplate->setVariable("ILC_FB_CSS_CLASS", $cssClass);
238 $solutiontemplate->setVariable("FEEDBACK", $this->object->prepareTextareaOutput($feedback, true));
239 }
240 $solutiontemplate->setVariable("SOLUTION_OUTPUT", $questionoutput);
241
242 $solutionoutput = $solutiontemplate->get();
243 if (!$show_question_only) {
244 // get page object output
245 $solutionoutput = $this->getILIASPage($solutionoutput);
246 }
247 return $solutionoutput;
248 }
249
250 public function getPreview($show_question_only = false, $showInlineFeedback = false)
251 {
252 $solutions = is_object($this->getPreviewSession()) ? (array) $this->getPreviewSession()->getParticipantsSolution() : array();
253 // generate the question output
254 include_once "./Services/UICore/classes/class.ilTemplate.php";
255 $template = new ilTemplate("tpl.il_as_qpl_textsubset_output.html", true, true, "Modules/TestQuestionPool");
256 $width = $this->object->getMaxTextboxWidth();
257 for ($i = 0; $i < $this->object->getCorrectAnswers(); $i++) {
258 $template->setCurrentBlock("textsubset_row");
259 foreach ($solutions as $idx => $solution_value) {
260 if ($idx == $i) {
261 $template->setVariable("TEXTFIELD_VALUE", " value=\"" . $this->escapeTemplatePlaceholders($solution_value) . "\"");
262 }
263 }
264 $template->setVariable("COUNTER", $i + 1);
265 $template->setVariable("TEXTFIELD_ID", sprintf("%02d", $i + 1));
266 $template->setVariable("TEXTFIELD_SIZE", $width);
267 $template->parseCurrentBlock();
268 }
269 $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
270 $questionoutput = $template->get();
271 if (!$show_question_only) {
272 // get page object output
273 $questionoutput = $this->getILIASPage($questionoutput);
274 }
275 return $questionoutput;
276 }
277
278 public function getTestOutput($active_id, $pass = null, $is_postponed = false, $use_post_solutions = false, $inlineFeedback = false)
279 {
280 // get the solution of the user for the active pass or from the last pass if allowed
281 $user_solution = "";
282 if ($active_id) {
283 $solutions = null;
284 // hey: prevPassSolutions - obsolete due to central check
285 #include_once "./Modules/Test/classes/class.ilObjTest.php";
286 #if (!ilObjTest::_getUsePreviousAnswers($active_id, true))
287 #{
288 # if (is_null($pass)) $pass = ilObjTest::_getPass($active_id);
289 #}
290 // hey.
291 $solutions = $this->object->getUserSolutionPreferingIntermediate($active_id, $pass);
292 }
293
294 // generate the question output
295 include_once "./Services/UICore/classes/class.ilTemplate.php";
296 $template = new ilTemplate("tpl.il_as_qpl_textsubset_output.html", true, true, "Modules/TestQuestionPool");
297 $width = $this->object->getMaxTextboxWidth();
298 for ($i = 0; $i < $this->object->getCorrectAnswers(); $i++) {
299 $template->setCurrentBlock("textsubset_row");
300 foreach ($solutions as $idx => $solution_value) {
301 if ($idx == $i) {
302 $template->setVariable("TEXTFIELD_VALUE", " value=\"" . $this->escapeTemplatePlaceholders($solution_value["value1"]) . "\"");
303 }
304 }
305 $template->setVariable("COUNTER", $i + 1);
306 $template->setVariable("TEXTFIELD_ID", sprintf("%02d", $i + 1));
307 $template->setVariable("TEXTFIELD_SIZE", $width);
308 $template->parseCurrentBlock();
309 }
310 $template->setVariable("QUESTIONTEXT", $this->object->getQuestionForHTMLOutput());
311 $questionoutput = $template->get();
312 $pageoutput = $this->outQuestionPage("", $is_postponed, $active_id, $questionoutput);
313 return $pageoutput;
314 }
315
316 public function getSpecificFeedbackOutput($userSolution)
317 {
318 $output = "";
319 return $this->object->prepareTextareaOutput($output, true);
320 }
321
323 {
324 $this->object->setCorrectAnswers($_POST["correctanswers"]);
325 $this->object->setTextRating($_POST["text_rating"]);
326 }
327
329 {
330 // Delete all existing answers and create new answers from the form data
331 $this->object->flushAnswers();
332 foreach ($this->answers_from_post as $index => $answertext) {
333 $this->object->addAnswer(ilUtil::secureString(htmlentities(trim($answertext))), $_POST['answers']['points'][$index], $index);
334 }
335 }
336
338 {
339 // number of requested answers
340 $correctanswers = new ilNumberInputGUI($this->lng->txt("nr_of_correct_answers"), "correctanswers");
341 $correctanswers->setMinValue(1);
342 $correctanswers->setDecimals(0);
343 $correctanswers->setSize(3);
344 $correctanswers->setValue($this->object->getCorrectAnswers());
345 $correctanswers->setRequired(true);
346 $form->addItem($correctanswers);
347
348 // maximum available points
349 $points = new ilNumberInputGUI($this->lng->txt("maximum_points"), "points");
350 $points->setMinValue(0.0);
351 $points->setMinvalueShouldBeGreater(true);
352 $points->setSize(6);
353 $points->setDisabled(true);
354 $points->allowDecimals(true);
355 $points->setValue($this->object->getMaximumPoints());
356 $points->setRequired(false);
357 $form->addItem($points);
358
359 // text rating
360 $textrating = new ilSelectInputGUI($this->lng->txt("text_rating"), "text_rating");
361 $text_options = array(
362 "ci" => $this->lng->txt("cloze_textgap_case_insensitive"),
363 "cs" => $this->lng->txt("cloze_textgap_case_sensitive")
364 );
365 if (!$this->object->getSelfAssessmentEditingMode()) {
366 $text_options["l1"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "1");
367 $text_options["l2"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "2");
368 $text_options["l3"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "3");
369 $text_options["l4"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "4");
370 $text_options["l5"] = sprintf($this->lng->txt("cloze_textgap_levenshtein_of"), "5");
371 }
372 $textrating->setOptions($text_options);
373 $textrating->setValue($this->object->getTextRating());
374 $form->addItem($textrating);
375 return $form;
376 }
377
379 {
380 // Choices
381 include_once "./Modules/TestQuestionPool/classes/class.ilAnswerWizardInputGUI.php";
382 $choices = new ilAnswerWizardInputGUI($this->lng->txt("answers"), "answers");
383 $choices->setRequired(true);
384 $choices->setQuestionObject($this->object);
385 $choices->setSingleline(true);
386 $choices->setAllowMove(false);
387 $choices->setMinValue(0.0);
388 if ($this->object->getAnswerCount() == 0) {
389 $this->object->addAnswer("", 0, 0);
390 }
391 $choices->setValues(array_map(
392 function (ASS_AnswerBinaryStateImage $value) {
393 $value->setAnswerText(html_entity_decode($value->getAnswerText()));
394 return $value;
395 },
396 $this->object->getAnswers()
397 ));
398 $form->addItem($choices);
399 return $form;
400 }
401
402
413 {
414 return array();
415 }
416
427 {
428 return array();
429 }
430
439 public function getAggregatedAnswersView($relevant_answers)
440 {
441 return $this->renderAggregateView(
442 $this->aggregateAnswers($relevant_answers)
443 )->get();
444 }
445
446 public function aggregateAnswers($relevant_answers_chosen)
447 {
448 $aggregate = array();
449
450 foreach ($relevant_answers_chosen as $relevant_answer) {
451 if (array_key_exists($relevant_answer['value1'], $aggregate)) {
452 $aggregate[$relevant_answer['value1']]++;
453 } else {
454 $aggregate[$relevant_answer['value1']] = 1;
455 }
456 }
457 return $aggregate;
458 }
459
465 public function renderAggregateView($aggregate)
466 {
467 $tpl = new ilTemplate('tpl.il_as_aggregated_answers_table.html', true, true, "Modules/TestQuestionPool");
468
469 foreach ($aggregate as $key => $value) {
470 $tpl->setCurrentBlock('aggregaterow');
471 $tpl->setVariable('OPTION', $key);
472 $tpl->setVariable('COUNT', $value);
473 $tpl->parseCurrentBlock();
474 }
475 return $tpl;
476 }
477
478 public function getAnswersFrequency($relevantAnswers, $questionIndex)
479 {
480 $answers = array();
481
482 foreach ($relevantAnswers as $ans) {
483 if (!isset($answers[$ans['value1']])) {
484 $answers[$ans['value1']] = array(
485 'answer' => $ans['value1'], 'frequency' => 0
486 );
487 }
488
489 $answers[$ans['value1']]['frequency']++;
490 }
491 $answers = $this->completeAddAnswerAction($answers, $questionIndex);
492 return $answers;
493 }
494
495 protected function completeAddAnswerAction($answers, $questionIndex)
496 {
497 foreach ($answers as $key => $ans) {
498 $found = false;
499
500 foreach ($this->object->getAnswers() as $item) {
501 if ($ans['answer'] !== $item->getAnswerText()) {
502 continue;
503 }
504
505 $found = true;
506 break;
507 }
508
509 if (!$found) {
510 $answers[$key]['addable'] = true;
511 }
512 }
513
514 return $answers;
515 }
516
518 {
519 // Choices
520 require_once 'Modules/TestQuestionPool/classes/forms/class.ilAssAnswerCorrectionsInputGUI.php';
521 $choices = new ilAssAnswerCorrectionsInputGUI($this->lng->txt("answers"), "answers");
522 $choices->setRequired(true);
523 $choices->setQuestionObject($this->object);
524 $choices->setValues($this->object->getAnswers());
525 $form->addItem($choices);
526
527 return $form;
528 }
529
534 {
535 $points = $form->getInput('answers')['points'];
536
537 foreach ($this->object->getAnswers() as $index => $answer) {
538 /* @var ASS_AnswerBinaryStateImage $answer */
539 $answer->setPoints((float) $points[$index]);
540 }
541 }
542}
$_POST["username"]
Class for answers with a binary state indicator.
An exception for terminatinating execution or to throw for unit testing.
Basic GUI class for assessment questions.
populateTaxonomyFormSection(ilPropertyFormGUI $form)
addQuestionFormCommandButtons($form)
Add the command buttons of a question properties form.
escapeTemplatePlaceholders(string $text)
getILIASPage($html="")
Returns the ILIAS Page around a question.
getQuestionTemplate()
get question template
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="", $inlineFeedbackEnabled=false)
output question page
getUseIntermediateSolution()
Get if intermediate solution should be used for solution output.
hasCorrectSolution($activeId, $passIndex)
addBasicQuestionFormProperties($form)
Add basic question form properties: assessment: title, author, description, question,...
getAnswerFeedbackOutput($active_id, $pass)
Returns the answer generic feedback depending on the results of the question.
Multiple choice question GUI representation.
getSpecificFeedbackOutput($userSolution)
Returns the answer specific feedback for the question.
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
getTestOutput($active_id, $pass=null, $is_postponed=false, $use_post_solutions=false, $inlineFeedback=false)
populateAnswerSpecificFormPart(\ilPropertyFormGUI $form)
writePostData($always=false)
{Evaluates a posted edit form and writes the form data in the question object.integer A positive valu...
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
__construct($id=-1)
assTextSubsetGUI constructor
editQuestion($checkonly=false)
Creates an output of the edit form for the question.
completeAddAnswerAction($answers, $questionIndex)
addanswers()
Add a new answer.
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.
getPreview($show_question_only=false, $showInlineFeedback=false)
getAfterParticipationSuppressionAnswerPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
supportsIntermediateSolutionOutput()
Question type specific support of intermediate solution output The function getSolutionOutput respect...
getAggregatedAnswersView($relevant_answers)
Returns an html string containing a question specific representation of the answers so far given in t...
writeAnswerSpecificPostData(ilPropertyFormGUI $form)
Extracts the answer specific values from $_POST and applies them to the data object.
aggregateAnswers($relevant_answers_chosen)
populateQuestionSpecificFormPart(\ilPropertyFormGUI $form)
writeQuestionSpecificPostData(ilPropertyFormGUI $form)
Extracts the question specific values from $_POST and applies them to the data object.
removeanswers()
Remove an answer.
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.
This class represents a number property in a property form.
This class represents a property form user interface.
addItem($a_item)
Add Item (Property, SectionHeader).
getInput($a_post_var, $ensureValidation=true)
Returns the value of a HTTP-POST variable, identified by the passed id.
This class represents a selection list property in a property form.
special template class to simplify handling of ITX/PEAR
static secureString($a_str, $a_strip_html=true, $a_allow="")
Remove unsecure tags.
static getImagePath($img, $module_path="", $mode="output", $offline=false)
get image path (for images located in a template directory)
$errors
Definition: imgupload.php:49
Interface ilGuiAnswerScoringAdjustable.
Interface ilGuiQuestionScoringAdjustable.
$index
Definition: metadata.php:128
$i
Definition: metadata.php:24
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc