ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.assTextQuestion.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
24
39{
40 protected const HAS_SPECIFIC_FEEDBACK = false;
41
46
47 private int $max_num_of_chars = 0;
48 private bool $word_counter_enabled = false;
50 private int $matchcondition = 0;
52
57 public array $keywords;
58
63 public array $answers = [];
64
77 public function __construct(
78 string $title = "",
79 string $comment = "",
80 string $author = "",
81 int $owner = -1,
82 string $question = ""
83 ) {
85 $this->points = 1;
86 }
87
88 public function getMatchcondition(): int
89 {
91 }
92
93 public function setMatchcondition(int $matchcondition): void
94 {
95 $this->matchcondition = $matchcondition;
96 }
97
98 public function isComplete(): bool
99 {
100 if (strlen($this->title)
101 && $this->author
102 && $this->question
103 && $this->getMaximumPoints() > 0
104 ) {
105 return true;
106 }
107 return false;
108 }
109
110 public function saveToDb(?int $original_id = null): void
111 {
115 parent::saveToDb();
116 }
117
118 public function loadFromDb(int $question_id): void
119 {
120 $result = $this->db->queryF(
121 "SELECT qpl_questions.*, " . $this->getAdditionalTableName() . ".* FROM qpl_questions LEFT JOIN " . $this->getAdditionalTableName() . " ON " . $this->getAdditionalTableName() . ".question_fi = qpl_questions.question_id WHERE qpl_questions.question_id = %s",
122 ["integer"],
123 [$question_id]
124 );
125 if ($this->db->numRows($result) == 1) {
126 $data = $this->db->fetchAssoc($result);
127 $this->setId($question_id);
128 $this->setObjId($data["obj_fi"]);
129 $this->setTitle((string) $data["title"]);
130 $this->setComment((string) $data["description"]);
131 $this->setOriginalId($data["original_id"]);
132 $this->setNrOfTries($data['nr_of_tries']);
133 $this->setAuthor($data["author"]);
134 $this->setPoints((float) $data["points"]);
135 $this->setOwner($data["owner"]);
136 $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc((string) $data["question_text"], 1));
137 $this->setShuffle(false);
138 $this->setWordCounterEnabled((bool) $data['word_cnt_enabled']);
139 $this->setMaxNumOfChars($data["maxnumofchars"] ?? 0);
140 $this->setTextRating($this->isValidTextRating($data["textgap_rating"]) ? $data["textgap_rating"] : assClozeGap::TEXTGAP_RATING_CASEINSENSITIVE);
141 $this->matchcondition = (isset($data['matchcondition'])) ? (int) $data['matchcondition'] : 0;
142 $this->setKeywordRelation(($data['keyword_relation']));
143
144 try {
145 $this->setLifecycle(ilAssQuestionLifecycle::getInstance($data['lifecycle']));
148 }
149
150 try {
151 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
153 }
154 }
155
156 $result = $this->db->queryF(
157 'SELECT * FROM qpl_a_essay WHERE question_fi = %s',
158 ['integer'],
159 [$this->getId()]
160 );
161
162 $this->flushAnswers();
163 while ($row = $this->db->fetchAssoc($result)) {
164 $this->addAnswer($row['answertext'] ?? '', $row['points'] ?? 0.0);
165 }
166
167 parent::loadFromDb($question_id);
168 }
169
170 public function getMaxNumOfChars(): int
171 {
173 }
174
175 public function setMaxNumOfChars(int $maxchars = 0): void
176 {
177 $this->max_num_of_chars = $maxchars;
178 }
179
180 public function isWordCounterEnabled(): bool
181 {
183 }
184
186 {
187 $this->word_counter_enabled = $word_counter_enabled;
188 }
189
196 public function getMaximumPoints(): float
197 {
198 if (in_array($this->getKeywordRelation(), self::getScoringModesWithPointsByQuestion())) {
199 return parent::getPoints();
200 }
201
202 $points = 0;
203
204 foreach ($this->answers as $answer) {
205 if ($answer->getPoints() > 0) {
206 $points = $points + $answer->getPoints();
207 }
208 }
209
210 return $points;
211 }
212
213 public function getMinimumPoints()
214 {
215 if (in_array($this->getKeywordRelation(), self::getScoringModesWithPointsByQuestion())) {
216 return 0;
217 }
218
219 $points = 0;
220
221 foreach ($this->answers as $answer) {
222 if ($answer->getPoints() < 0) {
223 $points = $points + $answer->getPoints();
224 }
225 }
226
227 return $points;
228 }
229
230 private function isValidTextRating($textRating): bool
231 {
232 switch ($textRating) {
240 return true;
241 }
242
243 return false;
244 }
245
254 public function isKeywordMatching($answertext, $a_keyword): bool
255 {
256 global $DIC;
257 $refinery = $DIC->refinery();
258 $result = false;
259 $textrating = $this->getTextRating();
260
261 switch ($textrating) {
263 if (ilStr::strPos(ilStr::strToLower($answertext), ilStr::strToLower($a_keyword), 0) !== false) {
264 return true;
265 }
266 break;
268 if (ilStr::strPos($answertext, $a_keyword) !== false) {
269 return true;
270 }
271 break;
272 }
273
274 // "<p>red</p>" would not match "red" even with distance of 5
275 $answertext = strip_tags($answertext);
276 $answerwords = [];
277 if (preg_match_all("/([^\s.]+)/", $answertext, $matches)) {
278 foreach ($matches[1] as $answerword) {
279 array_push($answerwords, trim($answerword));
280 }
281 }
282
283 // create correct transformation
284 switch ($textrating) {
286 $transformation = $refinery->string()->levenshtein()->standard($a_keyword, 1);
287 break;
289 $transformation = $refinery->string()->levenshtein()->standard($a_keyword, 2);
290 break;
292 $transformation = $refinery->string()->levenshtein()->standard($a_keyword, 3);
293 break;
295 $transformation = $refinery->string()->levenshtein()->standard($a_keyword, 4);
296 break;
298 $transformation = $refinery->string()->levenshtein()->standard($a_keyword, 5);
299 break;
300 }
301
302 // run answers against Levenshtein methods
303 foreach ($answerwords as $a_original) {
304 if (isset($transformation) && $transformation->transform($a_original) >= 0) {
305 return true;
306 }
307 }
308 return $result;
309 }
310
311 protected function calculateReachedPointsForSolution(string $solution): float
312 {
313 $decoded_solution = html_entity_decode($solution);
314 // Return min points when keyword relation is NON KEYWORDS
315 if ($this->getKeywordRelation() === self::SCORING_MODE_KEYWORD_RELATION_NONE) {
316 return $this->getMinimumPoints();
317 }
318
319 // Return min points if there are no answers present.
320 $answers = $this->getAnswers();
321
322 if ($answers === []) {
323 return $this->getMinimumPoints();
324 }
325
326 switch ($this->getKeywordRelation()) {
327 case 'any':
328 $points = 0.0;
329 foreach ($answers as $answer) {
330 $qst_answer = $answer->getAnswertext();
331 $user_answer = ' ' . $decoded_solution;
332 if ($this->isKeywordMatching($user_answer, $qst_answer)) {
333 $points += $answer->getPoints();
334 }
335 }
336 return $points;
337
338 case 'all':
339 foreach ($answers as $answer) {
340 $qst_answer = $answer->getAnswertext();
341 $user_answer = ' ' . $decoded_solution;
342 if (!$this->isKeywordMatching($user_answer, $qst_answer)) {
343 return 0.0;
344 }
345 }
346 return $this->getMaximumPoints();
347
348 case 'one':
349 foreach ($answers as $answer) {
350 $qst_answer = $answer->getAnswertext();
351 $user_answer = ' ' . $decoded_solution;
352 if ($this->isKeywordMatching($user_answer, $qst_answer)) {
353 return $this->getMaximumPoints();
354 }
355 }
356 }
357
358 return 0.0;
359 }
360
361 public function calculateReachedPoints(
362 int $active_id,
363 ?int $pass = null,
364 bool $authorized_solution = true
365 ): float {
366 if ($pass === null) {
367 $pass = $this->getSolutionMaxPass($active_id);
368 }
369
370 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized_solution);
371
372 // Return min points when no answer was given.
373 if ($this->db->numRows($result) === 0) {
374 return $this->getMinimumPoints();
375 }
376
377 // Return points of points are already on the row.
378 $row = $this->db->fetchAssoc($result);
379 if ($row['points'] !== null) {
380 return (float) $row["points"];
381 }
382
383 return $this->calculateReachedPointsForSolution($row['value1']);
384 }
385
386 public function saveWorkingData(
387 int $active_id,
388 ?int $pass = null,
389 bool $authorized = true
390 ): bool {
391 if ($pass === null) {
392 $pass = ilObjTest::_getPass($active_id);
393 }
394
395 $answer = $this->getSolutionSubmit();
396 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(
397 function () use ($answer, $active_id, $pass, $authorized) {
398 $this->removeCurrentSolution($active_id, $pass, $authorized);
399
400 if ($answer !== '') {
401 $this->saveCurrentSolution($active_id, $pass, $answer, null, $authorized);
402 }
403 }
404 );
405
406 return true;
407 }
408
409 protected function getSolutionSubmit(): string
410 {
411 $text = $this->questionpool_request->string('TEXT', '');
412
413 $text = (new ilRTESettings($this->lng, $this->current_user))->getRichTextEditor() === 'tinymce'
414 ? ilUtil::stripSlashes($text, false)
415 : htmlentities($text);
416
417 return ilUtil::isHTML($text) ? $this->getHtmlUserSolutionPurifier()->purify($text) : $text;
418 }
419
421 {
422 $this->db->manipulateF(
423 "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
424 ['integer'],
425 [$this->getId()]
426 );
427
428 $fields = [
429 'question_fi' => ['integer', $this->getId()],
430 'maxnumofchars' => ['integer', $this->getMaxNumOfChars()],
431 'word_cnt_enabled' => ['integer', (int) $this->isWordCounterEnabled()],
432 'keywords' => ['text', null],
433 'textgap_rating' => ['text', $this->getTextRating()],
434 'matchcondition' => ['integer', $this->getMatchcondition()],
435 'keyword_relation' => ['text', $this->getKeywordRelation()]
436 ];
437
438 $this->db->insert($this->getAdditionalTableName(), $fields);
439 }
440
441 public function saveAnswerSpecificDataToDb()
442 {
443 $this->db->manipulateF(
444 "DELETE FROM qpl_a_essay WHERE question_fi = %s",
445 ['integer'],
446 [$this->getId()]
447 );
448
449 foreach ($this->answers as $answer) {
451 $nextID = $this->db->nextId('qpl_a_essay');
452 $this->db->manipulateF(
453 "INSERT INTO qpl_a_essay (answer_id, question_fi, answertext, points) VALUES (%s, %s, %s, %s)",
454 ['integer', 'integer', 'text', 'float'],
455 [
456 $nextID,
457 $this->getId(),
458 $answer->getAnswertext(),
459 $answer->getPoints()
460 ]
461 );
462 }
463 }
464
465 public function getQuestionType(): string
466 {
467 return "assTextQuestion";
468 }
469
470 public function getTextRating(): string
471 {
472 return $this->text_rating;
473 }
474
475 public function setTextRating($a_text_rating): void
476 {
477 switch ($a_text_rating) {
485 $this->text_rating = $a_text_rating;
486 break;
487 default:
489 break;
490 }
491 }
492
499 public function getAdditionalTableName(): string
500 {
501 return "qpl_qst_essay";
502 }
503
508 public function getRTETextWithMediaObjects(): string
509 {
510 return parent::getRTETextWithMediaObjects();
511 }
512
516 public function toJSON(): string
517 {
518 $result = [];
519 $result['id'] = $this->getId();
520 $result['type'] = (string) $this->getQuestionType();
521 $result['title'] = $this->getTitleForHTMLOutput();
522 $result['question'] = $this->formatSAQuestion($this->getQuestion());
523 $result['nr_of_tries'] = $this->getNrOfTries();
524 $result['shuffle'] = $this->getShuffle();
525 $result['maxlength'] = $this->getMaxNumOfChars();
526 return json_encode($result);
527 }
528
529 public function getAnswerCount(): int
530 {
531 return count($this->answers);
532 }
533
547 public function addAnswer(
548 $answertext = "",
549 $points = 0.0,
550 $points_unchecked = 0.0,
551 $order = 0,
552 $answerimage = ""
553 ): void {
554 $this->answers[] = new ASS_AnswerMultipleResponseImage($answertext, $points);
555 }
556
561 public function getAnswers(): array
562 {
563 return $this->answers;
564 }
565
575 public function getAnswer($index = 0): ?ASS_AnswerMultipleResponseImage
576 {
577 if ($index < 0) {
578 return null;
579 }
580 if (count($this->answers) < 1) {
581 return null;
582 }
583 if ($index >= count($this->answers)) {
584 return null;
585 }
586
587 return $this->answers[$index];
588 }
589
598 public function deleteAnswer($index = 0): void
599 {
600 if ($index < 0) {
601 return;
602 }
603 if (count($this->answers) < 1) {
604 return;
605 }
606 if ($index >= count($this->answers)) {
607 return;
608 }
609 $answer = $this->answers[$index];
610 //if (strlen($answer->getImage())) {
611 // $this->deleteImage($answer->getImage());
612 //}
613 unset($this->answers[$index]);
614 $this->answers = array_values($this->answers);
615 for ($i = 0, $iMax = count($this->answers); $i < $iMax; $i++) {
616 if ($this->answers[$i]->getOrder() > $index) {
617 $this->answers[$i]->setOrder($i);
618 }
619 }
620 }
621
622 public function getAnswerTableName(): string
623 {
624 return 'qpl_a_essay';
625 }
626
633 public function flushAnswers(): void
634 {
635 $this->answers = [];
636 }
637
638 public function setAnswers($answers): void
639 {
640 if (isset($answers['answer'])) {
641 $count = count($answers['answer']);
642 $withPoints = true;
643 } else {
644 $count = count($answers);
645 $withPoints = false;
646 }
647
648 $this->flushAnswers();
649
650 for ($i = 0; $i < $count; $i++) {
651 if ($withPoints) {
652 $this->addAnswer(
653 $answers['answer'][$i],
654 $this->refinery->kindlyTo()->float()->transform($answers['points'][$i])
655 );
656 } else {
657 $this->addAnswer($answers[$i], 0);
658 }
659 }
660 }
661
662 public function duplicateAnswers($original_id): void
663 {
664 $result = $this->db->queryF(
665 "SELECT * FROM qpl_a_essay WHERE question_fi = %s",
666 ['integer'],
667 [$original_id]
668 );
669 if ($result->numRows()) {
670 while ($row = $this->db->fetchAssoc($result)) {
671 $next_id = $this->db->nextId('qpl_a_essay');
672 $affectedRows = $this->db->manipulateF(
673 "INSERT INTO qpl_a_essay (answer_id, question_fi, answertext, points)
674 VALUES (%s, %s, %s, %s)",
675 ['integer','integer','text','float'],
676 [$next_id, $this->getId(), $row["answertext"], $row["points"]]
677 );
678 }
679 }
680 }
681
682 public function getKeywordRelation()
683 {
684 return $this->keyword_relation;
685 }
686
691 public function setKeywordRelation(?string $relation): void
692 {
693 if ($relation !== null) {
694 $this->keyword_relation = $relation;
695 }
696 }
697
698 public static function getValidScoringModes(): array
699 {
700 return array_merge(self::getScoringModesWithPointsByQuestion(), self::getScoringModesWithPointsByKeyword());
701 }
702
703 public static function getScoringModesWithPointsByQuestion(): array
704 {
705 return [
706 self::SCORING_MODE_KEYWORD_RELATION_NONE,
707 self::SCORING_MODE_KEYWORD_RELATION_ALL,
708 self::SCORING_MODE_KEYWORD_RELATION_ONE
709 ];
710 }
711
712 public static function getScoringModesWithPointsByKeyword(): array
713 {
714 return [
715 self::SCORING_MODE_KEYWORD_RELATION_ANY
716 ];
717 }
718
719 public function countLetters($text): int
720 {
721 $text = strip_tags($text);
722
723 $text = str_replace('&gt;', '>', $text);
724 $text = str_replace('&lt;', '<', $text);
725 $text = str_replace('&nbsp;', ' ', $text);
726 $text = str_replace('&amp;', '&', $text);
727
728 $text = str_replace("\r\n", "\n", $text);
729 $text = str_replace("\n", "", $text);
730
731 return ilStr::strLen($text);
732 }
733
734 public function countWords($text): int
735 {
736 if ($text === '') {
737 return 0;
738 }
739 $text = str_replace('&nbsp;', ' ', $text);
740
741 $text = preg_replace('/[.,:;!?\-_#\'"+*\\/=()&%§$]/m', '', $text);
742
743 $text = preg_replace('/^\s*/m', '', $text);
744 $text = preg_replace('/\s*$/m', '', $text);
745 $text = preg_replace('/\s+/m', ' ', $text);
746
747 return count(explode(' ', $text));
748 }
749
750 public function getLatestAutosaveContent(int $active_id, int $pass): ?string
751 {
752 $question_fi = $this->getId();
753
754 // Do we have an unauthorized result?
755 $cntresult = $this->db->query(
756 '
757 SELECT count(solution_id) cnt
758 FROM tst_solutions
759 WHERE active_fi = ' . $this->db->quote($active_id, 'int') . '
760 AND question_fi = ' . $this->db->quote($this->getId(), 'int') . '
761 AND authorized = ' . $this->db->quote(0, 'int')
762 . ' AND pass = ' . $this->db->quote($pass, 'int')
763 );
764 $row = $this->db->fetchAssoc($cntresult);
765 if ($row['cnt'] > 0) {
766 $tresult = $this->db->query(
767 '
768 SELECT value1
769 FROM tst_solutions
770 WHERE active_fi = ' . $this->db->quote($active_id, 'int') . '
771 AND question_fi = ' . $this->db->quote($this->getId(), 'int') . '
772 AND authorized = ' . $this->db->quote(0, 'int')
773 . ' AND pass = ' . $this->db->quote($pass, 'int')
774 );
775 $trow = $this->db->fetchAssoc($tresult);
776 return $trow['value1'];
777 }
778 return null;
779 }
780
781 public function toLog(AdditionalInformationGenerator $additional_info): array
782 {
783 return [
784 AdditionalInformationGenerator::KEY_QUESTION_TYPE => (string) $this->getQuestionType(),
785 AdditionalInformationGenerator::KEY_QUESTION_TITLE => $this->getTitleForHTMLOutput(),
786 AdditionalInformationGenerator::KEY_QUESTION_TEXT => $this->formatSAQuestion($this->getQuestion()),
787 AdditionalInformationGenerator::KEY_QUESTION_REACHABLE_POINTS => $this->getMaximumPoints(),
788 AdditionalInformationGenerator::KEY_QUESTION_TEXT_WORDCOUNT_ENABLED => $additional_info
789 ->getEnabledDisabledTagForBool($this->isWordCounterEnabled()),
790 AdditionalInformationGenerator::KEY_QUESTION_MAXCHARS => $this->getMaxNumOfChars(),
791 AdditionalInformationGenerator::KEY_QUESTION_TEXT_SCORING_MODE => $additional_info->getTagForLangVar(
792 $this->getScoringModeLangVar($this->getKeywordRelation())
793 ),
794 AdditionalInformationGenerator::KEY_QUESTION_CORRECT_ANSWER_OPTIONS => array_map(
795 fn(ASS_AnswerMultipleResponseImage $answer) => [
796 AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTION => $answer->getAnswertext(),
797 AdditionalInformationGenerator::KEY_QUESTION_REACHABLE_POINTS => $answer->getPoints() === 0.0 ? '' : $answer->getPoints()
798 ],
799 $this->getAnswers()
800 ),
801 AdditionalInformationGenerator::KEY_FEEDBACK => [
802 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_INCOMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
803 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_COMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
804 ]
805 ];
806 }
807
808 private function getScoringModeLangVar(string $scoring_mode): string
809 {
810 switch ($scoring_mode) {
812 return 'essay_scoring_mode_without_keywords';
814 return 'essay_scoring_mode_keyword_relation_any';
816 return 'essay_scoring_mode_keyword_relation_all';
818 return 'essay_scoring_mode_keyword_relation_one';
819 default:
820 return '';
821 }
822 }
823
824 protected function solutionValuesToLog(
825 AdditionalInformationGenerator $additional_info,
826 array $solution_values
827 ): string {
828 if (!array_key_exists(0, $solution_values)
829 || !array_key_exists('value1', $solution_values[0])) {
830 return '';
831 }
832 return $this->refinery->string()->stripTags()->transform(
833 html_entity_decode($solution_values[0]['value1'])
834 );
835 }
836
837 public function solutionValuesToText(array $solution_values): string
838 {
839 if (!array_key_exists(0, $solution_values)
840 || !array_key_exists('value1', $solution_values[0])) {
841 return '';
842 }
843 return $solution_values[0]['value1'];
844 }
845
846 public function getCorrectSolutionForTextOutput(int $active_id, int $pass): array|string
847 {
848 switch ($this->getKeywordRelation()) {
849 case self::SCORING_MODE_KEYWORD_RELATION_NONE:
850 return '';
851 default:
852 return array_map(
853 static fn(ASS_AnswerMultipleResponseImage $v): string => $v->getAnswertext(),
854 $this->getAnswers()
855 );
856 }
857 }
858}
$relation
ASS_AnswerBinaryStateImage is a class for answers with a binary state indicator (checked/unchecked,...
getPoints()
Gets the points.
getAnswertext()
Gets the answer text.
const TEXTGAP_RATING_CASESENSITIVE
const TEXTGAP_RATING_LEVENSHTEIN1
const TEXTGAP_RATING_LEVENSHTEIN5
const TEXTGAP_RATING_CASEINSENSITIVE
const TEXTGAP_RATING_LEVENSHTEIN4
const TEXTGAP_RATING_LEVENSHTEIN2
const TEXTGAP_RATING_LEVENSHTEIN3
setOriginalId(?int $original_id)
setId(int $id=-1)
Refinery $refinery
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
setShuffle(?bool $shuffle=true)
setQuestion(string $question="")
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
setAuthor(string $author="")
setComment(string $comment="")
setObjId(int $obj_id=0)
getSolutionMaxPass(int $active_id)
setOwner(int $owner=-1)
setNrOfTries(int $a_nr_of_tries)
setLifecycle(ilAssQuestionLifecycle $lifecycle)
setTitle(string $title="")
saveQuestionDataToDb(?int $original_id=null)
setPoints(float $points)
Class for text questions.
const SCORING_MODE_KEYWORD_RELATION_NONE
isKeywordMatching($answertext, $a_keyword)
Checks if one of the keywords matches the answertext.
toLog(AdditionalInformationGenerator $additional_info)
MUST return an array of the question settings that can be stored in the log.
setWordCounterEnabled(bool $word_counter_enabled)
getLatestAutosaveContent(int $active_id, int $pass)
setMaxNumOfChars(int $maxchars=0)
setKeywordRelation(?string $relation)
This method implements a default behaviour.
solutionValuesToText(array $solution_values)
MUST convert the given solution values into text.
saveWorkingData(int $active_id, ?int $pass=null, bool $authorized=true)
addAnswer( $answertext="", $points=0.0, $points_unchecked=0.0, $order=0, $answerimage="")
Adds a possible answer for a multiple choice question.
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
calculateReachedPointsForSolution(string $solution)
__construct(string $title="", string $comment="", string $author="", int $owner=-1, string $question="")
assTextQuestion constructor
solutionValuesToLog(AdditionalInformationGenerator $additional_info, array $solution_values)
MUST convert the given solution values into an array or a string that can be stored in the log.
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
getAnswer($index=0)
Returns an answer with a given index.
setMatchcondition(int $matchcondition)
calculateReachedPoints(int $active_id, ?int $pass=null, bool $authorized_solution=true)
getAdditionalTableName()
Returns the name of the additional question data table in the database.
static getScoringModesWithPointsByKeyword()
getScoringModeLangVar(string $scoring_mode)
toJSON()
Returns a JSON representation of the question.
flushAnswers()
Deletes all answers.
setTextRating($a_text_rating)
deleteAnswer($index=0)
Deletes an answer with a given index.
saveToDb(?int $original_id=null)
loadFromDb(int $question_id)
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
getCorrectSolutionForTextOutput(int $active_id, int $pass)
isValidTextRating($textRating)
static getScoringModesWithPointsByQuestion()
duplicateAnswers($original_id)
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
static strPos(string $a_haystack, string $a_needle, int $a_offset=0)
Definition: class.ilStr.php:39
static strToLower(string $a_string)
Definition: class.ilStr.php:69
static strLen(string $a_string)
Definition: class.ilStr.php:60
static isHTML(string $a_text)
Checks if a given string contains HTML or not.
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
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'))
global $DIC
Definition: shib_login.php:26