19 declare(strict_types=1);
88 protected \ilAssQuestionPage
$page;
121 string $comment =
"",
124 string $question =
"" 131 $ilDB = $DIC[
'ilDB'];
132 $ilLog = $DIC->logger();
133 $local_dic = QuestionPoolDIC::dic();
134 $this->questionrepository = $local_dic[
'question.general_properties.repository'];
135 $this->questionpool_request = $local_dic[
'request_data_collector'];
136 $this->question_files = $local_dic[
'question_files'];
137 $this->suggestedsolution_repo = $local_dic[
'question.repo.suggestedsolutions'];
138 $this->current_user = $DIC[
'ilUser'];
143 $this->
http = $DIC->http();
146 $this->thumb_size = self::DEFAULT_THUMB_SIZE;
157 $this->suggested_solutions = [];
158 $this->shuffle =
true;
159 $this->nr_of_tries = 0;
162 $this->questionActionCmd =
'handleQuestionAction';
163 $this->export_image_path =
'';
164 $this->shuffler = $DIC->refinery()->random()->dontShuffle();
166 $this->skillUsageService = $DIC->skills()->usage();
168 $this->test_result_repository = TestDIC::dic()[
'results.data.repository'];
175 abstract public function saveWorkingData(
int $active_id, ?
int $pass =
null,
bool $authorized =
true): bool;
180 bool $authorized_solution =
true 206 array $solution_values
214 array $solution_values
224 return self::$force_pass_results_update_enabled;
229 return $this->questionpool_request->getCmdIndex($this->questionActionCmd) ??
'';
234 return !empty($this->questionpool_request->strArray($post_submission_field_name));
263 string $importdirectory,
266 int $questionpool_id,
269 int &$question_counter,
270 array $import_mapping
273 $import =
new $classname($this);
274 $new_import_mapping = $import->fromXML(
284 return $new_import_mapping;
293 bool $a_include_header =
true,
294 bool $a_include_binary =
true,
295 bool $a_shuffle =
false,
296 bool $test_output =
false,
297 bool $force_image_references =
false 300 $export =
new $classname($this);
301 return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
309 public function setId(
int $id = -1): void
316 $this->test_id =
$id;
326 $this->shuffle = $shuffle ??
false;
331 if ($author ===
'') {
332 $author = $this->current_user->getFullname();
349 return $this->
refinery->encode()->htmlSpecialCharsAsEntities()->transform($this->title);
379 return $this->
refinery->encode()->htmlSpecialCharsAsEntities()->transform($this->
comment);
389 if ($a_size >= self::MINIMUM_THUMB_SIZE) {
390 $this->thumb_size = $a_size;
392 throw new ilException(
"Thumb size must be at least " . self::MINIMUM_THUMB_SIZE .
"px");
398 return self::MINIMUM_THUMB_SIZE;
403 return self::MAXIMUM_THUMB_SIZE;
413 return $this->
refinery->string()->stripTags()->transform($this->author);
448 if ($this->external_id ===
null || $this->external_id ===
'') {
449 if ($this->
getId() > 0) {
452 return uniqid(
'',
true);
459 $question = self::instantiateQuestion($question_id);
460 if (!is_object($question)) {
463 return $question->getSuggestedSolutionOutput();
469 foreach ($this->suggested_solutions as $solution) {
470 switch ($solution->getType()) {
471 case SuggestedSolution::TYPE_LM:
472 case SuggestedSolution::TYPE_LM_CHAPTER:
473 case SuggestedSolution::TYPE_LM_PAGE:
474 case SuggestedSolution::TYPE_GLOSARY_TERM:
475 $output[] =
'<a href="' 478 . $this->
lng->txt(
"solution_hint")
482 case SuggestedSolution::TYPE_FILE:
483 $possible_texts = array_values(
488 $this->
lng->txt(
'tst_show_solution_suggested')
495 if (!file_exists($path_to_solution)) {
498 $output[] =
'<a href="' 506 return implode(
"<br />", $output);
517 $ilDB = $DIC[
'ilDB'];
521 $result =
$ilDB->queryF(
522 "SELECT * FROM tst_test_result WHERE active_fi = %s AND question_fi = %s AND pass = %s",
523 [
'integer',
'integer',
'integer'],
524 [$active_id, $question_id, $pass]
526 if ($result->numRows() == 1) {
527 $row =
$ilDB->fetchAssoc($result);
528 $points = (float) $row[
"points"];
535 return round(self::_getReachedPoints($active_id, $this->
getId(), $pass), 2);
550 return $reached_points;
564 if (is_null($reached_points)) {
565 $reached_points = 0.0;
572 function () use ($active_id, $pass, $reached_points, $existing_solutions) {
574 DELETE FROM tst_test_result 581 $types = [
'integer',
'integer',
'integer'];
582 $values = [$active_id, $this->
getId(), $pass];
589 $types[] =
'integer';
592 $this->db->manipulateF($query, $types, $values);
594 if ($existing_solutions[
'authorized']) {
595 $next_id = $this->db->nextId(
"tst_test_result");
597 'test_result_id' => [
'integer', $next_id],
598 'active_fi' => [
'integer', $active_id],
599 'question_fi' => [
'integer', $this->
getId()],
600 'pass' => [
'integer', $pass],
601 'points' => [
'float', $reached_points],
602 'tstamp' => [
'integer', time()],
603 'answered' => [
'integer',
true]
607 $fieldData[
'step'] = [
'integer', $this->
getStep()];
610 $this->db->insert(
'tst_test_result', $fieldData);
615 $this->test_result_repository->updateTestAttemptResult($active_id, $pass, $this->
getProcessLocker());
631 $this->
getProcessLocker()->executePersistWorkingStateLockOperation(
function () use ($active_id, $pass, $authorized, &$saveStatus) {
632 if ($pass ===
null) {
671 return CLIENT_WEB_DIR .
"/assessment/$this->obj_id/$this->id/solution/";
680 if ($question_id ===
null) {
684 if ($object_id ===
null) {
688 return $this->question_files->buildImagePath($question_id, $object_id);
694 .
"/assessment/{$this->obj_id}/{$this->id}/solution/";
709 if (!$this->export_image_path) {
711 .
"/assessment/{$this->obj_id}/{$this->id}/images/";
736 if (!count($solution)) {
749 bool $authorized =
true 751 if ($pass ===
null && is_numeric($active_id)) {
764 ORDER BY solution_id";
766 $result = $this->db->queryF(
768 [
'integer',
'integer',
'integer',
'integer',
'integer'],
769 [(
int) $active_id, $this->
getId(), $pass, $this->
getStep(), (
int) $authorized]
782 $result = $this->db->queryF(
784 [
'integer',
'integer',
'integer',
'integer'],
785 [(
int) $active_id, $this->
getId(), $pass, (
int) $authorized]
791 while ($row = $this->db->fetchAssoc($result)) {
802 if (!is_array($answer_table_name)) {
803 $answer_table_name = [$answer_table_name];
806 foreach ($answer_table_name as $table) {
807 if (strlen($table)) {
808 $this->db->manipulateF(
809 "DELETE FROM $table WHERE question_fi = %s",
821 if (!is_array($additional_table_name)) {
822 $additional_table_name = [$additional_table_name];
825 foreach ($additional_table_name as $table) {
826 if (strlen($table)) {
827 $this->db->manipulateF(
828 "DELETE FROM $table WHERE question_fi = %s",
844 public function delete(
int $question_id):
void 846 if ($question_id < 1) {
850 $result = $this->db->queryF(
851 "SELECT obj_fi FROM qpl_questions WHERE question_id = %s",
855 if ($this->db->numRows($result) !== 1) {
859 $row = $this->db->fetchAssoc($result);
860 $obj_id = $row[
"obj_fi"];
865 $this->log->root()->error(
"EXCEPTION: Could not delete page of question $question_id: $e");
869 $affectedRows = $this->db->manipulateF(
870 "DELETE FROM qpl_questions WHERE question_id = %s",
874 if ($affectedRows == 0) {
884 $this->log->root()->error(
"EXCEPTION: Could not delete additional table data of question {$question_id}: {$e}");
889 $affectedRows = $this->db->manipulateF(
890 "DELETE FROM tst_test_question WHERE question_fi = %s",
895 $this->log->root()->error(
"EXCEPTION: Could not delete delete question {$question_id} from a test: {$e}");
901 $this->log->root()->error(
"EXCEPTION: Could not delete suggested solutions of question {$question_id}: {$e}");
904 $directory =
CLIENT_WEB_DIR .
"/assessment/" . $obj_id .
"/$question_id";
906 if (is_dir($directory)) {
910 $this->log->root()->error(
"EXCEPTION: Could not delete question file directory {$directory} of question {$question_id}: {$e}");
919 foreach ($mobs as $mob) {
927 $this->log->root()->error(
"EXCEPTION: Error deleting the media objects of question {$question_id}: {$e}");
930 $assignmentList->setParentObjId($obj_id);
931 $assignmentList->setQuestionIdFilter($question_id);
932 $assignmentList->loadFromDb();
933 foreach ($assignmentList->getAssignmentsByQuestionId($question_id) as $assignment) {
935 $assignment->deleteFromDb();
938 if (!$assignment->isSkillUsed()) {
939 $this->skillUsageService->removeUsage(
940 $assignment->getParentObjId(),
941 $assignment->getSkillBaseId(),
942 $assignment->getSkillTrefId()
953 $this->log->root()->error(
954 "EXCEPTION: Error updating the question pool question count of" 955 .
" question pool {$this->getObjId()} when deleting question {$question_id}: {$e}" 964 foreach ($taxIds as $taxId) {
966 $taxNodeAssignment->deleteAssignmentsOfItem($this->
getId());
973 $result = $this->db->queryF(
974 "SELECT question_id FROM qpl_questions WHERE original_id = %s OR question_id = %s",
975 [
'integer',
'integer'],
976 [$this->
id, $this->
id]
978 if ($this->db->numRows($result) == 0) {
982 while ($row = $this->db->fetchAssoc($result)) {
983 $found_id[] = $row[
"question_id"];
986 $result = $this->db->query(
"SELECT * FROM tst_test_result WHERE " . $this->db->in(
'question_fi', $found_id,
false,
'integer'));
988 return $this->db->numRows($result);
993 if (!file_exists($file)) {
997 if (!is_file($file)) {
1001 if (!is_readable($file)) {
1011 foreach ($mobs as $mob) {
1020 $this->page->setId($this->
getId());
1021 $this->page->setParentId($qpl_id);
1022 $this->page->setXMLContent(
"<PageObject><PageContent>" .
1023 "<Question QRef=\"il__qst_" . $this->
getId() .
"\"/>" .
1024 "</PageContent></PageObject>");
1025 $this->page->create(
false);
1033 ilPCPlugged::handleCopiedPluggedContent($page, $page->getDomDoc());
1034 $xml = str_replace(
"il__qst_" . $a_q_id,
"il__qst_" . $this->
id, $page->getXMLFromDom());
1035 $this->page->setXMLContent($xml);
1036 $this->page->updateFromXML();
1043 return $page->getXMLContent();
1057 'ok.svg' =>
'ok.png',
1058 'not_ok.svg' =>
'not_ok.png',
1059 'object/checkbox_checked.svg' =>
'checkbox_checked.png',
1060 'object/checkbox_unchecked.svg' =>
'checkbox_unchecked.png',
1061 'object/radiobutton_checked.svg' =>
'radiobutton_checked.png',
1062 'object/radiobutton_unchecked.svg' =>
'radiobutton_unchecked.png' 1065 public function fixSvgToPng(
string $imageFilenameContainingString): string
1067 $needles = array_keys(self::$imageSourceFixReplaceMap);
1068 $replacements = array_values(self::$imageSourceFixReplaceMap);
1069 return str_replace($needles, $replacements, $imageFilenameContainingString);
1075 if (preg_match_all(
'/src="(.*?)"/m', $html, $matches)) {
1076 $sources = $matches[1];
1078 $needleReplacementMap = [];
1080 foreach ($sources as $src) {
1083 if (file_exists($file)) {
1087 $levels = explode(DIRECTORY_SEPARATOR, $src);
1088 if (count($levels) < 5 || $levels[0] !==
'Customizing' || $levels[2] !==
'skin') {
1094 if ($levels[4] ===
'components/ILIAS' || $levels[4] ===
'components/ILIAS') {
1095 $component = $levels[4] . DIRECTORY_SEPARATOR . $levels[5];
1101 if (count($needleReplacementMap)) {
1102 $html = str_replace(array_keys($needleReplacementMap), array_values($needleReplacementMap), $html);
1111 $result = $this->db->queryF(
1112 'SELECT external_id FROM qpl_questions WHERE question_id = %s',
1116 if ($this->db->numRows($result) === 1) {
1117 $data = $this->db->fetchAssoc($result);
1118 $this->external_id =
$data[
'external_id'];
1122 $this->suggested_solutions = [];
1123 if ($suggested_solutions) {
1124 foreach ($suggested_solutions as $solution) {
1125 $this->suggested_solutions[$solution->getSubquestionIndex()] = $solution;
1141 && $this->questionpool_request->hasRefId()) {
1142 $obj_id = $this->questionpool_request->getRefId();
1146 $obj_id = $this->questionpool_request->int(
'sel_qpl');
1150 return $this->
getId();
1154 if ($a_create_page) {
1158 $next_id = $this->db->nextId(
'qpl_questions');
1159 $this->db->insert(
"qpl_questions", [
1160 "question_id" => [
"integer", $next_id],
1162 "obj_fi" => [
"integer", $obj_id],
1163 "title" => [
"text",
''],
1164 "description" => [
"text",
''],
1165 "author" => [
"text", $this->
getAuthor()],
1166 "owner" => [
"integer", $this->current_user->getId()],
1167 "question_text" => [
"clob",
''],
1168 "points" => [
"float",
"0.0"],
1170 "complete" => [
"text", $complete],
1171 "created" => [
"integer", time()],
1172 "original_id" => [
"integer",
null],
1173 "tstamp" => [
"integer", $tstamp],
1177 $this->
setId($next_id);
1179 if ($a_create_page) {
1184 return $this->
getId();
1189 if ($this->
getId() === -1) {
1190 $next_id = $this->db->nextId(
'qpl_questions');
1191 $this->db->insert(
"qpl_questions", [
1192 "question_id" => [
"integer", $next_id],
1194 "obj_fi" => [
"integer", $this->
getObjId()],
1195 "title" => [
"text", mb_substr($this->
getTitle(), 0, 124)],
1196 "description" => [
"text", mb_substr($this->
getComment(), 0, 1000)],
1197 "author" => [
"text", mb_substr($this->
getAuthor(), 0, 512)],
1198 "owner" => [
"integer", $this->
getOwner()],
1202 "created" => [
"integer", time()],
1203 "original_id" => [
"integer", $original_id],
1204 "tstamp" => [
"integer", time()],
1208 $this->
setId($next_id);
1215 $this->db->update(
"qpl_questions", [
1216 "obj_fi" => [
"integer", $this->
getObjId()],
1217 "title" => [
"text", mb_substr($this->
getTitle(), 0, 124)],
1218 "description" => [
"text", mb_substr($this->
getComment(), 0, 1000)],
1219 "author" => [
"text", mb_substr($this->
getAuthor(), 0, 512)],
1223 "tstamp" => [
"integer", time()],
1224 'complete' => [
'integer', $this->
isComplete()],
1227 "question_id" => [
"integer", $this->
getId()]
1232 bool $for_test =
true,
1234 string $author =
'',
1238 if ($this->
id <= 0) {
1243 $clone = clone $this;
1246 if ((
int) $test_obj_id > 0) {
1247 $clone->setObjId($test_obj_id);
1251 $clone->setTitle($title);
1254 $clone->setAuthor($author);
1257 $clone->setOwner($owner);
1260 $clone->saveToDb($this->
id);
1265 $clone->clonePageOfQuestion($this->
getId());
1266 $clone->cloneXHTMLMediaObjectsOfQuestion($this->
getId());
1270 $clone->onDuplicate($this->
getObjId(), $this->
getId(), $clone->getObjId(), $clone->getId());
1276 int $target_parent_id,
1279 if ($this->
getId() <= 0) {
1280 throw new RuntimeException(
'The question has not been saved. It cannot be duplicated');
1283 $clone = clone $this;
1285 $source_parent_id = $this->
getObjId();
1286 $clone->setObjId($target_parent_id);
1288 $clone->setTitle($title);
1291 $clone->clonePageOfQuestion($this->
id);
1292 $clone->cloneXHTMLMediaObjectsOfQuestion($this->
id);
1295 $clone->onCopy($source_parent_id, $this->
id, $clone->getObjId(), $clone->getId());
1301 int $target_parent_id,
1302 string $target_question_title =
'' 1304 if ($this->
getId() <= 0) {
1305 throw new RuntimeException(
'The question has not been saved. It cannot be duplicated');
1309 $source_parent_id = $this->
getObjId();
1312 $clone = clone $this;
1315 $clone->setObjId($target_parent_id);
1317 if ($target_question_title) {
1318 $clone->setTitle($target_question_title);
1322 $clone->clonePageOfQuestion($source_question_id);
1323 $clone->cloneXHTMLMediaObjectsOfQuestion($source_question_id);
1327 $clone->onCopy($source_parent_id, $source_question_id, $clone->getObjId(), $clone->getId());
1351 'tstamp' => [
'integer', time()],
1352 'owner' => [
'integer', $this->
getOwner()],
1353 'complete' => [
'integer', $complete],
1354 'lifecycle' => [
'text', $this->
getLifecycle()->getIdentifier()],
1357 'question_id' => [
'integer', $this->
getId()]
1366 $target = opendir($image_target_path);
1367 while ($target_file = readdir($target)) {
1368 if ($target_file ===
'.' || $target_file ===
'..') {
1372 $image_target_path . DIRECTORY_SEPARATOR . $target_file,
1373 $image_target_path . DIRECTORY_SEPARATOR . $target_file
1381 $ilDB = $DIC->database();
1382 $query =
"UPDATE qpl_questions SET tstamp = %s, original_id = %s WHERE question_id = %s";
1386 [
'integer',
'integer',
'text'],
1387 [time(), $originalId, $questionId]
1394 $ilDB = $DIC->database();
1396 $query =
"UPDATE qpl_questions SET tstamp = %s, original_id = NULL WHERE question_id = %s";
1400 [
'integer',
'text'],
1401 [time(), $questionId]
1406 int $original_parent_id,
1407 int $original_question_id,
1408 int $duplicate_parent_id,
1409 int $duplicate_question_id
1413 $this->feedbackOBJ->duplicateFeedback($original_question_id, $duplicate_question_id);
1414 $this->
duplicateSkillAssignments($original_parent_id, $original_question_id, $duplicate_parent_id, $duplicate_question_id);
1415 $this->
duplicateComments($original_parent_id, $original_question_id, $duplicate_parent_id, $duplicate_question_id);
1419 int $original_question_id,
1420 int $clone_question_id,
1421 int $original_parent_id,
1422 int $clone_parent_id
1424 $this->feedbackOBJ->cloneFeedback($original_question_id, $clone_question_id);
1427 protected function onCopy(
int $sourceParentId,
int $sourceQuestionId,
int $targetParentId,
int $targetQuestionId): void
1431 $this->feedbackOBJ->duplicateFeedback($sourceQuestionId, $targetQuestionId);
1433 $this->
duplicateComments($sourceParentId, $sourceQuestionId, $targetParentId, $targetQuestionId);
1437 int $parent_source_id,
1439 int $parent_target_id,
1444 $notes = $manager->getNotesForRepositoryObjIds([$parent_source_id], Note::PUBLIC);
1445 $notes = array_filter(
1447 fn($n) => $n->getContext()->getSubObjId() === $source_id
1450 foreach ($notes as $note) {
1451 $new_context = $data_service->context(
1454 $note->getContext()->getType()
1456 $new_note = $data_service->note(
1462 $note->getCreationDate(),
1463 $note->getUpdateDate(),
1464 $note->getRecipient()
1466 $manager->createNote($new_note, [],
true);
1474 $source_id = $this->
getId();
1475 $notes = $manager->getNotesForRepositoryObjIds([$this->
getObjId()], Note::PUBLIC);
1476 $notes = array_filter(
1478 fn($n) => $n->getContext()->getSubObjId() === $source_id
1480 foreach ($notes as $note) {
1481 $repo->deleteNote($note->getId());
1488 return $service->internal()->domain()->notes();
1494 return $service->internal()->data();
1500 return $service->internal()->repo()->note();
1508 $this->suggested_solutions = [];
1514 if (array_key_exists($subquestion_index, $this->suggested_solutions)) {
1515 return $this->suggested_solutions[$subquestion_index];
1521 int $source_question_id,
1522 int $target_question_id
1533 foreach ($this->suggested_solutions as $solution) {
1534 if (!$solution->isOfTypeFile()
1535 || $solution->getFilename() ===
'') {
1540 $filepath_original = str_replace(
1541 "/{$this->obj_id}/{$this->id}/solution",
1542 "/{$parent_id}/{$question_id}/solution",
1545 if (!file_exists($filepath)) {
1548 if (!is_file($filepath_original . $solution->getFilename())
1549 || !copy($filepath_original . $solution->getFilename(), $filepath . $solution->getFilename())) {
1550 $this->log->root()->error(
'File for suggested solutions could not be duplicated:');
1551 $this->log->root()->error(
"Question-Id: {$this->id}; Question-Title: {$this->title}; File: {$filepath_original}{$solution->getFilename()}");
1557 int $source_question_id,
1558 int $target_question_id
1561 $filepath_original = str_replace(
"/$target_question_id/solution",
"/$source_question_id/solution", $filepath_target);
1563 foreach ($this->suggested_solutions as $solution) {
1564 if (!$solution->isOfTypeFile()
1565 || $solution->getFilename() ===
'') {
1569 if (!file_exists($filepath_original)) {
1573 if (!is_file($filepath_original . $solution->getFilename())
1574 || copy($filepath_target . $solution->getFilename(), $filepath_target . $solution->getFilename())) {
1575 $this->log->root()->error(
'File for suggested solutions could not be cloned:');
1576 $this->log->root()->error(
"Question-Id: {$this->id}; Question-Title: {$this->title}; File: {$filepath_original}{$solution->getFilename()}");
1585 $solution = $solution->withQuestionId($target_question_id);
1586 $update[] = $solution;
1593 if (preg_match(
"/il_(\d+)_(\w+)_(\d+)/", $internal_link, $matches) ===
false) {
1594 return $internal_link;
1596 switch ($matches[2]) {
1613 if ($resolved_link ===
null) {
1614 return "il__{$matches[2]}_{$matches[3]}";
1616 return $internal_link;
1625 $result_pre = $this->db->queryF(
1626 "SELECT internal_link, suggested_solution_id FROM qpl_sol_sug WHERE question_fi = %s",
1630 if ($this->db->numRows($result_pre) < 1) {
1634 while ($row = $this->db->fetchAssoc($result_pre)) {
1635 $internal_link = $row[
"internal_link"];
1637 if ($internal_link === $resolved_link) {
1641 $this->db->manipulateF(
1642 "UPDATE qpl_sol_sug SET internal_link = %s WHERE suggested_solution_id = %s",
1644 [$resolved_link, $row[
"suggested_solution_id"]]
1648 if ($resolvedlinks === 0) {
1654 $result_post = $this->db->queryF(
1655 "SELECT internal_link FROM qpl_sol_sug WHERE question_fi = %s",
1659 if ($this->db->numRows($result_post) < 1) {
1663 while ($row = $this->db->fetchAssoc($result_post)) {
1664 if (preg_match(
"/il_(\d*?)_(\w+)_(\d+)/", $row[
"internal_link"], $matches)) {
1673 "lm" =>
"LearningModule",
1674 "pg" =>
"PageObject",
1675 "st" =>
"StructureObject",
1676 "git" =>
"GlossaryItem",
1677 "mob" =>
"MediaObject" 1680 if (preg_match(
"/il__(\w+)_(\d+)/", $target, $matches)) {
1681 $type = $matches[1];
1682 $target_id = $matches[2];
1683 switch ($linktypes[$matches[1]]) {
1685 $href =
"./ilias.php?baseClass=ilLMPresentationGUI&obj_type=" . $linktypes[$type]
1686 .
"&cmd=media&ref_id=" . $this->questionpool_request->getRefId()
1687 .
"&mob_id=" . $target_id;
1689 case "StructureObject":
1690 case "GlossaryItem":
1692 case "LearningModule":
1694 $href =
"./goto.php?target=" . $type .
"_" . $target_id;
1707 $original_parent_id = self::lookupParentObjId($this->
getOriginalId());
1709 if ($original_parent_id ===
null) {
1714 $original = clone $this;
1717 $original->setOriginalId(
null);
1718 $original->setObjId($original_parent_id);
1720 $original->saveToDb();
1723 $original->createPageObject();
1724 $original->clonePageOfQuestion($this->
getId());
1739 $ilCtrl = $DIC[
'ilCtrl'];
1740 $ilDB = $DIC[
'ilDB'];
1742 $questionrepository = QuestionPoolDIC::dic()[
'question.general_properties.repository'];
1743 $question_type = $questionrepository->
getForQuestionId($question_id)?->getClassName() ??
'';
1744 if ($question_type ===
'') {
1748 $question =
new $question_type();
1749 $question->loadFromDb($question_id);
1751 $feedbackObjectClassname = self::getFeedbackClassNameByQuestionType($question_type);
1752 $question->feedbackOBJ =
new $feedbackObjectClassname($question, $ilCtrl,
$ilDB, $lng);
1769 return self::_getSolutionMaxPass($this->
getId(), $active_id);
1781 $ilDB = $DIC[
'ilDB'];
1783 $result =
$ilDB->queryF(
1784 "SELECT MAX(pass) maxpass FROM tst_test_result WHERE active_fi = %s AND question_fi = %s",
1785 [
'integer',
'integer'],
1786 [$active_id, $question_id]
1788 if ($result->numRows() === 1) {
1789 $row =
$ilDB->fetchAssoc($result);
1790 return $row[
"maxpass"];
1809 return $points > 0.0 ?
$points : 0.0;
1833 if ($count_system == 1) {
1839 if ($score_cutting == 0) {
1850 if (preg_match(
"/.*\.(png|jpg|gif|jpeg)$/i", $plain_image_filename, $matches)) {
1851 $extension =
"." . $matches[1];
1855 $plain_image_filename = uniqid($plain_image_filename . microtime(
true),
true);
1858 return md5($plain_image_filename) . $extension;
1871 public static function _setReachedPoints(
1880 $ilDB = $DIC[
'ilDB'];
1882 $question_properties_repository = QuestionPoolDIC::dic()[
'question.general_properties.repository'];
1884 if ($points > $maxpoints) {
1888 if ($pass ===
null) {
1893 $result =
$ilDB->queryF(
1894 "SELECT points FROM tst_test_result WHERE active_fi = %s AND question_fi = %s AND pass = %s",
1895 [
'integer',
'integer',
'integer'],
1896 [$active_id, $question_id, $pass]
1898 $manual = ($manualscoring) ? 1 : 0;
1899 $rowsnum = $result->numRows();
1901 $row =
$ilDB->fetchAssoc($result);
1902 $old_points = $row[
'points'];
1903 if ($old_points !== $points) {
1904 $affectedRows =
$ilDB->manipulateF(
1905 "UPDATE tst_test_result SET points = %s, manual = %s, tstamp = %s WHERE active_fi = %s AND question_fi = %s AND pass = %s",
1906 [
'float',
'integer',
'integer',
'integer',
'integer',
'integer'],
1907 [$points, $manual, time(), $active_id, $question_id, $pass]
1911 $next_id =
$ilDB->nextId(
'tst_test_result');
1912 $affectedRows =
$ilDB->manipulateF(
1913 "INSERT INTO tst_test_result (test_result_id, active_fi, question_fi, points, pass, manual, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s)",
1914 [
'integer',
'integer',
'integer',
'float',
'integer',
'integer',
'integer'],
1915 [$next_id, $active_id, $question_id, $points, $pass, $manual, time()]
1919 if (!self::isForcePassResultUpdateEnabled() && $old_points === $points && $rowsnum !== 0) {
1924 $test_result_repository = TestDIC::dic()[
'results.data.repository'];
1925 $test_result_repository->updateTestAttemptResult($active_id, $pass);
1943 || !(
new ilSetting(
'advanced_editing'))->
get(
'advanced_editing_javascript_editor') ===
'tinymce') {
1944 $purified_content = nl2br($purified_content);
1960 $result = $this->db->queryF(
1961 "SELECT question_type_id FROM qpl_qst_type WHERE type_tag = %s",
1965 if ($this->db->numRows($result) == 1) {
1966 $row = $this->db->fetchAssoc($result);
1967 return (
int) $row[
"question_type_id"];
1977 $collected .= $this->feedbackOBJ->getGenericFeedbackContent($this->
getId(),
false);
1978 $collected .= $this->feedbackOBJ->getGenericFeedbackContent($this->
getId(),
true);
1979 $collected .= $this->feedbackOBJ->getAllSpecificAnswerFeedbackContents($this->
getId());
1991 $result = $this->db->queryF(
1992 "SELECT question_id FROM qpl_questions WHERE original_id = %s",
1998 while ($row = $this->db->fetchAssoc($result)) {
1999 $ids[] = $row[
"question_id"];
2001 foreach ($ids as $question_id) {
2003 $result = $this->db->queryF(
2004 "SELECT tst_tests.obj_fi FROM tst_tests, tst_test_question WHERE tst_test_question.question_fi = %s AND tst_test_question.test_fi = tst_tests.test_id",
2008 while ($row = $this->db->fetchAssoc($result)) {
2012 $result = $this->db->queryF(
2013 "SELECT tst_tests.obj_fi FROM tst_tests, tst_test_rnd_qst, tst_active WHERE tst_test_rnd_qst.active_fi = tst_active.active_id AND tst_test_rnd_qst.question_fi = %s AND tst_tests.test_id = tst_active.test_fi",
2017 while ($row = $this->db->fetchAssoc($result)) {
2021 foreach ($instances as $key => $value) {
2035 $result = $this->db->queryF(
2036 "SELECT * FROM tst_active WHERE active_id = %s",
2040 if ($this->db->numRows($result)) {
2041 $row = $this->db->fetchAssoc($result);
2042 return [
"user_id" => $row[
"user_fi"],
"test_id" => $row[
"test_fi"]];
2050 return static::HAS_SPECIFIC_FEEDBACK;
2055 return str_replace(
'ass',
'ilAss', $questionType) .
'Feedback';
2060 public static function instantiateQuestionGUI(
int $question_id): ?
assQuestionGUI 2064 $ilCtrl = $DIC[
'ilCtrl'];
2065 $ilDB = $DIC[
'ilDB'];
2067 $ilUser = $DIC[
'ilUser'];
2068 $ilLog = $DIC[
'ilLog'];
2070 if ($question_id <= 0) {
2071 $ilLog->warning(
'Instantiate question called without question id. (instantiateQuestionGUI@assQuestion)');
2072 throw new InvalidArgumentException(
'Instantiate question called without question id. (instantiateQuestionGUI@assQuestion)');
2075 $questionrepository = QuestionPoolDIC::dic()[
'question.general_properties.repository'];
2076 $question_type = $questionrepository->
getForQuestionId($question_id)?->getClassName();
2078 if ($question_type ===
null) {
2082 $question_type_gui = $question_type .
'GUI';
2083 $question_gui =
new $question_type_gui($question_id);
2085 $feedback_object_classname = self::getFeedbackClassNameByQuestionType($question_type);
2086 $question = $question_gui->getObject();
2087 $question->feedbackOBJ =
new $feedback_object_classname($question, $ilCtrl,
$ilDB, $lng);
2089 $assSettings =
new ilSetting(
'assessment');
2091 $processLockerFactory->setQuestionId($question_gui->getObject()->getId());
2092 $processLockerFactory->setUserId($ilUser->getId());
2093 $question->setProcessLocker($processLockerFactory->getLocker());
2094 $question_gui->setObject($question);
2096 return $question_gui;
2106 $this->nr_of_tries = $a_nr_of_tries;
2111 $this->export_image_path =
$path;
2117 $ilDB = $DIC[
'ilDB'];
2119 if ($question_id < 1) {
2123 $result =
$ilDB->queryF(
2124 "SELECT question_fi FROM tst_test_question WHERE question_fi = %s AND test_fi = %s",
2125 [
'integer',
'integer'],
2126 [$question_id, $test_id]
2128 return $ilDB->numRows($result) == 1;
2138 return new \ilAssSelfAssessmentQuestionFormatter();
2159 $this->feedbackOBJ->migrateContentForLearningModule($migrator, $this->
getId());
2197 $ilDB = $DIC[
'ilDB'];
2199 $query =
"SELECT obj_fi FROM qpl_questions WHERE question_id = %s";
2201 $res =
$ilDB->queryF($query, [
'integer'], [$question_id]);
2204 return $row[
'obj_fi'] ??
null;
2210 $assignmentList->setParentObjId($srcParentId);
2211 $assignmentList->setQuestionIdFilter($srcQuestionId);
2212 $assignmentList->loadFromDb();
2214 foreach ($assignmentList->getAssignmentsByQuestionId($srcQuestionId) as $assignment) {
2215 $assignment->setParentObjId($trgParentId);
2216 $assignment->setQuestionId($trgQuestionId);
2217 $assignment->saveToDb();
2220 $this->skillUsageService->addUsage(
2222 $assignment->getSkillBaseId(),
2223 $assignment->getSkillTrefId()
2228 public function syncSkillAssignments(
int $srcParentId,
int $srcQuestionId,
int $trgParentId,
int $trgQuestionId): void
2231 $assignmentList->setParentObjId($trgParentId);
2232 $assignmentList->setQuestionIdFilter($trgQuestionId);
2233 $assignmentList->loadFromDb();
2235 foreach ($assignmentList->getAssignmentsByQuestionId($trgQuestionId) as $assignment) {
2236 $assignment->deleteFromDb();
2239 if (!$assignment->isSkillUsed()) {
2240 $this->skillUsageService->removeUsage(
2241 $assignment->getParentObjId(),
2242 $assignment->getSkillBaseId(),
2243 $assignment->getSkillTrefId()
2254 return $numExistingSolutionRecords > 0;
2260 $ilDB = $DIC[
'ilDB'];
2263 SELECT count(active_fi) cnt 2267 WHERE active_fi = %s 2268 AND question_fi = %s 2274 [
'integer',
'integer',
'integer'],
2275 [$activeId, $questionId, $pass]
2280 return (
int) $row[
'cnt'];
2314 self::ADDITIONAL_CONTENT_EDITING_MODE_RTE,
2315 self::ADDITIONAL_CONTENT_EDITING_MODE_IPE
2338 SELECT qpl_questions.*, 2339 {$this->getAdditionalTableName()}.* 2341 LEFT JOIN {$this->getAdditionalTableName()} 2342 ON {$this->getAdditionalTableName()}.question_fi = qpl_questions.question_id 2343 WHERE qpl_questions.question_id = %s 2363 WHERE active_fi = %s 2364 AND question_fi = %s 2370 return $this->db->queryF(
2372 [
'integer',
'integer',
'integer',
'integer',
'integer'],
2373 [$active_id, $this->
getId(), $pass, $this->
getStep(), (
int) $authorized]
2380 WHERE active_fi = %s 2381 AND question_fi = %s 2386 return $this->db->queryF(
2388 [
'integer',
'integer',
'integer',
'integer'],
2389 [$active_id, $this->
getId(), $pass, (
int) $authorized]
2395 return $this->db->manipulateF(
2396 "DELETE FROM tst_solutions WHERE solution_id = %s",
2408 $result = $this->db->queryF(
2409 "SELECT * FROM tst_solutions WHERE solution_id = %s",
2414 if ($this->db->numRows($result) > 0) {
2415 return $this->db->fetchAssoc($result);
2423 $this->
getProcessLocker()->executeUserSolutionUpdateLockOperation(
function () use ($active_id, $pass) {
2435 DELETE FROM tst_solutions 2436 WHERE active_fi = %s 2437 AND question_fi = %s 2443 return $this->db->manipulateF(
2445 [
'integer',
'integer',
'integer',
'integer',
'integer'],
2446 [$active_id, $this->
getId(), $pass, $this->
getStep(), (
int) $authorized]
2451 DELETE FROM tst_solutions 2452 WHERE active_fi = %s 2453 AND question_fi = %s 2458 return $this->db->manipulateF(
2460 [
'integer',
'integer',
'integer',
'integer'],
2461 [$active_id, $this->
getId(), $pass, (
int) $authorized]
2468 $next_id = $this->db->nextId(
"tst_solutions");
2471 "solution_id" => [
"integer", $next_id],
2472 "active_fi" => [
"integer", $active_id],
2473 "question_fi" => [
"integer", $this->
getId()],
2474 "value1" => [
"clob", $value1],
2475 "value2" => [
"clob", $value2],
2476 "pass" => [
"integer", $pass],
2477 "tstamp" => [
"integer", ((
int) $tstamp > 0) ? (
int) $tstamp : time()],
2478 'authorized' => [
'integer', (
int) $authorized]
2482 $fieldData[
'step'] = [
"integer", $this->
getStep()];
2485 return $this->db->insert(
"tst_solutions", $fieldData);
2492 "value1" => [
"clob", $value1],
2493 "value2" => [
"clob", $value2],
2494 "tstamp" => [
"integer", time()],
2495 'authorized' => [
'integer', (
int) $authorized]
2499 $fieldData[
'step'] = [
"integer", $this->
getStep()];
2502 return $this->db->update(
"tst_solutions", $fieldData, [
2503 'solution_id' => [
'integer', $solutionId]
2511 'authorized' => [
'integer', (
int) $authorized]
2515 $fieldData[
'tstamp'] = [
'integer', time()];
2519 'question_fi' => [
'integer', $this->
getId()],
2520 'active_fi' => [
'integer', $activeId],
2521 'pass' => [
'integer', $pass]
2525 $whereData[
'step'] = [
"integer", $this->
getStep()];
2528 return $this->db->update(
'tst_solutions', $fieldData, $whereData);
2547 foreach ($this->
getSolutionValues($activeId, $passIndex,
false) as $solutionRec) {
2548 if ($solutionRec[
'value1'] ==
'' && $solutionRec[
'value2'] ==
'') {
2556 return !strlen($solutionRecord[
'value1']) && !strlen($solutionRecord[
'value2']);
2561 $types = [
"integer",
"integer",
"integer",
"integer"];
2562 $values = [$activeId, $this->
getId(), $passIndex, (
int) $authorized];
2563 $valuesCondition = [];
2565 foreach ($matchValues as $valueField => $value) {
2566 switch ($valueField) {
2569 $valuesCondition[] =
"{$valueField} = %s";
2579 $valuesCondition = implode(
' AND ', $valuesCondition);
2582 DELETE FROM tst_solutions 2583 WHERE active_fi = %s 2584 AND question_fi = %s 2587 AND $valuesCondition 2591 $query .=
" AND step = %s ";
2592 $types[] =
'integer';
2596 $this->db->manipulateF($query, $types, $values);
2602 $this->
saveCurrentSolution($activeId, $passIndex, $rec[
'value1'], $rec[
'value2'],
true, $rec[
'tstamp']);
2610 if (!count($intermediateSolution)) {
2616 if ($considerDummyRecordCreation) {
2630 $this->step =
$step;
2644 $time_array = explode(
':', $time);
2645 if (count($time_array) == 3) {
2646 $sec += (
int) $time_array[0] * 3600;
2647 $sec += (
int) $time_array[1] * 60;
2648 $sec += (
int) $time_array[2];
2655 return json_encode([]);
2662 return (
bool) $solutionAvailability[
'intermediate'];
2667 if ($pass ===
null) {
2671 return (
bool) $solutionAvailability[
'authorized'];
2677 return $solutionAvailability[
'authorized'] || $solutionAvailability[
'intermediate'];
2683 $result = $this->db->queryF(
2684 "SELECT MAX(step) max_step FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
2685 [
"integer",
"integer",
"integer"],
2686 [$active_id, $pass, $this->
getId()]
2689 $row = $this->db->fetchAssoc($result);
2691 return (
int) $row[
'max_step'];
2702 'authorized' =>
false,
2703 'intermediate' => false
2707 SELECT authorized, COUNT(*) cnt 2709 WHERE active_fi = %s 2710 AND question_fi = %s 2715 $query .=
" AND step = " . $this->db->quote((
int) $this->
getStep(),
'integer') .
" ";
2722 $result = $this->db->queryF($query, [
'integer',
'integer',
'integer'], [$activeId, $this->
getId(), $pass]);
2724 while ($row = $this->db->fetchAssoc($result)) {
2725 if ($row[
'authorized']) {
2726 $return[
'authorized'] = $row[
'cnt'] > 0;
2728 $return[
'intermediate'] = $row[
'cnt'] > 0;
2746 $query =
"DELETE FROM tst_solutions WHERE question_fi = %s";
2747 $this->db->manipulateF($query, [
'integer'], [$this->
getId()]);
2753 DELETE FROM tst_solutions 2754 WHERE active_fi = %s 2755 AND question_fi = %s 2760 $query .=
" AND step = " . $this->db->quote((
int) $this->
getStep(),
'integer') .
" ";
2763 return $this->db->manipulateF(
2765 [
'integer',
'integer',
'integer'],
2766 [$activeId, $this->
getId(), $pass]
2774 $this->test_result_repository->updateTestAttemptResult($activeId, $pass, $this->
getProcessLocker(), $this->
getTestId());
2780 DELETE FROM tst_test_result 2781 WHERE active_fi = %s 2782 AND question_fi = %s 2787 $query .=
" AND step = " . $this->db->quote((
int) $this->
getStep(),
'integer') .
" ";
2790 return $this->db->manipulateF(
2792 [
'integer',
'integer',
'integer'],
2793 [$activeId, $this->
getId(), $pass]
2801 foreach ($indexedValues as $value1 => $value2) {
2802 $valuePairs[] = [
'value1' => $value1,
'value2' => $value2];
2810 $indexed_values = [];
2812 foreach ($value_pairs as $valuePair) {
2813 $indexed_values[$valuePair[
'value1']] = $valuePair[
'value2'];
2816 return $indexed_values;
2821 $this->db->manipulateF(
2822 "UPDATE qpl_questions SET tstamp = %s WHERE question_id = %s",
2823 [
'integer',
'integer'],
2824 [time(), $this->
getId()]
2830 if ($this->test_question_config ===
null) {
2849 $question_id = $this->
getId();
2869 return preg_replace(self::TRIM_PATTERN,
'', $value);
2874 return !is_null($this->original_id)
2875 && $this->questionrepository->questionExistsInPool($this->original_id)
2890 $this->current_user->getId(),
2894 $this->
answerToLog($additional_info, $active_id, $pass)
2906 $this->current_user->getId(),
2909 $this->
toLog($additional_info)
2919 AdditionalInformationGenerator::KEY_PASS => $pass,
2920 AdditionalInformationGenerator::KEY_REACHED_POINTS => $this->
getReachedPoints($active_id, $pass),
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...
loadFromDb(int $question_id)
const ADDITIONAL_CONTENT_EDITING_MODE_IPE
cloneXHTMLMediaObjectsOfQuestion(int $source_question_id)
setNrOfTries(int $a_nr_of_tries)
ilTestQuestionConfig $testQuestionConfig
ilAssQuestionFeedback $feedbackOBJ
syncSkillAssignments(int $srcParentId, int $srcQuestionId, int $trgParentId, int $trgQuestionId)
ilGlobalPageTemplate $tpl
deletePageOfQuestion(int $question_id)
getSolutionValues(int $active_id, ?int $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
SuggestedSolutionsDatabaseRepository $suggestedsolution_repo
authorizedOrIntermediateSolutionExists(int $active_id, int $pass)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
resolveSuggestedSolutionLinks()
getActiveUserData(int $active_id)
Returns the user id and the test id for a given active id.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
migrateContentForLearningModule(ilAssSelfAssessmentMigrator $migrator)
toXML(bool $a_include_header=true, bool $a_include_binary=true, bool $a_shuffle=false, bool $test_output=false, bool $force_image_references=false)
Returns a QTI xml representation of the question.
static _isWriteable($object_id, $user_id)
Returns true, if the question pool is writeable by a given user.
calculateResultsFromSolution(int $active_id, int $pass)
Calculates the question results from a previously saved question solution.
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
ilTestQuestionConfig $test_question_config
getSuggestedSolutionPath()
getTitleFilenameCompliant()
isPreviewSolutionCorrect(ilAssQuestionPreviewSession $preview_session)
answerToLog(AdditionalInformationGenerator $additional_info, int $active_id, int $pass)
removeResultRecord(int $activeId, int $pass)
deleteAdditionalTableData(int $question_id)
deleteAnswers(int $question_id)
duplicate(bool $for_test=true, string $title='', string $author='', int $owner=-1, $test_obj_id=null)
static _getSuggestedSolutionOutput(int $question_id)
updateCurrentSolutionsAuthorization(int $activeId, int $pass, bool $authorized, bool $keepTime=false)
onDuplicate(int $original_parent_id, int $original_question_id, int $duplicate_parent_id, int $duplicate_question_id)
buildTestPresentationConfig()
TestResultRepository $test_result_repository
resetUsersAnswer(int $activeId, int $pass)
static isFileAvailable(string $file)
removeAllImageFiles(string $image_target_path)
setProcessLocker(ilAssQuestionProcessLocker $processLocker)
Provides fluid interface to LoggingServices.
Interface Observer Contains several chained tasks and infos about them.
cloneSuggestedSolutionFiles(int $source_question_id, int $target_question_id)
ensureNonNegativePoints(float $points)
static _getAllReferences(int $id)
get all reference ids for object ID
ilAssQuestionProcessLocker $processLocker
setSelfAssessmentEditingMode(bool $selfassessmenteditingmode)
SkillUsageService $skillUsageService
isDummySolutionRecord(array $solutionRecord)
solutionValuesToText(array $solution_values)
MUST convert the given solution values into text.
toLog(AdditionalInformationGenerator $additional_info)
MUST return an array of the question settings that can be stored in the log.
adjustReachedPointsByScoringOptions(float $points, int $active_id)
Adjust the given reached points by checks for all special scoring options in the test container...
createNewOriginalFromThisDuplicate(int $target_parent_id, string $target_question_title='')
static isForcePassResultUpdateEnabled()
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getImagePathWeb()
Returns the web image path for web accessable images of a question.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setThumbSize(int $a_size)
static getUsageOfObject(int $a_obj_id, bool $a_include_titles=false)
cloneQuestionTypeSpecificProperties(self $target)
migrateToLmContent($content)
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
static getNumExistingSolutionRecords(int $activeId, int $pass, int $questionId)
duplicateSkillAssignments(int $srcParentId, int $srcQuestionId, int $trgParentId, int $trgQuestionId)
getAdditionalContentEditingMode()
cloneSuggestedSolutions(int $source_question_id, int $target_question_id)
RequestDataCollector $questionpool_request
getParticipantsSolution()
static _cleanupMediaObjectUsage(string $a_text, string $a_usage_type, int $a_usage_id)
Synchronises appearances of media objects in $a_text with media object usage table.
persistWorkingState(int $active_id, $pass, bool $authorized=true)
persists the working state for current testactive and testpass
getSelfAssessmentEditingMode()
QuestionFiles $question_files
static setTokenMaxLifetimeInSeconds(int $token_max_lifetime_in_seconds)
persistPreviewState(ilAssQuestionPreviewSession $preview_session)
persists the preview state for current user and question
setPreventRteUsage(bool $prevent_rte_usage)
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
getSuggestedSolution(int $subquestion_index=0)
setComment(string $comment="")
static _questionExistsInTest(int $question_id, int $test_id)
string $questionActionCmd
getValidAdditionalContentEditingModes()
Customizing of pimple-DIC for ILIAS.
getUserSolutionPreferingIntermediate(int $active_id, ?int $pass=null)
afterSyncWithOriginal(int $original_question_id, int $clone_question_id, int $original_parent_id, int $clone_parent_id)
bool $selfassessmenteditingmode
static implodeKeyValues(array $keyValues)
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...
setExportImagePath(string $path)
static removeTrailingPathSeparators(string $path)
Repository internal data service.
static _saveLink(string $a_source_type, int $a_source_id, string $a_target_type, int $a_target_id, int $a_target_inst=0, string $a_source_lang="-")
save internal link information
calculateReachedPoints(int $active_id, ?int $pass=null, bool $authorized_solution=true)
fixUnavailableSkinImageSources(string $html)
getSuggestedSolutionsRepo()
isAddableAnswerOptionValue(int $qIndex, string $answerOptionValue)
updateCurrentSolution(int $solutionId, $value1, $value2, bool $authorized=true)
setParticipantsSolution($participantSolution)
static getASCIIFilename(string $a_filename)
deleteDummySolutionRecord(int $activeId, int $passIndex)
getVariablesAsTextArray(int $active_id, int $pass)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
removeIntermediateSolution(int $active_id, int $pass)
static _getUserIdFromActiveId(int $active_id)
static resetOriginalId(int $questionId)
resolveInternalLink(string $internal_link)
getQuestionForHTMLOutput()
static setForcePassResultUpdateEnabled(bool $force_pass_results_update_enabled)
static instantiateQuestion(int $question_id)
Interface for html sanitizing functionality.
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
TestQuestionAdministrationInteractionTypes
purifyAndPrepareTextAreaOutput(string $content)
static http()
Fetches the global http state from ILIAS.
clonePageOfQuestion(int $a_q_id)
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
duplicateComments(int $parent_source_id, int $source_id, int $parent_target_id, int $target_id)
buildHashedImageFilename(string $plain_image_filename, bool $unique=false)
static _lookupTitle(int $obj_id)
lookupForExistingSolutions(int $activeId, int $pass)
Lookup if an authorized or intermediate solution exists.
copyObject(int $target_parent_id, string $title='')
getAdjustedReachedPoints(int $active_id, int $pass, bool $authorized_solution=true)
static _exists(string $a_parent_type, int $a_id, string $a_lang="", bool $a_no_cache=false)
Checks whether page exists.
static _deleteAllLinksOfSource(string $a_source_type, int $a_source_id, string $a_lang="-")
Delete all links of a given source.
answerToParticipantInteraction(AdditionalInformationGenerator $additional_info, int $test_ref_id, int $active_id, int $pass, string $source_ip, TestParticipantInteractionTypes $interaction_type)
removeSolutionRecordById(int $solutionId)
static _getReachedPoints(int $active_id, int $question_id, int $pass)
saveWorkingData(int $active_id, ?int $pass=null, bool $authorized=true)
getCorrectSolutionForTextOutput(int $active_id, int $pass)
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
setLastChange(int $lastChange)
getSolutionForTextOutput(int $active_id, int $pass)
static _getIdForImportId(string $a_import_id)
get current object id for import id (static)
static _getSolutionMaxPass(int $question_id, int $active_id)
Returns the maximum pass a users question solution.
static _getIdForImportId(string $a_type, string $a_target)
Get current id for an import id.
intermediateSolutionExists(int $active_id, int $pass)
static _getCountSystem($active_id)
Repository for suggested solutions.
static getImagePath(string $image_name, string $module_path="", string $mode="output", bool $offline=false)
get image path (for images located in a template directory)
getDescriptionForHTMLOutput()
getHtmlUserSolutionPurifier()
hasWritableOriginalInQuestionPool()
static getInstanceByType(string $type)
static _lookupAuthor($obj_id)
Gets the authors name of the ilObjTest object.
getSuggestedSolutionPathWeb()
saveQuestionDataToDb(?int $original_id=null)
removeExistingSolutions(int $activeId, int $pass)
setDefaultNrOfTries(int $defaultnroftries)
saveToDb(?int $original_id=null)
cleanupMediaObjectUsage()
deleteSuggestedSolutions()
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
fetchIndexedValuesFromValuePairs(array $value_pairs)
static saveOriginalId(int $questionId, int $originalId)
TestParticipantInteractionTypes
static _updateQuestionCount(int $object_id)
savePreviewData(ilAssQuestionPreviewSession $preview_session)
ilAssQuestionLifecycle $lifecycle
fetchValuePairsFromIndexedValues(array $indexedValues)
getRTETextWithMediaObjects()
createNewQuestion(bool $a_create_page=true)
Creates a new question without an owner when a new question is created This assures that an ID is giv...
isAdditionalContentEditingModePageObject()
duplicateIntermediateSolutionAuthorized(int $activeId, int $passIndex)
const KEY_VALUES_IMPLOSION_SEPARATOR
toQuestionAdministrationInteraction(AdditionalInformationGenerator $additional_info, int $test_ref_id, TestQuestionAdministrationInteractionTypes $interaction_type)
static $imageSourceFixReplaceMap
static _getScoreCutting(int $active_id)
Determines if the score of a question should be cut at 0 points or the score of the whole test...
getSolutionMaxPass(int $active_id)
isValidAdditionalContentEditingMode(string $additionalContentEditingMode)
static extendedTrim(string $value)
Trim non-printable characters from the beginning and end of a string.
removeCurrentSolution(int $active_id, int $pass, bool $authorized=true)
array $suggested_solutions
lmMigrateQuestionTypeGenericContent(ilAssSelfAssessmentMigrator $migrator)
static lookupParentObjId(int $question_id)
__construct(Container $dic, ilPlugin $plugin)
onCopy(int $sourceParentId, int $sourceQuestionId, int $targetParentId, int $targetQuestionId)
const ADDITIONAL_CONTENT_EDITING_MODE_RTE
setOriginalId(?int $original_id)
getTestPresentationConfig()
setExternalId(?string $external_id)
getTestOutputSolutions(int $activeId, int $pass)
setShuffler(Transformation $shuffler)
static convertISO8601FormatH_i_s_ExtendedToSeconds(string $time)
setTitle(string $title="")
deleteSolutionRecordByValues(int $activeId, int $passIndex, bool $authorized, array $matchValues)
static signFile(string $path_to_file)
authorizedSolutionExists(int $active_id, ?int $pass)
setLifecycle(ilAssQuestionLifecycle $lifecycle)
fromXML(string $importdirectory, int $user_id, ilQTIItem $item, int $questionpool_id, ?int $tst_id, ?ilObject &$tst_object, int &$question_counter, array $import_mapping)
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
getReachedPoints(int $active_id, int $pass)
isAnswered(int $active_id, int $pass)
addAnswerOptionValue(int $qIndex, string $answerOptionValue, float $points)
deleteTaxonomyAssignments()
forceExistingIntermediateSolution(int $activeId, int $passIndex, bool $considerDummyRecordCreation)
static $force_pass_results_update_enabled
static _updateObjectiveResult(int $a_user_id, int $a_active_id, int $a_question_id)
GeneralQuestionPropertiesRepository $questionrepository
static fixSvgToPng(string $imageFilenameContainingString)
const HAS_SPECIFIC_FEEDBACK
getHtmlQuestionContentPurifier()
lookupMaxStep(int $active_id, int $pass)
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $preview_session)
setAuthor(string $author="")
isNonEmptyItemListPostSubmission(string $post_submission_field_name)
duplicateSuggestedSolutionFiles(int $parent_id, int $question_id)
Duplicates the files of a suggested solution if the question is duplicated.
string $additionalContentEditingMode
copySuggestedSolutions(int $target_question_id)
getSolutionRecordById(int $solutionId)
setShuffle(?bool $shuffle=true)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
static explodeKeyValues(string $keyValues)
static getDraftInstance()
removeAllExistingSolutions()
getSuggestedSolutionOutput()
getSelfAssessmentFormatter()
getForQuestionId(int $question_id)
getInternalLinkHref(string $target)
static getFeedbackClassNameByQuestionType(string $questionType)
setQuestion(string $question="")
string $export_image_path