ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.assLongMenu.php
Go to the documentation of this file.
1 <?php
2 
19 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
20 
22 {
23  private ?array $answerType = null;
24  private $long_menu_text;
25  private $json_structure;
26  private $ilDB;
30 
31  public const ANSWER_TYPE_SELECT_VAL = 0;
32  public const ANSWER_TYPE_TEXT_VAL = 1;
33  public const GAP_PLACEHOLDER = 'Longmenu';
34  public const MIN_LENGTH_AUTOCOMPLETE = 3;
35  public const MAX_INPUT_FIELDS = 500;
36 
37  protected const HAS_SPECIFIC_FEEDBACK = false;
38 
40  private $correct_answers = [];
41 
43  private $answers = [];
44 
45  public function __construct(
46  $title = "",
47  $comment = "",
48  $author = "",
49  $owner = -1,
50  $question = ""
51  ) {
52  global $DIC;
54  $this->minAutoComplete = self::MIN_LENGTH_AUTOCOMPLETE;
56  $this->ilDB = $DIC->database();
57  $this->identical_scoring = 1;
58  }
59 
60  public function getAnswerType(): ?array
61  {
62  return $this->answerType;
63  }
64 
65  public function setAnswerType(array $answerType): void
66  {
67  $this->answerType = $answerType;
68  }
69 
73  public function getCorrectAnswers()
74  {
76  }
77 
78 
79  public function setCorrectAnswers($correct_answers): void
80  {
81  $this->correct_answers = $correct_answers;
82  }
83 
84  private function buildFolderName(): string
85  {
86  return ilFileUtils::getDataDir() . '/assessment/longMenuQuestion/' . $this->getId() . '/' ;
87  }
88 
89  public function getAnswerTableName(): string
90  {
91  return "qpl_a_lome";
92  }
93 
94  private function buildFileName($gap_id): ?string
95  {
96  try {
97  $this->assertDirExists();
98  return $this->buildFolderName() . $gap_id . '.txt';
99  } catch (ilException $e) {
100  }
101  return null;
102  }
103 
104  public function setLongMenuTextValue($long_menu_text = ""): void
105  {
106  $this->long_menu_text = $this->getHtmlQuestionContentPurifier()->purify($long_menu_text);
107  }
108 
109  public function getLongMenuTextValue()
110  {
111  return $this->long_menu_text;
112  }
113 
114  public function setAnswers($answers): void
115  {
116  $this->answers = $answers;
117  }
118 
119  public function getAnswers(): array
120  {
121  return $this->answers;
122  }
123 
127  public function getJsonStructure()
128  {
129  return $this->json_structure;
130  }
131 
135  public function setJsonStructure($json_structure): void
136  {
137  $this->json_structure = $json_structure;
138  }
139 
141  {
142  $this->specificFeedbackSetting = $specificFeedbackSetting;
143  }
144 
145  public function getSpecificFeedbackSetting(): int
146  {
148  }
149 
150  public function setMinAutoComplete($minAutoComplete): void
151  {
152  $this->minAutoComplete = $minAutoComplete;
153  }
154 
155  public function getMinAutoComplete(): int
156  {
157  return $this->minAutoComplete ? $this->minAutoComplete : self::MIN_LENGTH_AUTOCOMPLETE;
158  }
159 
160  public function isComplete(): bool
161  {
162  if (strlen($this->title)
163  && $this->author
164  && $this->long_menu_text
165  && sizeof($this->answers) > 0
166  && sizeof($this->correct_answers) > 0
167  && $this->getPoints() > 0
168  ) {
169  return true;
170  }
171  return false;
172  }
173 
174  public function saveToDb(int $original_id = -1): void
175  {
179  parent::saveToDb();
180  }
181 
186  public function checkQuestionCustomPart($form = null): bool
187  {
188  $hidden_text_files = $this->getAnswers();
190  $points = array();
191  if ($correct_answers === null || sizeof($correct_answers) == 0 || $hidden_text_files === null || sizeof($hidden_text_files) == 0) {
192  return false;
193  }
194  if (sizeof($correct_answers) != sizeof($hidden_text_files)) {
195  return false;
196  }
197  foreach ($correct_answers as $key => $correct_answers_row) {
198  if ($this->correctAnswerDoesNotExistInAnswerOptions($correct_answers_row, $hidden_text_files[$key])) {
199  return false;
200  }
201  if (!is_array($correct_answers_row[0]) || sizeof($correct_answers_row[0]) == 0) {
202  return false;
203  }
204  if ($correct_answers_row[1] > 0) {
205  array_push($points, $correct_answers_row[1]);
206  }
207  }
208  if (sizeof($correct_answers) != sizeof($points)) {
209  return false;
210  }
211 
212  foreach ($points as $row) {
213  if ($row <= 0) {
214  return false;
215  }
216  }
217  return true;
218  }
219 
225  private function correctAnswerDoesNotExistInAnswerOptions($answers, $answer_options): bool
226  {
227  foreach ($answers[0] as $key => $answer) {
228  if (!in_array($answer, $answer_options)) {
229  return true;
230  }
231  }
232  return false;
233  }
234 
235 
242  public function getMaximumPoints(): float
243  {
244  $sum = 0;
245  $points = $this->getCorrectAnswers();
246  if ($points) {
247  foreach ($points as $add) {
248  $sum += (float) $add[1];
249  }
250  }
251  return $sum;
252  }
253 
255  {
256  // save additional data
257  $this->ilDB->manipulateF(
258  "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
259  array( "integer" ),
260  array( $this->getId() )
261  );
262  $this->ilDB->manipulateF(
263  "INSERT INTO " . $this->getAdditionalTableName(
264  ) . " (question_fi, long_menu_text, feedback_setting, min_auto_complete, identical_scoring) VALUES (%s, %s, %s, %s, %s)",
265  array( "integer", "text", "integer", "integer", "integer"),
266  array(
267  $this->getId(),
268  $this->getLongMenuTextValue(),
270  $this->getMinAutoComplete(),
271  $this->getIdenticalScoring()
272  )
273  );
274 
275  $this->createFileFromArray();
276  }
277 
278  public function saveAnswerSpecificDataToDb(): void
279  {
280  $this->clearAnswerSpecificDataFromDb($this->getId());
281  $type_array = $this->getAnswerType();
282  $points = 0;
283  foreach ($this->getCorrectAnswers() as $gap_number => $gap) {
284  foreach ($gap[0] as $position => $answer) {
285  if ($type_array == null) {
286  $type = $gap[2];
287  } else {
288  $type = $type_array[$gap_number];
289  }
290  $this->db->replace(
291  $this->getAnswerTableName(),
292  array(
293  'question_fi' => array('integer', $this->getId()),
294  'gap_number' => array('integer', (int) $gap_number),
295  'position' => array('integer', (int) $position)
296  ),
297  array(
298  'answer_text' => array('text', $answer),
299  'points' => array('float', (float) str_replace(',', '.', $gap[1])),
300  'type' => array('integer', (int) $type)
301  )
302  );
303  }
304  $points += (float) str_replace(',', '.', $gap[1]);
305  }
306  $this->setPoints($points);
307  }
308 
309  private function createFileFromArray(): void
310  {
311  $array = $this->getAnswers();
312  $this->clearFolder();
313  foreach ($array as $gap => $values) {
314  $file_content = '';
315  if (is_array($values)) {
316  foreach ($values as $key => $value) {
317  $file_content .= $value . "\n";
318  }
319  $file_content = rtrim($file_content, "\n");
320  $file = fopen($this->buildFileName($gap), "w");
321  fwrite($file, $file_content);
322  fclose($file);
323  }
324  }
325  }
326 
327  private function createArrayFromFile(): array
328  {
329  $files = glob($this->buildFolderName() . '*.txt');
330 
331  if ($files === false) {
332  $files = array();
333  }
334 
335  $answers = array();
336 
337  foreach ($files as $file) {
338  $gap = str_replace('.txt', '', basename($file));
339  $answers[(int) $gap] = explode("\n", file_get_contents($file));
340  }
341  // Sort by gap keys, to ensure the numbers are in ascending order.
342  // Glob will report the keys in files order like 0, 1, 10, 11, 2,...
343  // json_encoding the array with keys in order 0,1,10,11,2,.. will create
344  // a json_object instead of a list when keys are numeric, sorted and start with 0
345  ksort($answers);
346  $this->setAnswers($answers);
347  return $answers;
348  }
349 
350  private function clearFolder($let_folder_exists = true): void
351  {
352  ilFileUtils::delDir($this->buildFolderName(), $let_folder_exists);
353  }
354 
355  private function assertDirExists(): void
356  {
357  $folder_name = $this->buildFolderName();
358  if (!ilFileUtils::makeDirParents($folder_name)) {
359  throw new ilException('Cannot create export directory');
360  }
361 
362  if (
363  !is_dir($folder_name) ||
364  !is_readable($folder_name) ||
365  !is_writable($folder_name)
366  ) {
367  throw new ilException('Cannot create export directory');
368  }
369  }
370 
371  public function loadFromDb($question_id): void
372  {
373  $result = $this->ilDB->queryF(
374  "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",
375  array("integer"),
376  array($question_id)
377  );
378  if ($result->numRows() == 1) {
379  $data = $this->ilDB->fetchAssoc($result);
380  $this->setId($question_id);
381  $this->setObjId($data["obj_fi"]);
382  $this->setNrOfTries($data['nr_of_tries']);
383  $this->setTitle((string) $data["title"]);
384  $this->setComment((string) $data["description"]);
385  $this->setOriginalId($data["original_id"]);
386  $this->setAuthor($data["author"]);
387  $this->setPoints($data["points"]);
388  $this->setIdenticalScoring($data["identical_scoring"]);
389  $this->setOwner($data["owner"]);
390  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc((string) $data['question_text'], 1));
391  $this->setLongMenuTextValue(ilRTE::_replaceMediaObjectImageSrc((string) $data['long_menu_text'], 1));
392  $this->loadCorrectAnswerData($question_id);
393  $this->setMinAutoComplete($data["min_auto_complete"]);
394  if (isset($data['feedback_setting'])) {
395  $this->setSpecificFeedbackSetting((int) $data['feedback_setting']);
396  }
397 
398  try {
399  $this->setLifecycle(ilAssQuestionLifecycle::getInstance($data['lifecycle']));
402  }
403 
404  try {
405  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
406  } catch (ilTestQuestionPoolException $e) {
407  }
408  }
409 
410  $this->loadCorrectAnswerData($question_id);
411  $this->createArrayFromFile();
412  parent::loadFromDb($question_id);
413  }
414 
415  private function loadCorrectAnswerData($question_id): void
416  {
417  $res = $this->db->queryF(
418  "SELECT * FROM {$this->getAnswerTableName()} WHERE question_fi = %s ORDER BY gap_number, position ASC",
419  array('integer'),
420  array($question_id)
421  );
422 
423  $correct_answers = array();
424  while ($data = $this->ilDB->fetchAssoc($res)) {
425  $correct_answers[$data['gap_number']][0][$data['position']] = rtrim($data['answer_text']);
426  $correct_answers[$data['gap_number']][1] = $data['points'];
427  $correct_answers[$data['gap_number']][2] = $data['type'];
428  }
429  $this->setJsonStructure(json_encode($correct_answers));
431  }
432 
433  public function getCorrectAnswersForQuestionSolution($question_id): array
434  {
435  $correct_answers = array();
436  $res = $this->db->queryF(
437  'SELECT gap_number, answer_text FROM ' . $this->getAnswerTableName() . ' WHERE question_fi = %s',
438  array('integer'),
439  array($question_id)
440  );
441  while ($data = $this->ilDB->fetchAssoc($res)) {
442  if (array_key_exists($data['gap_number'], $correct_answers)) {
443  $correct_answers[$data['gap_number']] .= ' ' . $this->lng->txt("or") . ' ';
444  $correct_answers[$data['gap_number']] .= rtrim($data['answer_text']);
445  } else {
446  $correct_answers[$data['gap_number']] = rtrim($data['answer_text']);
447  }
448  }
449  return $correct_answers;
450  }
451 
452  private function getCorrectAnswersForGap($question_id, $gap_id): array
453  {
454  $correct_answers = array();
455  $res = $this->db->queryF(
456  'SELECT answer_text FROM ' . $this->getAnswerTableName() . ' WHERE question_fi = %s AND gap_number = %s',
457  array('integer', 'integer'),
458  array($question_id, $gap_id)
459  );
460  while ($data = $this->ilDB->fetchAssoc($res)) {
461  $correct_answers[] = rtrim($data['answer_text']);
462  }
463  return $correct_answers;
464  }
465 
466  private function getPointsForGap($question_id, $gap_id): float
467  {
468  $points = 0.0;
469  $res = $this->db->queryF(
470  'SELECT points FROM ' . $this->getAnswerTableName() . ' WHERE question_fi = %s AND gap_number = %s GROUP BY gap_number, points',
471  array('integer', 'integer'),
472  array($question_id, $gap_id)
473  );
474  while ($data = $this->ilDB->fetchAssoc($res)) {
475  $points = (float) $data['points'];
476  }
477  return $points;
478  }
479 
480 
481  public function getAnswersObject()
482  {
483  return json_encode($this->createArrayFromFile());
484  }
485 
486  public function getCorrectAnswersAsJson()
487  {
488  $this->loadCorrectAnswerData($this->getId());
489  return $this->getJsonStructure();
490  }
491 
492  public function duplicate(bool $for_test = true, string $title = "", string $author = "", int $owner = -1, $testObjId = null): int
493  {
494  if ($this->id <= 0) {
495  // The question has not been saved. It cannot be duplicated
496  return -1;
497  }
498 
499  // duplicate the question in database
500  $this_id = $this->getId();
501  $thisObjId = $this->getObjId();
502 
503  $clone = $this;
504 
505  $original_id = $this->questioninfo->getOriginalId($this->id);
506  $clone->id = -1;
507 
508  if ((int) $testObjId > 0) {
509  $clone->setObjId($testObjId);
510  }
511 
512  if ($title) {
513  $clone->setTitle($title);
514  }
515 
516  if ($author) {
517  $clone->setAuthor($author);
518  }
519  if ($owner) {
520  $clone->setOwner($owner);
521  }
522 
523  if ($for_test) {
524  $clone->saveToDb($original_id);
525  } else {
526  $clone->saveToDb();
527  }
528 
529  $clone->copyPageOfQuestion($this_id);
530  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
531  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
532 
533  return $clone->id;
534  }
535 
536  public function copyObject($target_questionpool_id, $title = ""): int
537  {
538  if ($this->getId() <= 0) {
539  throw new RuntimeException('The question has not been saved. It cannot be duplicated');
540  }
541  // duplicate the question in database
542  $clone = $this;
543 
544  $original_id = $this->questioninfo->getOriginalId($this->id);
545  $clone->id = -1;
546  $source_questionpool_id = $this->getObjId();
547  $clone->setObjId($target_questionpool_id);
548  if ($title) {
549  $clone->setTitle($title);
550  }
551  $clone->saveToDb();
552 
553  $clone->copyPageOfQuestion($original_id);
554  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
555 
556  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
557 
558  return $clone->id;
559  }
560 
561  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = ""): int
562  {
563  if ($this->getId() <= 0) {
564  throw new RuntimeException('The question has not been saved. It cannot be duplicated');
565  }
566 
567  $sourceQuestionId = $this->id;
568  $sourceParentId = $this->getObjId();
569 
570  // duplicate the question in database
571  $clone = $this;
572  $clone->id = -1;
573 
574  $clone->setObjId($targetParentId);
575 
576  if ($targetQuestionTitle) {
577  $clone->setTitle($targetQuestionTitle);
578  }
579 
580  $clone->saveToDb();
581  $clone->copyPageOfQuestion($sourceQuestionId);
582  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
583 
584  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
585 
586  return $clone->id;
587  }
588 
589 
601  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false): float
602  {
603  if ($returndetails) {
604  throw new ilTestException('return details not implemented for ' . __METHOD__);
605  }
606 
607  $found_values = array();
608  if (is_null($pass)) {
609  $pass = $this->getSolutionMaxPass($active_id);
610  }
611  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
612  while ($data = $this->ilDB->fetchAssoc($result)) {
613  $found_values[(int) $data['value1']] = $data['value2'];
614  }
615 
616  return $this->calculateReachedPointsForSolution($found_values, $active_id);
617  }
618 
619  protected function calculateReachedPointsForSolution($found_values, $active_id = 0): float
620  {
621  if ($found_values == null) {
622  $found_values = [];
623  }
624  $points = 0.0;
625  $solution_values_text = array();
626  foreach ($found_values as $key => $answer) {
627  if ($answer != '') {
628  $correct_answers = $this->getCorrectAnswersForGap($this->id, $key);
629  if (in_array($answer, $correct_answers)) {
630  $points_gap = $this->getPointsForGap($this->id, $key);
631  if (!$this->getIdenticalScoring()) {
632  // check if the same solution text was already entered
633  if ((in_array($answer, $solution_values_text)) && ($points > 0)) {
634  $points_gap = 0;
635  }
636  }
637  $points += $points_gap;
638  array_push($solution_values_text, $answer);
639  }
640  }
641  }
642  return $points;
643  }
644 
645  public function saveWorkingData(int $active_id, int $pass = null, bool $authorized = true): bool
646  {
647  if (is_null($pass)) {
648  $pass = ilObjTest::_getPass($active_id);
649  }
650 
651  $entered_values = 0;
652 
653  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
654  $this->removeCurrentSolution($active_id, $pass, $authorized);
655 
656  foreach ($this->getSolutionSubmit() as $val1 => $val2) {
657  $value = ilUtil::stripSlashes(trim($val2), false);
658  if (strlen($value)) {
659  $this->saveCurrentSolution($active_id, $pass, $val1, $value, $authorized);
660  $entered_values++;
661  }
662  }
663  });
664 
665  if ($entered_values) {
667  assQuestion::logAction($this->lng->txtlng(
668  "assessment",
669  "log_user_entered_values",
671  ), $active_id, $this->getId());
672  }
673  } else {
675  assQuestion::logAction($this->lng->txtlng(
676  "assessment",
677  "log_user_not_entered_values",
679  ), $active_id, $this->getId());
680  }
681  }
682  return true;
683  }
684 
685  // fau: testNav - overridden function lookupForExistingSolutions (specific for long menu question: ignore unselected values)
690  public function lookupForExistingSolutions(int $activeId, int $pass): array
691  {
692  global $DIC;
693  $ilDB = $DIC['ilDB'];
694 
695  $return = array(
696  'authorized' => false,
697  'intermediate' => false
698  );
699 
700  $query = "
701  SELECT authorized, COUNT(*) cnt
702  FROM tst_solutions
703  WHERE active_fi = " . $ilDB->quote($activeId, 'integer') . "
704  AND question_fi = " . $ilDB->quote($this->getId(), 'integer') . "
705  AND pass = " . $ilDB->quote($pass, 'integer') . "
706  AND value2 <> '-1'
707  ";
708 
709  if ($this->getStep() !== null) {
710  $query .= " AND step = " . $ilDB->quote((int) $this->getStep(), 'integer') . " ";
711  }
712 
713  $query .= "
714  GROUP BY authorized
715  ";
716 
717  $result = $ilDB->query($query);
718 
719  while ($row = $ilDB->fetchAssoc($result)) {
720  if ($row['authorized']) {
721  $return['authorized'] = $row['cnt'] > 0;
722  } else {
723  $return['intermediate'] = $row['cnt'] > 0;
724  }
725  }
726  return $return;
727  }
728  // fau.
729 
730 
731  public function getSolutionSubmit(): array
732  {
733  $solutionSubmit = array();
734  $answer = ilArrayUtil::stripSlashesRecursive($_POST['answer']);
735 
736  foreach ($answer as $key => $value) {
737  $solutionSubmit[$key] = $value;
738  }
739 
740  return $solutionSubmit;
741  }
742 
743  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession): void
744  {
745  $answer = $_POST['answer'] ?? null;
746  if (is_array($answer)) {
747  $answer = array_map(function ($value) {
748  return trim($value);
749  }, $answer);
750  }
751  $previewSession->setParticipantsSolution($answer);
752  }
753 
759  public function getQuestionType(): string
760  {
761  return "assLongMenu";
762  }
763 
764  public function getAdditionalTableName(): string
765  {
766  return 'qpl_qst_lome';
767  }
768 
773  public function getRTETextWithMediaObjects(): string
774  {
775  return parent::getRTETextWithMediaObjects() . $this->getLongMenuTextValue();
776  }
777 
781  public function setExportDetailsXLSX(ilAssExcelFormatHelper $worksheet, int $startrow, int $col, int $active_id, int $pass): int
782  {
783  parent::setExportDetailsXLSX($worksheet, $startrow, $col, $active_id, $pass);
784 
785  $solution = $this->getSolutionValues($active_id, $pass);
786 
787  $i = 1;
788  foreach ($this->getCorrectAnswers() as $gap_index => $gap) {
789  $worksheet->setCell($startrow + $i, $col, $this->lng->txt('assLongMenu') . " $i");
790  $worksheet->setBold($worksheet->getColumnCoord($col) . ($startrow + $i));
791  foreach ($solution as $solutionvalue) {
792  if ($gap_index == $solutionvalue["value1"]) {
793  switch ($gap[2]) {
794  case self::ANSWER_TYPE_SELECT_VAL:
795  $value = $solutionvalue["value2"];
796  if ($value == -1) {
797  $value = '';
798  }
799  $worksheet->setCell($startrow + $i, $col + 2, $value);
800  break;
801  case self::ANSWER_TYPE_TEXT_VAL:
802  $worksheet->setCell($startrow + $i, $col + 2, $solutionvalue["value2"]);
803  break;
804  }
805  }
806  }
807  $i++;
808  }
809 
810  return $startrow + $i + 1;
811  }
812 
821  public function getUserQuestionResult($active_id, $pass): ilUserQuestionResult
822  {
823  $result = new ilUserQuestionResult($this, $active_id, $pass);
824 
825  $points = $this->calculateReachedPoints($active_id, $pass);
826  $max_points = $this->getMaximumPoints();
827 
828  $result->setReachedPercentage(($points / $max_points) * 100);
829 
830  return $result;
831  }
832 
841  public function getAvailableAnswerOptions($index = null)
842  {
843  return $this->createArrayFromFile();
844  }
845 
846  public function isShuffleAnswersEnabled(): bool
847  {
848  return false;
849  }
850 
851  public function clearAnswerSpecificDataFromDb($question_id): void
852  {
853  $this->ilDB->manipulateF(
854  'DELETE FROM ' . $this->getAnswerTableName() . ' WHERE question_fi = %s',
855  array( 'integer' ),
856  array( $question_id )
857  );
858  }
859 
860  public function delete(int $question_id): void
861  {
862  parent::delete($question_id);
863  $this->clearFolder(false);
864  }
865 
870  {
871  $this->setLongMenuTextValue($migrator->migrateToLmContent($this->getLongMenuTextValue()));
872  }
873 
877  public function toJSON(): string
878  {
879  $result = array();
880  $result['id'] = $this->getId();
881  $result['type'] = (string) $this->getQuestionType();
882  $result['title'] = $this->getTitleForHTMLOutput();
883  $replaced_quesiton_text = $this->getLongMenuTextValue();
884  $result['question'] = $this->formatSAQuestion($this->getQuestion());
885  $result['lmtext'] = $this->formatSAQuestion($replaced_quesiton_text);
886  $result['nr_of_tries'] = $this->getNrOfTries();
887  $result['shuffle'] = $this->getShuffle();
888  $result['feedback'] = array(
889  'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
890  'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
891  );
892 
893  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
894  $result['answers'] = $this->getAnswers();
895  $result['correct_answers'] = $this->getCorrectAnswers();
896  $result['mobs'] = $mobs;
897  return json_encode($result);
898  }
899 
900  public function getIdenticalScoring(): int
901  {
902  return ($this->identical_scoring) ? 1 : 0;
903  }
904 
908  public function setIdenticalScoring($a_identical_scoring): void
909  {
910  $this->identical_scoring = ($a_identical_scoring) ? 1 : 0;
911  }
912 }
lookupForExistingSolutions(int $activeId, int $pass)
Lookup if an authorized or intermediate solution exists.
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)
correctAnswerDoesNotExistInAnswerOptions($answers, $answer_options)
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
setAnswers($answers)
$res
Definition: ltiservices.php:69
getCorrectAnswersForQuestionSolution($question_id)
setIdenticalScoring($a_identical_scoring)
static stripSlashesRecursive($a_data, bool $a_strip_html=true, string $a_allow="")
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
duplicate(bool $for_test=true, string $title="", string $author="", int $owner=-1, $testObjId=null)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
getCorrectAnswersForGap($question_id, $gap_id)
setMinAutoComplete($minAutoComplete)
setJsonStructure($json_structure)
Abstract basic class which is to be extended by the concrete assessment question type classes...
setOwner(int $owner=-1)
savePreviewData(ilAssQuestionPreviewSession $previewSession)
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
checkQuestionCustomPart($form=null)
setExportDetailsXLSX(ilAssExcelFormatHelper $worksheet, int $startrow, int $col, int $active_id, int $pass)
{}
getColumnCoord(int $a_col)
Get column "name" from number.
setAnswerType(array $answerType)
saveToDb(int $original_id=-1)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setSpecificFeedbackSetting($specificFeedbackSetting)
calculateReachedPointsForSolution($found_values, $active_id=0)
setCell($a_row, $a_col, $a_value, $datatype=null)
loadCorrectAnswerData($question_id)
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
setComment(string $comment="")
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
float $points
The maximum available points for the question.
Base Exception for all Exceptions relating to Modules/Test.
global $DIC
Definition: feed.php:28
saveWorkingData(int $active_id, int $pass=null, bool $authorized=true)
const MIN_LENGTH_AUTOCOMPLETE
copyObject($target_questionpool_id, $title="")
const ANSWER_TYPE_TEXT_VAL
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
setBold(string $a_coords)
Set cell(s) to bold.
__construct(VocabulariesInterface $vocabularies)
setLongMenuTextValue($long_menu_text="")
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...
static logAction(string $logtext, int $active_id, int $question_id)
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
loadFromDb($question_id)
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
string $key
Consumer key/client ID value.
Definition: System.php:193
toJSON()
Returns a JSON representation of the question.
setPoints(float $points)
setObjId(int $obj_id=0)
string $question
The question text.
static getDataDir()
get data directory (outside webspace)
getPointsForGap($question_id, $gap_id)
static _getMobsOfObject(string $a_type, int $a_id, int $a_usage_hist_nr=0, string $a_lang="-")
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
const HAS_SPECIFIC_FEEDBACK
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
saveQuestionDataToDb(int $original_id=-1)
getSolutionMaxPass(int $active_id)
removeCurrentSolution(int $active_id, int $pass, bool $authorized=true)
clearAnswerSpecificDataFromDb($question_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setId(int $id=-1)
setOriginalId(?int $original_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getQuestionType()
Returns the question type of the question.
setTitle(string $title="")
setLifecycle(ilAssQuestionLifecycle $lifecycle)
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
setAuthor(string $author="")
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
clearFolder($let_folder_exists=true)
const ANSWER_TYPE_SELECT_VAL
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
setCorrectAnswers($correct_answers)
buildFileName($gap_id)
setQuestion(string $question="")