ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.assTextQuestion.php
Go to the documentation of this file.
1 <?php
2 
19 require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
20 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
21 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
22 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
23 
38 {
39  protected const HAS_SPECIFIC_FEEDBACK = false;
48 
53 
62  public $keywords;
63 
64  public $answers;
65 
71  public $text_rating;
72 
73  /* method for automatic string matching */
74  private $matchcondition;
75 
76  public $keyword_relation = 'any';
77 
90  public function __construct(
91  $title = "",
92  $comment = "",
93  $author = "",
94  $owner = -1,
95  $question = ""
96  ) {
98  $this->wordCounterEnabled = false;
99  $this->maxNumOfChars = 0;
100  $this->points = 1;
101  $this->answers = array();
102  $this->matchcondition = 0;
103  }
104 
105  public function getMatchcondition(): int
106  {
107  return $this->matchcondition;
108  }
109 
110  public function setMatchcondition(int $matchcondition): void
111  {
112  $this->matchcondition = $matchcondition;
113  }
114 
120  public function isComplete(): bool
121  {
122  if (strlen($this->title)
123  && $this->author
124  && $this->question
125  && $this->getMaximumPoints() > 0
126  ) {
127  return true;
128  }
129  return false;
130  }
131 
137  public function saveToDb($original_id = ""): void
138  {
139  if ($original_id == '') {
140  $this->saveQuestionDataToDb();
141  } else {
143  }
144 
147  parent::saveToDb();
148  }
149 
157  public function loadFromDb($question_id): void
158  {
159  global $DIC;
160  $ilDB = $DIC['ilDB'];
161 
162  $result = $ilDB->queryF(
163  "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",
164  array("integer"),
165  array($question_id)
166  );
167  if ($ilDB->numRows($result) == 1) {
168  $data = $ilDB->fetchAssoc($result);
169  $this->setId($question_id);
170  $this->setObjId($data["obj_fi"]);
171  $this->setTitle((string) $data["title"]);
172  $this->setComment((string) $data["description"]);
173  $this->setOriginalId($data["original_id"]);
174  $this->setNrOfTries($data['nr_of_tries']);
175  $this->setAuthor($data["author"]);
176  $this->setPoints((float) $data["points"]);
177  $this->setOwner($data["owner"]);
178  include_once("./Services/RTE/classes/class.ilRTE.php");
179  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc((string) $data["question_text"], 1));
180  $this->setShuffle(false);
181  $this->setWordCounterEnabled((bool) $data['word_cnt_enabled']);
182  $this->setMaxNumOfChars($data["maxnumofchars"]);
183  $this->setTextRating($this->isValidTextRating($data["textgap_rating"]) ? $data["textgap_rating"] : TEXTGAP_RATING_CASEINSENSITIVE);
184  $this->matchcondition = (strlen($data['matchcondition'])) ? (int) $data['matchcondition'] : 0;
185  $this->setKeywordRelation(($data['keyword_relation']));
186 
187  try {
188  $this->setLifecycle(ilAssQuestionLifecycle::getInstance($data['lifecycle']));
191  }
192 
193  try {
194  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
195  } catch (ilTestQuestionPoolException $e) {
196  }
197  }
198 
199  $result = $ilDB->queryF(
200  "SELECT * FROM qpl_a_essay WHERE question_fi = %s",
201  array("integer"),
202  array($this->getId())
203  );
204 
205  $this->flushAnswers();
206  while ($row = $ilDB->fetchAssoc($result)) {
207  $this->addAnswer($row['answertext'], $row['points']);
208  }
209 
210  parent::loadFromDb($question_id);
211  }
212 
218  public function duplicate(bool $for_test = true, string $title = "", string $author = "", string $owner = "", $testObjId = null): int
219  {
220  if ($this->id <= 0) {
221  // The question has not been saved. It cannot be duplicated
222  return -1;
223  }
224  // duplicate the question in database
225  $this_id = $this->getId();
226  $thisObjId = $this->getObjId();
227 
228  $clone = $this;
229  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
231  $clone->id = -1;
232 
233  if ((int) $testObjId > 0) {
234  $clone->setObjId($testObjId);
235  }
236 
237  if ($title) {
238  $clone->setTitle($title);
239  }
240 
241  if ($author) {
242  $clone->setAuthor($author);
243  }
244  if ($owner) {
245  $clone->setOwner($owner);
246  }
247 
248  if ($for_test) {
249  $clone->saveToDb($original_id);
250  } else {
251  $clone->saveToDb();
252  }
253 
254  // copy question page content
255  $clone->copyPageOfQuestion($this_id);
256  // copy XHTML media objects
257  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
258  #$clone->duplicateAnswers($this_id);
259 
260  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
261 
262  return $clone->id;
263  }
264 
270  public function copyObject($target_questionpool_id, $title = ""): int
271  {
272  if ($this->getId() <= 0) {
273  throw new RuntimeException('The question has not been saved. It cannot be duplicated');
274  }
275  // duplicate the question in database
276  $clone = $this;
277  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
279  $clone->id = -1;
280  $source_questionpool_id = $this->getObjId();
281  $clone->setObjId($target_questionpool_id);
282  if ($title) {
283  $clone->setTitle($title);
284  }
285  $clone->saveToDb();
286  // copy question page content
287  $clone->copyPageOfQuestion($original_id);
288  // copy XHTML media objects
289  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
290  // duplicate answers
291  #$clone->duplicateAnswers($original_id);
292 
293  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
294 
295  return $clone->id;
296  }
297 
298  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = ""): int
299  {
300  if ($this->getId() <= 0) {
301  throw new RuntimeException('The question has not been saved. It cannot be duplicated');
302  }
303 
304  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
305 
306  $sourceQuestionId = $this->id;
307  $sourceParentId = $this->getObjId();
308 
309  // duplicate the question in database
310  $clone = $this;
311  $clone->id = -1;
312 
313  $clone->setObjId($targetParentId);
314 
315  if ($targetQuestionTitle) {
316  $clone->setTitle($targetQuestionTitle);
317  }
318 
319  $clone->saveToDb();
320  // copy question page content
321  $clone->copyPageOfQuestion($sourceQuestionId);
322  // copy XHTML media objects
323  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
324  // duplicate answers
325  #$clone->duplicateAnswers($sourceQuestionId);
326 
327  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
328 
329  return $clone->id;
330  }
331 
339  public function getMaxNumOfChars(): int
340  {
341  if (strcmp($this->maxNumOfChars, "") == 0) {
342  return 0;
343  } else {
344  return $this->maxNumOfChars;
345  }
346  }
347 
355  public function setMaxNumOfChars($maxchars = 0): void
356  {
357  $this->maxNumOfChars = $maxchars;
358  }
359 
363  public function isWordCounterEnabled(): bool
364  {
366  }
367 
372  {
373  $this->wordCounterEnabled = $wordCounterEnabled;
374  }
375 
382  public function getMaximumPoints(): float
383  {
384  if (in_array($this->getKeywordRelation(), self::getScoringModesWithPointsByQuestion())) {
385  return parent::getPoints();
386  }
387 
388  $points = 0;
389 
390  foreach ($this->answers as $answer) {
391  if ($answer->getPoints() > 0) {
392  $points = $points + $answer->getPoints();
393  }
394  }
395 
396  return $points;
397  }
398 
399  public function getMinimumPoints()
400  {
401  if (in_array($this->getKeywordRelation(), self::getScoringModesWithPointsByQuestion())) {
402  return 0;
403  }
404 
405  $points = 0;
406 
407  foreach ($this->answers as $answer) {
408  if ($answer->getPoints() < 0) {
409  $points = $points + $answer->getPoints();
410  }
411  }
412 
413  return $points;
414  }
424  public function setReachedPoints($active_id, $points, $pass = null): bool
425  {
426  global $DIC;
427  $ilDB = $DIC['ilDB'];
428 
429  if (($points > 0) && ($points <= $this->getPoints())) {
430  if (is_null($pass)) {
431  $pass = $this->getSolutionMaxPass($active_id);
432  }
433  $affectedRows = $ilDB->manipulateF(
434  "UPDATE tst_test_result SET points = %s WHERE active_fi = %s AND question_fi = %s AND pass = %s",
435  array('float','integer','integer','integer'),
436  array($points, $active_id, $this->getId(), $pass)
437  );
438  self::_updateTestPassResults($active_id, $pass);
439  return true;
440  } else {
441  return true;
442  }
443  }
444 
445  private function isValidTextRating($textRating): bool
446  {
447  switch ($textRating) {
455  return true;
456  }
457 
458  return false;
459  }
460 
469  public function isKeywordMatching($answertext, $a_keyword): bool
470  {
471  global $DIC;
472  $refinery = $DIC->refinery();
473  $result = false;
474  $textrating = $this->getTextRating();
475  include_once "./Services/Utilities/classes/class.ilStr.php";
476 
477  switch ($textrating) {
479  if (ilStr::strPos(ilStr::strToLower($answertext), ilStr::strToLower($a_keyword)) !== false) {
480  return true;
481  }
482  break;
484  if (ilStr::strPos($answertext, $a_keyword) !== false) {
485  return true;
486  }
487  break;
488  }
489 
490  // "<p>red</p>" would not match "red" even with distance of 5
491  $answertext = strip_tags($answertext);
492  $answerwords = array();
493  if (preg_match_all("/([^\s.]+)/", $answertext, $matches)) {
494  foreach ($matches[1] as $answerword) {
495  array_push($answerwords, trim($answerword));
496  }
497  }
498 
499  // create correct transformation
500  switch ($textrating) {
502  $transformation = $refinery->string()->levenshtein()->standard($a_keyword, 1);
503  break;
505  $transformation = $refinery->string()->levenshtein()->standard($a_keyword, 2);
506  break;
508  $transformation = $refinery->string()->levenshtein()->standard($a_keyword, 3);
509  break;
511  $transformation = $refinery->string()->levenshtein()->standard($a_keyword, 4);
512  break;
514  $transformation = $refinery->string()->levenshtein()->standard($a_keyword, 5);
515  break;
516  }
517 
518  // run answers against Levenshtein methods
519  foreach ($answerwords as $a_original) {
520  if (isset($transformation) && $transformation->transform($a_original) >= 0) {
521  return true;
522  }
523  }
524  return $result;
525  }
526 
527  protected function calculateReachedPointsForSolution($solution)
528  {
529  $solution = html_entity_decode($solution);
530  // Return min points when keyword relation is NON KEYWORDS
531  if ($this->getKeywordRelation() == 'non') {
532  return $this->getMinimumPoints();
533  }
534 
535  // Return min points if there are no answers present.
536  $answers = $this->getAnswers();
537 
538  if (count($answers) == 0) {
539  return $this->getMinimumPoints();
540  }
541 
542  switch ($this->getKeywordRelation()) {
543  case 'any':
544  $points = 0;
545  foreach ($answers as $answer) {
546  $qst_answer = $answer->getAnswertext();
547  $user_answer = ' ' . $solution;
548  if ($this->isKeywordMatching($user_answer, $qst_answer)) {
549  $points += $answer->getPoints();
550  }
551  }
552  break;
553 
554  case 'all':
555  $points = $this->getMaximumPoints();
556  foreach ($answers as $answer) {
557  $qst_answer = $answer->getAnswertext();
558  $user_answer = ' ' . $solution;
559  if (!$this->isKeywordMatching($user_answer, $qst_answer)) {
560  $points = 0;
561  break;
562  }
563  }
564  break;
565 
566  case 'one':
567  $points = 0;
568  foreach ($answers as $answer) {
569  $qst_answer = $answer->getAnswertext();
570  $user_answer = ' ' . $solution;
571  if ($this->isKeywordMatching($user_answer, $qst_answer)) {
572  $points = $this->getMaximumPoints();
573  break;
574  }
575  }
576  break;
577  }
578 
579  return $points;
580  }
581 
592  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
593  {
594  if ($returndetails) {
595  throw new ilTestException('return details not implemented for ' . __METHOD__);
596  }
597 
598  global $DIC;
599  $ilDB = $DIC['ilDB'];
600 
601  if (is_null($pass)) {
602  $pass = $this->getSolutionMaxPass($active_id);
603  }
604 
605  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
606 
607  // Return min points when no answer was given.
608  if ($ilDB->numRows($result) == 0) {
609  return $this->getMinimumPoints();
610  }
611 
612  // Return points of points are already on the row.
613  $row = $ilDB->fetchAssoc($result);
614  if ($row["points"] != null) {
615  return $row["points"];
616  }
617 
618  return $this->calculateReachedPointsForSolution($row['value1']);
619  }
620 
629  public function saveWorkingData($active_id, $pass = null, $authorized = true): bool
630  {
631  global $DIC;
632  $ilDB = $DIC['ilDB'];
633  $ilUser = $DIC['ilUser'];
634 
635  include_once "./Services/Utilities/classes/class.ilStr.php";
636  if (is_null($pass)) {
637  include_once "./Modules/Test/classes/class.ilObjTest.php";
638  $pass = ilObjTest::_getPass($active_id);
639  }
640 
641  $entered_values = 0;
642  $text = $this->getSolutionSubmit();
643 
644  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized, $text) {
645  $this->removeCurrentSolution($active_id, $pass, $authorized);
646 
647  if (strlen($text)) {
648  $this->saveCurrentSolution($active_id, $pass, trim($text), null, $authorized);
649  $entered_values++;
650  }
651  });
652 
653  if ($entered_values) {
654  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
656  assQuestion::logAction($this->lng->txtlng(
657  "assessment",
658  "log_user_entered_values",
660  ), $active_id, $this->getId());
661  }
662  } else {
663  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
665  assQuestion::logAction($this->lng->txtlng(
666  "assessment",
667  "log_user_not_entered_values",
669  ), $active_id, $this->getId());
670  }
671  }
672 
673  return true;
674  }
675 
679  public function getSolutionSubmit()
680  {
681  if (ilObjAdvancedEditing::_getRichTextEditor() === 'tinymce') {
682  $text = ilUtil::stripSlashes($_POST["TEXT"], false);
683  } else {
684  $text = htmlentities($_POST["TEXT"]);
685  }
686 
687  if (ilUtil::isHTML($text)) {
688  $text = $this->getHtmlUserSolutionPurifier()->purify($text);
689  }
690 
691  return $text;
692  }
693 
694  public function saveAdditionalQuestionDataToDb()
695  {
697  global $DIC;
698  $ilDB = $DIC['ilDB'];
699  $ilDB->manipulateF(
700  "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
701  array( "integer" ),
702  array( $this->getId()
703  )
704  );
705 
706  $fields = array(
707  'question_fi' => array('integer', $this->getId()),
708  'maxnumofchars' => array('integer', $this->getMaxNumOfChars()),
709  'word_cnt_enabled' => array('integer', (int) $this->isWordCounterEnabled()),
710  'keywords' => array('text', null),
711  'textgap_rating' => array('text', $this->getTextRating()),
712  'matchcondition' => array('integer', $this->getMatchcondition()),
713  'keyword_relation' => array('text', $this->getKeywordRelation())
714  );
715 
716  $ilDB->insert($this->getAdditionalTableName(), $fields);
717  }
718 
719  public function saveAnswerSpecificDataToDb()
720  {
722  global $DIC;
723  $ilDB = $DIC['ilDB'];
724 
725  $ilDB->manipulateF(
726  "DELETE FROM qpl_a_essay WHERE question_fi = %s",
727  array( "integer" ),
728  array( $this->getId() )
729  );
730 
731  foreach ($this->answers as $answer) {
733  $nextID = $ilDB->nextId('qpl_a_essay');
734  $ilDB->manipulateF(
735  "INSERT INTO qpl_a_essay (answer_id, question_fi, answertext, points) VALUES (%s, %s, %s, %s)",
736  array( "integer", "integer", "text", 'float' ),
737  array(
738  $nextID,
739  $this->getId(),
740  $answer->getAnswertext(),
741  $answer->getPoints()
742  )
743  );
744  }
745  }
746 
753  public function getQuestionType(): string
754  {
755  return "assTextQuestion";
756  }
757 
765  public function getTextRating(): string
766  {
767  return $this->text_rating;
768  }
769 
777  public function setTextRating($a_text_rating): void
778  {
779  switch ($a_text_rating) {
787  $this->text_rating = $a_text_rating;
788  break;
789  default:
790  $this->text_rating = TEXTGAP_RATING_CASEINSENSITIVE;
791  break;
792  }
793  }
794 
801  public function getAdditionalTableName(): string
802  {
803  return "qpl_qst_essay";
804  }
805 
810  public function getRTETextWithMediaObjects(): string
811  {
812  return parent::getRTETextWithMediaObjects();
813  }
814 
818  public function setExportDetailsXLS(ilAssExcelFormatHelper $worksheet, int $startrow, int $active_id, int $pass): int
819  {
820  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
821 
822  $solutions = $this->getSolutionValues($active_id, $pass);
823 
824  $i = 1;
825  $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
826  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
827 
828  require_once 'Modules/Test/classes/class.ilObjAssessmentFolder.php';
829  $assessment_folder = new ilObjAssessmentFolder();
830 
831  $string_escaping_org_value = $worksheet->getStringEscaping();
832  if ($assessment_folder->getExportEssayQuestionsWithHtml()) {
833  $worksheet->setStringEscaping(false);
834  }
835 
836  if (array_key_exists(0, $solutions) && strlen($solutions[0]["value1"])) {
837  $worksheet->setCell($startrow + $i, 2, html_entity_decode($solutions[0]["value1"]));
838  }
839  $i++;
840 
841  $worksheet->setStringEscaping($string_escaping_org_value);
842  return $startrow + $i + 1;
843  }
844 
848  public function toJSON(): string
849  {
850  include_once("./Services/RTE/classes/class.ilRTE.php");
851  $result = array();
852  $result['id'] = $this->getId();
853  $result['type'] = (string) $this->getQuestionType();
854  $result['title'] = $this->getTitleForHTMLOutput();
855  $result['question'] = $this->formatSAQuestion($this->getQuestion());
856  $result['nr_of_tries'] = $this->getNrOfTries();
857  $result['shuffle'] = $this->getShuffle();
858  $result['maxlength'] = $this->getMaxNumOfChars();
859  return json_encode($result);
860  }
861 
862  public function getAnswerCount(): int
863  {
864  return count($this->answers);
865  }
866 
880  public function addAnswer(
881  $answertext = "",
882  $points = 0.0,
883  $points_unchecked = 0.0,
884  $order = 0,
885  $answerimage = ""
886  ): void {
887  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMultipleResponseImage.php";
888 
889  // add answer
890  $answer = new ASS_AnswerMultipleResponseImage($answertext, $points);
891  $this->answers[] = $answer;
892  }
893 
894  public function getAnswers(): array
895  {
896  return $this->answers;
897  }
898 
908  public function getAnswer($index = 0): ?object
909  {
910  if ($index < 0) {
911  return null;
912  }
913  if (count($this->answers) < 1) {
914  return null;
915  }
916  if ($index >= count($this->answers)) {
917  return null;
918  }
919 
920  return $this->answers[$index];
921  }
922 
931  public function deleteAnswer($index = 0): void
932  {
933  if ($index < 0) {
934  return;
935  }
936  if (count($this->answers) < 1) {
937  return;
938  }
939  if ($index >= count($this->answers)) {
940  return;
941  }
942  $answer = $this->answers[$index];
943  //if (strlen($answer->getImage())) {
944  // $this->deleteImage($answer->getImage());
945  //}
946  unset($this->answers[$index]);
947  $this->answers = array_values($this->answers);
948  for ($i = 0, $iMax = count($this->answers); $i < $iMax; $i++) {
949  if ($this->answers[$i]->getOrder() > $index) {
950  $this->answers[$i]->setOrder($i);
951  }
952  }
953  }
954 
955  public function getAnswerTableName(): string
956  {
957  return 'qpl_a_essay';
958  }
959 
966  public function flushAnswers(): void
967  {
968  $this->answers = array();
969  }
970 
971  public function setAnswers($answers): void
972  {
973  if (isset($answers['answer'])) {
974  $count = count($answers['answer']);
975  $withPoints = true;
976  } else {
977  $count = count($answers);
978  $withPoints = false;
979  }
980 
981  $this->flushAnswers();
982 
983  for ($i = 0; $i < $count; $i++) {
984  if ($withPoints) {
985  $this->addAnswer($answers['answer'][$i], $answers['points'][$i]);
986  } else {
987  $this->addAnswer($answers[$i], 0);
988  }
989  }
990  }
991 
992  public function duplicateAnswers($original_id): void
993  {
994  global $DIC;
995  $ilDB = $DIC['ilDB'];
996 
997  $result = $ilDB->queryF(
998  "SELECT * FROM qpl_a_essay WHERE question_fi = %s",
999  array('integer'),
1000  array($original_id)
1001  );
1002  if ($result->numRows()) {
1003  while ($row = $ilDB->fetchAssoc($result)) {
1004  $next_id = $ilDB->nextId('qpl_a_essay');
1005  $affectedRows = $ilDB->manipulateF(
1006  "INSERT INTO qpl_a_essay (answer_id, question_fi, answertext, points)
1007  VALUES (%s, %s, %s, %s)",
1008  array('integer','integer','text','float'),
1009  array($next_id, $this->getId(), $row["answertext"], $row["points"])
1010  );
1011  }
1012  }
1013  }
1014 
1015  public function getKeywordRelation()
1016  {
1017  return $this->keyword_relation;
1018  }
1019 
1024  public function setKeywordRelation($a_relation): void
1025  {
1026  $this->keyword_relation = $a_relation;
1027  }
1028 
1029  public static function getValidScoringModes(): array
1030  {
1031  return array_merge(self::getScoringModesWithPointsByQuestion(), self::getScoringModesWithPointsByKeyword());
1032  }
1033 
1034  public static function getScoringModesWithPointsByQuestion(): array
1035  {
1036  return array('non', 'all', 'one');
1037  }
1038 
1039  public static function getScoringModesWithPointsByKeyword(): array
1040  {
1041  return array('any');
1042  }
1043 
1044 
1055  public function isAnswered(int $active_id, int $pass): bool
1056  {
1057  $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1058 
1059  return $numExistingSolutionRecords > 0;
1060  }
1061 
1072  public static function isObligationPossible(int $questionId): bool
1073  {
1074  return true;
1075  }
1076 
1077  public function countLetters($text): int
1078  {
1079  $text = strip_tags($text);
1080 
1081  $text = str_replace('&gt;', '>', $text);
1082  $text = str_replace('&lt;', '<', $text);
1083  $text = str_replace('&nbsp;', ' ', $text);
1084  $text = str_replace('&amp;', '&', $text);
1085 
1086  $text = str_replace("\r\n", "\n", $text);
1087  $text = str_replace("\n", "", $text);
1088 
1089  return ilStr::strLen($text);
1090  }
1091 
1092  public function countWords($text): int
1093  {
1094  if ($text === '') {
1095  return 0;
1096  }
1097  $text = str_replace('&nbsp;', ' ', $text);
1098 
1099  $text = preg_replace('/[.,:;!?\-_#\'"+*\\/=()&%§$]/m', '', $text);
1100 
1101  $text = preg_replace('/^\s*/m', '', $text);
1102  $text = preg_replace('/\s*$/m', '', $text);
1103  $text = preg_replace('/\s+/m', ' ', $text);
1104 
1105  return count(explode(' ', $text));
1106  }
1107 
1108  public function getLatestAutosaveContent(int $active_id, int $pass): ?string
1109  {
1110  // Do we have an unauthorized result?
1111  $cntresult = $this->db->query(
1112  '
1113  SELECT count(solution_id) cnt
1114  FROM tst_solutions
1115  WHERE active_fi = ' . $this->db->quote($active_id, 'int') . '
1116  AND question_fi = ' . $this->db->quote($this->getId(), 'int') . '
1117  AND authorized = ' . $this->db->quote(0, 'int')
1118  . ' AND pass = ' . $this->db->quote($pass, 'int')
1119  );
1120  $row = $this->db->fetchAssoc($cntresult);
1121  if ($row['cnt'] > 0) {
1122  $tresult = $this->db->query(
1123  '
1124  SELECT value1
1125  FROM tst_solutions
1126  WHERE active_fi = ' . $this->db->quote($active_id, 'int') . '
1127  AND question_fi = ' . $this->db->quote($this->getId(), 'int') . '
1128  AND authorized = ' . $this->db->quote(0, 'int')
1129  . ' AND pass = ' . $this->db->quote($pass, 'int')
1130  );
1131  $trow = $this->db->fetchAssoc($tresult);
1132  return $trow['value1'];
1133  }
1134  return null;
1135  }
1136 }
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...
getSolutionValues($active_id, $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
setNrOfTries(int $a_nr_of_tries)
const TEXTGAP_RATING_LEVENSHTEIN5
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static getScoringModesWithPointsByKeyword()
static isObligationPossible(int $questionId)
returns boolean wether it is possible to set this question type as obligatory or not considering the ...
getLatestAutosaveContent(int $active_id, int $pass)
static _getRichTextEditor()
Returns the identifier for the Rich Text Editor.
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
const TEXTGAP_RATING_LEVENSHTEIN2
static isHTML(string $a_text)
Checks if a given string contains HTML or not.
const TEXTGAP_RATING_LEVENSHTEIN1
Abstract basic class which is to be extended by the concrete assessment question type classes...
setOwner(int $owner=-1)
isComplete()
Returns true, if a multiple choice question is complete for use.
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
getColumnCoord(int $a_col)
Get column "name" from number.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setExportDetailsXLS(ilAssExcelFormatHelper $worksheet, int $startrow, int $active_id, int $pass)
{}
loadFromDb($question_id)
Loads a assTextQuestion object from a database.
saveToDb($original_id="")
Saves a assTextQuestion object to a database.
static strPos(string $a_haystack, string $a_needle, ?int $a_offset=null)
Definition: class.ilStr.php:42
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
copyObject($target_questionpool_id, $title="")
Copies an assTextQuestion object.
static _getOriginalId(int $question_id)
static getNumExistingSolutionRecords(int $activeId, int $pass, int $questionId)
toJSON()
Returns a JSON representation of the question.
setCell($a_row, $a_col, $a_value, $datatype=null)
setComment(string $comment="")
setWordCounterEnabled($wordCounterEnabled)
float $points
The maximum available points for the question.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
$index
Definition: metadata.php:145
global $DIC
Definition: feed.php:28
static strLen(string $a_string)
Definition: class.ilStr.php:63
duplicate(bool $for_test=true, string $title="", string $author="", string $owner="", $testObjId=null)
Duplicates an assTextQuestion.
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
assTextQuestion constructor
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
setBold(string $a_coords)
Set cell(s) to bold.
isValidTextRating($textRating)
const TEXTGAP_RATING_LEVENSHTEIN3
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
calculateReachedPointsForSolution($solution)
setMatchcondition(int $matchcondition)
duplicateAnswers($original_id)
flushAnswers()
Deletes all answers.
static logAction(string $logtext, int $active_id, int $question_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getScoringModesWithPointsByQuestion()
const TEXTGAP_RATING_CASESENSITIVE
setKeywordRelation($a_relation)
This method implements a default behaviour.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setTextRating($a_text_rating)
Sets the rating option for text comparisons.
setMaxNumOfChars($maxchars=0)
Sets the maximum number of characters for the text solution.
getQuestionType()
Returns the question type of the question.
setReachedPoints($active_id, $points, $pass=null)
Sets the points, a learner has reached answering the question.
isKeywordMatching($answertext, $a_keyword)
Checks if one of the keywords matches the answertext.
setPoints(float $points)
setObjId(int $obj_id=0)
getAdditionalTableName()
Returns the name of the additional question data table in the database.
string $question
The question text.
getAnswer($index=0)
Returns an answer with a given index.
deleteAnswer($index=0)
Deletes an answer with a given index.
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
addAnswer( $answertext="", $points=0.0, $points_unchecked=0.0, $order=0, $answerimage="")
Adds a possible answer for a multiple choice question.
saveQuestionDataToDb(int $original_id=-1)
getMaxNumOfChars()
Gets the maximum number of characters for the text solution.
getSolutionMaxPass(int $active_id)
removeCurrentSolution(int $active_id, int $pass, bool $authorized=true)
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setId(int $id=-1)
__construct(Container $dic, ilPlugin $plugin)
setOriginalId(?int $original_id)
const TEXTGAP_RATING_LEVENSHTEIN4
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$ilUser
Definition: imgupload.php:34
setTitle(string $title="")
static strToLower(string $a_string)
Definition: class.ilStr.php:72
setLifecycle(ilAssQuestionLifecycle $lifecycle)
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
isAnswered(int $active_id, int $pass)
returns boolean wether the question is answered during test pass or not
getTextRating()
Returns the rating option for text comparisons.
setAuthor(string $author="")
setShuffle(?bool $shuffle=true)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
ILIAS Refinery Factory $refinery
$i
Definition: metadata.php:41
const TEXTGAP_RATING_CASEINSENSITIVE
setQuestion(string $question="")