19 declare(strict_types=1);
    86     protected \ilAssQuestionPage 
$page;
   118         string $comment = 
"",
   121         string $question = 
""   128         $ilDB = $DIC[
'ilDB'];
   129         $ilLog = $DIC->logger();
   130         $local_dic = QuestionPoolDIC::dic();
   131         $this->questionrepository = $local_dic[
'question.general_properties.repository'];
   132         $this->questionpool_request = $local_dic[
'request_data_collector'];
   133         $this->question_files = $local_dic[
'question_files'];
   134         $this->suggestedsolution_repo = $local_dic[
'question.repo.suggestedsolutions'];
   135         $this->current_user = $DIC[
'ilUser'];
   140         $this->
http = $DIC->http();
   143         $this->thumb_size = self::DEFAULT_THUMB_SIZE;
   154         $this->suggested_solutions = [];
   155         $this->shuffle = 
true;
   156         $this->nr_of_tries = 0;
   159         $this->questionActionCmd = 
'handleQuestionAction';
   160         $this->export_image_path = 
'';
   161         $this->shuffler = $DIC->refinery()->random()->dontShuffle();
   163         $this->skillUsageService = $DIC->skills()->usage();
   170     abstract public function saveWorkingData(
int $active_id, ?
int $pass = null, 
bool $authorized = 
true): bool;
   175         bool $authorized_solution = 
true   201         array $solution_values
   209         array $solution_values
   219         return self::$force_pass_results_update_enabled;
   224         return $this->questionpool_request->getCmdIndex($this->questionActionCmd) ?? 
'';
   229         return !empty($this->questionpool_request->strArray($post_submission_field_name));
   258         string $importdirectory,
   261         int $questionpool_id,
   264         int &$question_counter,
   265         array $import_mapping,
   266         array $solutionhints = []
   269         $import = 
new $classname($this);
   270         $new_import_mapping = $import->fromXML(
   281         foreach ($solutionhints as $hint) {
   282             $this->
importHint($import->getQuestionId(), $hint);
   285         return $new_import_mapping;
   288     private function importHint(
int $question_id, array $hint_array): void
   291         $hint->setQuestionId($question_id);
   292         $hint->setIndex($hint_array[
'index'] ?? 
'');
   293         $hint->setPoints($hint_array[
'points'] ?? 
'');
   297             $hint_page->setParentId($question_id);
   298             $hint_page->setId($hint->getId());
   299             $hint_page->setXMLContent($hint_array[
'txt']);
   300             $hint_page->createFromXML();
   304         $hint->setText($hint_array[
'txt'] ?? 
'');
   314         bool $a_include_header = 
true,
   315         bool $a_include_binary = 
true,
   316         bool $a_shuffle = 
false,
   317         bool $test_output = 
false,
   318         bool $force_image_references = 
false   321         $export = 
new $classname($this);
   322         return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
   330     public function setId(
int $id = -1): void
   337         $this->test_id = 
$id;
   347         $this->shuffle = $shuffle ?? 
false;
   352         if ($author === 
'') {
   353             $author = $this->current_user->getFullname();
   370         return $this->
refinery->encode()->htmlSpecialCharsAsEntities()->transform($this->title);
   400         return $this->
refinery->encode()->htmlSpecialCharsAsEntities()->transform($this->
comment);
   410         if ($a_size >= self::MINIMUM_THUMB_SIZE) {
   411             $this->thumb_size = $a_size;
   413             throw new ilException(
"Thumb size must be at least " . self::MINIMUM_THUMB_SIZE . 
"px");
   419         return self::MINIMUM_THUMB_SIZE;
   424         return self::MAXIMUM_THUMB_SIZE;
   434         return $this->
refinery->string()->stripTags()->transform($this->author);
   469         if ($this->external_id === null || $this->external_id === 
'') {
   470             if ($this->
getId() > 0) {
   473             return uniqid(
'', 
true);
   480         $question = self::instantiateQuestion($question_id);
   481         if (!is_object($question)) {
   484         return $question->getSuggestedSolutionOutput();
   490         foreach ($this->suggested_solutions as $solution) {
   491             switch ($solution->getType()) {
   492                 case SuggestedSolution::TYPE_LM:
   493                 case SuggestedSolution::TYPE_LM_CHAPTER:
   494                 case SuggestedSolution::TYPE_LM_PAGE:
   495                 case SuggestedSolution::TYPE_GLOSARY_TERM:
   496                     $output[] = 
'<a href="'   499                         . $this->
lng->txt(
"solution_hint")
   503                 case SuggestedSolution::TYPE_FILE:
   504                     $possible_texts = array_values(
   509                                 $this->
lng->txt(
'tst_show_solution_suggested')
   516                     if (!file_exists($path_to_solution)) {
   519                     $output[] = 
'<a href="'   527         return implode(
"<br />", $output);
   538         $ilDB = $DIC[
'ilDB'];
   542         $result = 
$ilDB->queryF(
   543             "SELECT * FROM tst_test_result WHERE active_fi = %s AND question_fi = %s AND pass = %s",
   544             [
'integer',
'integer',
'integer'],
   545             [$active_id, $question_id, $pass]
   547         if ($result->numRows() == 1) {
   548             $row = 
$ilDB->fetchAssoc($result);
   549             $points = (float) $row[
"points"];
   556         return round(self::_getReachedPoints($active_id, $this->
getId(), $pass), 2);
   569         $requests_statistic_data = $hint_tracking->getRequestStatisticDataByQuestionAndTestpass();
   570         $reached_points = $reached_points - $requests_statistic_data->getRequestsPoints();
   575         return $reached_points;
   586         $requests_statistic_data = $questionHintTracking->getRequestStatisticDataByQuestionAndTestpass();
   587         $reached_points = $reached_points - $requests_statistic_data->getRequestsPoints();
   592         if (is_null($reached_points)) {
   593             $reached_points = 0.0;
   600             function () use ($active_id, $pass, $reached_points, $requests_statistic_data, $existing_solutions) {
   602                     DELETE FROM         tst_test_result   609                 $types = [
'integer', 
'integer', 
'integer'];
   610                 $values = [$active_id, $this->
getId(), $pass];
   612                 if ($this->
getStep() !== null) {
   617                     $types[] = 
'integer';
   620                 $this->db->manipulateF($query, $types, $values);
   622                 if ($existing_solutions[
'authorized']) {
   623                     $next_id = $this->db->nextId(
"tst_test_result");
   625                         'test_result_id' => [
'integer', $next_id],
   626                         'active_fi' => [
'integer', $active_id],
   627                         'question_fi' => [
'integer', $this->
getId()],
   628                         'pass' => [
'integer', $pass],
   629                         'points' => [
'float', $reached_points],
   630                         'tstamp' => [
'integer', time()],
   631                         'hint_count' => [
'integer', $requests_statistic_data->getRequestsCount()],
   632                         'hint_points' => [
'float', $requests_statistic_data->getRequestsPoints()],
   633                         'answered' => [
'integer', 
true]
   636                     if ($this->
getStep() !== null) {
   637                         $fieldData[
'step'] = [
'integer', $this->
getStep()];
   640                     $this->db->insert(
'tst_test_result', $fieldData);
   666         $this->
getProcessLocker()->executePersistWorkingStateLockOperation(
function () use ($active_id, $pass, $authorized, &$saveStatus) {
   667             if ($pass === null) {
   706         return CLIENT_WEB_DIR . 
"/assessment/$this->obj_id/$this->id/solution/";
   713     public function getImagePath($question_id = null, $object_id = null): string
   715         if ($question_id === null) {
   719         if ($object_id === null) {
   723         return $this->question_files->buildImagePath($question_id, $object_id);
   729             . 
"/assessment/{$this->obj_id}/{$this->id}/solution/";
   744         if (!$this->export_image_path) {
   746                 . 
"/assessment/{$this->obj_id}/{$this->id}/images/";
   771         if (!count($solution)) {
   784         bool $authorized = 
true   786         if ($pass === null && is_numeric($active_id)) {
   790         if ($this->
getStep() !== null) {
   799                                 ORDER BY solution_id";
   801             $result = $this->db->queryF(
   803                 [
'integer', 
'integer', 
'integer', 
'integer', 
'integer'],
   804                 [(
int) $active_id, $this->
getId(), $pass, $this->
getStep(), (
int) $authorized]
   817             $result = $this->db->queryF(
   819                 [
'integer', 
'integer', 
'integer', 
'integer'],
   820                 [(
int) $active_id, $this->
getId(), $pass, (
int) $authorized]
   826         while ($row = $this->db->fetchAssoc($result)) {
   837         if (!is_array($answer_table_name)) {
   838             $answer_table_name = [$answer_table_name];
   841         foreach ($answer_table_name as $table) {
   842             if (strlen($table)) {
   843                 $this->db->manipulateF(
   844                     "DELETE FROM $table WHERE question_fi = %s",
   856         if (!is_array($additional_table_name)) {
   857             $additional_table_name = [$additional_table_name];
   860         foreach ($additional_table_name as $table) {
   861             if (strlen($table)) {
   862                 $this->db->manipulateF(
   863                     "DELETE FROM $table WHERE question_fi = %s",
   879     public function delete(
int $question_id): 
void   881         if ($question_id < 1) {
   885         $result = $this->db->queryF(
   886             "SELECT obj_fi FROM qpl_questions WHERE question_id = %s",
   890         if ($this->db->numRows($result) !== 1) {
   894         $row = $this->db->fetchAssoc($result);
   895         $obj_id = $row[
"obj_fi"];
   900             $this->log->root()->error(
"EXCEPTION: Could not delete page of question $question_id: $e");
   904         $affectedRows = $this->db->manipulateF(
   905             "DELETE FROM qpl_questions WHERE question_id = %s",
   909         if ($affectedRows == 0) {
   919             $this->log->root()->error(
"EXCEPTION: Could not delete additional table data of question {$question_id}: {$e}");
   924             $affectedRows = $this->db->manipulateF(
   925                 "DELETE FROM tst_test_question WHERE question_fi = %s",
   930             $this->log->root()->error(
"EXCEPTION: Could not delete delete question {$question_id} from a test: {$e}");
   936             $this->log->root()->error(
"EXCEPTION: Could not delete suggested solutions of question {$question_id}: {$e}");
   939         $directory = 
CLIENT_WEB_DIR . 
"/assessment/" . $obj_id . 
"/$question_id";
   941             if (is_dir($directory)) {
   945             $this->log->root()->error(
"EXCEPTION: Could not delete question file directory {$directory} of question {$question_id}: {$e}");
   954             foreach ($mobs as $mob) {
   962             $this->log->root()->error(
"EXCEPTION: Error deleting the media objects of question {$question_id}: {$e}");
   964         ilAssQuestionHintTracking::deleteRequestsByQuestionIds([$question_id]);
   967         $assignmentList->setParentObjId($obj_id);
   968         $assignmentList->setQuestionIdFilter($question_id);
   969         $assignmentList->loadFromDb();
   970         foreach ($assignmentList->getAssignmentsByQuestionId($question_id) as $assignment) {
   972             $assignment->deleteFromDb();
   975             if (!$assignment->isSkillUsed()) {
   976                 $this->skillUsageService->removeUsage(
   977                     $assignment->getParentObjId(),
   978                     $assignment->getSkillBaseId(),
   979                     $assignment->getSkillTrefId()
   990             $this->log->root()->error(
   991                 "EXCEPTION: Error updating the question pool question count of"   992                 . 
" question pool {$this->getObjId()} when deleting question {$question_id}: {$e}"  1001         foreach ($taxIds as $taxId) {
  1003             $taxNodeAssignment->deleteAssignmentsOfItem($this->
getId());
  1010         $result = $this->db->queryF(
  1011             "SELECT question_id FROM qpl_questions WHERE original_id = %s OR question_id = %s",
  1012             [
'integer',
'integer'],
  1013             [$this->
id, $this->
id]
  1015         if ($this->db->numRows($result) == 0) {
  1019         while ($row = $this->db->fetchAssoc($result)) {
  1020             $found_id[] = $row[
"question_id"];
  1023         $result = $this->db->query(
"SELECT * FROM tst_test_result WHERE " . $this->db->in(
'question_fi', $found_id, 
false, 
'integer'));
  1025         return $this->db->numRows($result);
  1030         if (!file_exists($file)) {
  1034         if (!is_file($file)) {
  1038         if (!is_readable($file)) {
  1048         foreach ($mobs as $mob) {
  1057         $this->page->setId($this->
getId());
  1058         $this->page->setParentId($qpl_id);
  1059         $this->page->setXMLContent(
"<PageObject><PageContent>" .
  1060             "<Question QRef=\"il__qst_" . $this->
getId() . 
"\"/>" .
  1061             "</PageContent></PageObject>");
  1062         $this->page->create(
false);
  1070             ilPCPlugged::handleCopiedPluggedContent($page, $page->getDomDoc());
  1071             $xml = str_replace(
"il__qst_" . $a_q_id, 
"il__qst_" . $this->
id, $page->getXMLFromDom());
  1072             $this->page->setXMLContent($xml);
  1073             $this->page->updateFromXML();
  1080         return $page->getXMLContent();
  1094         'ok.svg' => 
'ok.png',
  1095         'not_ok.svg' => 
'not_ok.png',
  1096         'object/checkbox_checked.svg' => 
'checkbox_checked.png',
  1097         'object/checkbox_unchecked.svg' => 
'checkbox_unchecked.png',
  1098         'object/radiobutton_checked.svg' => 
'radiobutton_checked.png',
  1099         'object/radiobutton_unchecked.svg' => 
'radiobutton_unchecked.png'  1102     public function fixSvgToPng(
string $imageFilenameContainingString): string
  1104         $needles = array_keys(self::$imageSourceFixReplaceMap);
  1105         $replacements = array_values(self::$imageSourceFixReplaceMap);
  1106         return str_replace($needles, $replacements, $imageFilenameContainingString);
  1112         if (preg_match_all(
'/src="(.*?)"/m', $html, $matches)) {
  1113             $sources = $matches[1];
  1115             $needleReplacementMap = [];
  1117             foreach ($sources as $src) {
  1120                 if (file_exists($file)) {
  1124                 $levels = explode(DIRECTORY_SEPARATOR, $src);
  1125                 if (count($levels) < 5 || $levels[0] !== 
'Customizing' || $levels[2] !== 
'skin') {
  1131                 if ($levels[4] === 
'components/ILIAS' || $levels[4] === 
'components/ILIAS') {
  1132                     $component = $levels[4] . DIRECTORY_SEPARATOR . $levels[5];
  1138             if (count($needleReplacementMap)) {
  1139                 $html = str_replace(array_keys($needleReplacementMap), array_values($needleReplacementMap), $html);
  1148         $result = $this->db->queryF(
  1149             'SELECT external_id FROM qpl_questions WHERE question_id = %s',
  1153         if ($this->db->numRows($result) === 1) {
  1154             $data = $this->db->fetchAssoc($result);
  1155             $this->external_id = 
$data[
'external_id'];
  1159         $this->suggested_solutions = [];
  1160         if ($suggested_solutions) {
  1161             foreach ($suggested_solutions as $solution) {
  1162                 $this->suggested_solutions[$solution->getSubquestionIndex()] = $solution;
  1178             && $this->questionpool_request->hasRefId()) {
  1179             $obj_id = $this->questionpool_request->getRefId();
  1183             $obj_id = $this->questionpool_request->int(
'sel_qpl');
  1187             return $this->
getId();
  1191         if ($a_create_page) {
  1195         $next_id = $this->db->nextId(
'qpl_questions');
  1196         $this->db->insert(
"qpl_questions", [
  1197             "question_id" => [
"integer", $next_id],
  1199             "obj_fi" => [
"integer", $obj_id],
  1200             "title" => [
"text", 
''],
  1201             "description" => [
"text", 
''],
  1202             "author" => [
"text", $this->
getAuthor()],
  1203             "owner" => [
"integer", $this->current_user->getId()],
  1204             "question_text" => [
"clob", 
''],
  1205             "points" => [
"float", 
"0.0"],
  1207             "complete" => [
"text", $complete],
  1208             "created" => [
"integer", time()],
  1209             "original_id" => [
"integer", null],
  1210             "tstamp" => [
"integer", $tstamp],
  1214         $this->
setId($next_id);
  1216         if ($a_create_page) {
  1221         return $this->
getId();
  1226         if ($this->
getId() === -1) {
  1227             $next_id = $this->db->nextId(
'qpl_questions');
  1228             $this->db->insert(
"qpl_questions", [
  1229                 "question_id" => [
"integer", $next_id],
  1231                 "obj_fi" => [
"integer", $this->
getObjId()],
  1232                 "title" => [
"text", mb_substr($this->
getTitle(), 0, 124)],
  1233                 "description" => [
"text", mb_substr($this->
getComment(), 0, 1000)],
  1234                 "author" => [
"text", mb_substr($this->
getAuthor(), 0, 512)],
  1235                 "owner" => [
"integer", $this->
getOwner()],
  1239                 "created" => [
"integer", time()],
  1240                 "original_id" => [
"integer", $original_id],
  1241                 "tstamp" => [
"integer", time()],
  1245             $this->
setId($next_id);
  1252         $this->db->update(
"qpl_questions", [
  1253             "obj_fi" => [
"integer", $this->
getObjId()],
  1254             "title" => [
"text", mb_substr($this->
getTitle(), 0, 124)],
  1255             "description" => [
"text", mb_substr($this->
getComment(), 0, 1000)],
  1256             "author" => [
"text", mb_substr($this->
getAuthor(), 0, 512)],
  1260             "tstamp" => [
"integer", time()],
  1261             'complete' => [
'integer', $this->
isComplete()],
  1264             "question_id" => [
"integer", $this->
getId()]
  1269         bool $for_test = 
true,
  1271         string $author = 
'',
  1275         if ($this->
id <= 0) {
  1280         $clone = clone $this;
  1283         if ((
int) $test_obj_id > 0) {
  1284             $clone->setObjId($test_obj_id);
  1288             $clone->setTitle($title);
  1291             $clone->setAuthor($author);
  1294             $clone->setOwner($owner);
  1297             $clone->saveToDb($this->
id);
  1302         $clone->clonePageOfQuestion($this->
getId());
  1303         $clone->cloneXHTMLMediaObjectsOfQuestion($this->
getId());
  1307         $clone->onDuplicate($this->
getObjId(), $this->
getId(), $clone->getObjId(), $clone->getId());
  1313         int $target_parent_id,
  1316         if ($this->
getId() <= 0) {
  1317             throw new RuntimeException(
'The question has not been saved. It cannot be duplicated');
  1320         $clone = clone $this;
  1322         $source_parent_id = $this->
getObjId();
  1323         $clone->setObjId($target_parent_id);
  1325             $clone->setTitle($title);
  1328         $clone->clonePageOfQuestion($this->
id);
  1329         $clone->cloneXHTMLMediaObjectsOfQuestion($this->
id);
  1332         $clone->onCopy($source_parent_id, $this->
id, $clone->getObjId(), $clone->getId());
  1338         int $target_parent_id,
  1339         string $target_question_title = 
''  1341         if ($this->
getId() <= 0) {
  1342             throw new RuntimeException(
'The question has not been saved. It cannot be duplicated');
  1346         $source_parent_id = $this->
getObjId();
  1349         $clone = clone $this;
  1352         $clone->setObjId($target_parent_id);
  1354         if ($target_question_title) {
  1355             $clone->setTitle($target_question_title);
  1359         $clone->clonePageOfQuestion($source_question_id);
  1360         $clone->cloneXHTMLMediaObjectsOfQuestion($source_question_id);
  1364         $clone->onCopy($source_parent_id, $source_question_id, $clone->getObjId(), $clone->getId());
  1375     public function saveToDb(?
int $original_id = null): void
  1388                 'tstamp' => [
'integer', time()],
  1389                 'owner' => [
'integer', $this->
getOwner()],
  1390                 'complete' => [
'integer', $complete],
  1391                 'lifecycle' => [
'text', $this->
getLifecycle()->getIdentifier()],
  1394                 'question_id' => [
'integer', $this->
getId()]
  1403         $target = opendir($image_target_path);
  1404         while ($target_file = readdir($target)) {
  1405             if ($target_file === 
'.' || $target_file === 
'..') {
  1409                 $image_target_path . DIRECTORY_SEPARATOR . $target_file,
  1410                 $image_target_path . DIRECTORY_SEPARATOR . $target_file
  1418         $ilDB = $DIC->database();
  1419         $query = 
"UPDATE qpl_questions SET tstamp = %s, original_id = %s WHERE question_id = %s";
  1423             [
'integer',
'integer', 
'text'],
  1424             [time(), $originalId, $questionId]
  1431         $ilDB = $DIC->database();
  1433         $query = 
"UPDATE qpl_questions SET tstamp = %s, original_id = NULL WHERE question_id = %s";
  1437             [
'integer', 
'text'],
  1438             [time(), $questionId]
  1443         int $original_parent_id,
  1444         int $original_question_id,
  1445         int $duplicate_parent_id,
  1446         int $duplicate_question_id
  1450         $this->feedbackOBJ->duplicateFeedback($original_question_id, $duplicate_question_id);
  1452         $this->
duplicateSkillAssignments($original_parent_id, $original_question_id, $duplicate_parent_id, $duplicate_question_id);
  1453         $this->
duplicateComments($original_parent_id, $original_question_id, $duplicate_parent_id, $duplicate_question_id);
  1457         int $original_question_id,
  1458         int $clone_question_id,
  1459         int $original_parent_id,
  1460         int $clone_parent_id
  1462         $this->feedbackOBJ->cloneFeedback($original_question_id, $clone_question_id);
  1465     protected function onCopy(
int $sourceParentId, 
int $sourceQuestionId, 
int $targetParentId, 
int $targetQuestionId): void
  1469         $this->feedbackOBJ->duplicateFeedback($sourceQuestionId, $targetQuestionId);
  1472         $this->
duplicateComments($sourceParentId, $sourceQuestionId, $targetParentId, $targetQuestionId);
  1476         int $parent_source_id,
  1478         int $parent_target_id,
  1483         $notes = $manager->getNotesForRepositoryObjIds([$parent_source_id], Note::PUBLIC);
  1484         $notes = array_filter(
  1486             fn($n) => $n->getContext()->getSubObjId() === $source_id
  1489         foreach ($notes as $note) {
  1490             $new_context = $data_service->context(
  1493                 $note->getContext()->getType()
  1495             $new_note = $data_service->note(
  1501                 $note->getCreationDate(),
  1502                 $note->getUpdateDate(),
  1503                 $note->getRecipient()
  1505             $manager->createNote($new_note, [], 
true);
  1513         $source_id = $this->
getId();
  1514         $notes = $manager->getNotesForRepositoryObjIds([$this->
getObjId()], Note::PUBLIC);
  1515         $notes = array_filter(
  1517             fn($n) => $n->getContext()->getSubObjId() === $source_id
  1519         foreach ($notes as $note) {
  1520             $repo->deleteNote($note->getId());
  1527         return $service->internal()->domain()->notes();
  1533         return $service->internal()->data();
  1539         return $service->internal()->repo()->note();
  1547         $this->suggested_solutions = [];
  1553         if (array_key_exists($subquestion_index, $this->suggested_solutions)) {
  1554             return $this->suggested_solutions[$subquestion_index];
  1560         int $source_question_id,
  1561         int $target_question_id
  1572         foreach ($this->suggested_solutions as $solution) {
  1573             if (!$solution->isOfTypeFile()
  1574                 || $solution->getFilename() === 
'') {
  1579             $filepath_original = str_replace(
  1580                 "/{$this->obj_id}/{$this->id}/solution",
  1581                 "/{$parent_id}/{$question_id}/solution",
  1584             if (!file_exists($filepath)) {
  1587             if (!is_file($filepath_original . $solution->getFilename())
  1588                 || !copy($filepath_original . $solution->getFilename(), $filepath . $solution->getFilename())) {
  1589                 $this->log->root()->error(
'File for suggested solutions could not be duplicated:');
  1590                 $this->log->root()->error(
"Question-Id: {$this->id}; Question-Title: {$this->title}; File: {$filepath_original}{$solution->getFilename()}");
  1596         int $source_question_id,
  1597         int $target_question_id
  1600         $filepath_original = str_replace(
"/$target_question_id/solution", 
"/$source_question_id/solution", $filepath_target);
  1602         foreach ($this->suggested_solutions as $solution) {
  1603             if (!$solution->isOfTypeFile()
  1604                 || $solution->getFilename() === 
'') {
  1608             if (!file_exists($filepath_original)) {
  1612             if (!is_file($filepath_original . $solution->getFilename())
  1613                 || copy($filepath_target . $solution->getFilename(), $filepath_target . $solution->getFilename())) {
  1614                 $this->log->root()->error(
'File for suggested solutions could not be cloned:');
  1615                 $this->log->root()->error(
"Question-Id: {$this->id}; Question-Title: {$this->title}; File: {$filepath_original}{$solution->getFilename()}");
  1624             $solution = $solution->withQuestionId($target_question_id);
  1625             $update[] = $solution;
  1632         if (preg_match(
"/il_(\d+)_(\w+)_(\d+)/", $internal_link, $matches) === 
false) {
  1633             return $internal_link;
  1635         switch ($matches[2]) {
  1652         if ($resolved_link === null) {
  1653             return "il__{$matches[2]}_{$matches[3]}";
  1655         return $internal_link;
  1664         $result_pre = $this->db->queryF(
  1665             "SELECT internal_link, suggested_solution_id FROM qpl_sol_sug WHERE question_fi = %s",
  1669         if ($this->db->numRows($result_pre) < 1) {
  1673         while ($row = $this->db->fetchAssoc($result_pre)) {
  1674             $internal_link = $row[
"internal_link"];
  1676             if ($internal_link === $resolved_link) {
  1680             $this->db->manipulateF(
  1681                 "UPDATE qpl_sol_sug SET internal_link = %s WHERE suggested_solution_id = %s",
  1683                 [$resolved_link, $row[
"suggested_solution_id"]]
  1687         if ($resolvedlinks === 0) {
  1693         $result_post = $this->db->queryF(
  1694             "SELECT internal_link FROM qpl_sol_sug WHERE question_fi = %s",
  1698         if ($this->db->numRows($result_post) < 1) {
  1702         while ($row = $this->db->fetchAssoc($result_post)) {
  1703             if (preg_match(
"/il_(\d*?)_(\w+)_(\d+)/", $row[
"internal_link"], $matches)) {
  1712             "lm" => 
"LearningModule",
  1713             "pg" => 
"PageObject",
  1714             "st" => 
"StructureObject",
  1715             "git" => 
"GlossaryItem",
  1716             "mob" => 
"MediaObject"  1719         if (preg_match(
"/il__(\w+)_(\d+)/", $target, $matches)) {
  1720             $type = $matches[1];
  1721             $target_id = $matches[2];
  1722             switch ($linktypes[$matches[1]]) {
  1724                     $href = 
"./ilias.php?baseClass=ilLMPresentationGUI&obj_type=" . $linktypes[$type]
  1725                         . 
"&cmd=media&ref_id=" . $this->questionpool_request->getRefId()
  1726                         . 
"&mob_id=" . $target_id;
  1728                 case "StructureObject":
  1729                 case "GlossaryItem":
  1731                 case "LearningModule":
  1733                     $href = 
"./goto.php?target=" . $type . 
"_" . $target_id;
  1746         $original_parent_id = self::lookupParentObjId($this->
getOriginalId());
  1748         if ($original_parent_id === null) {
  1753         $original = clone $this;
  1756         $original->setOriginalId(null);
  1757         $original->setObjId($original_parent_id);
  1759         $original->saveToDb();
  1762         $original->createPageObject();
  1763         $original->clonePageOfQuestion($this->
getId());
  1767         $this->
cloneHints($this->
id, $this->original_id);
  1778         $ilCtrl = $DIC[
'ilCtrl'];
  1779         $ilDB = $DIC[
'ilDB'];
  1781         $questionrepository = QuestionPoolDIC::dic()[
'question.general_properties.repository'];
  1782         $question_type = $questionrepository->
getForQuestionId($question_id)?->getClassName() ?? 
'';
  1783         if ($question_type === 
'') {
  1787         $question = 
new $question_type();
  1788         $question->loadFromDb($question_id);
  1790         $feedbackObjectClassname = self::getFeedbackClassNameByQuestionType($question_type);
  1791         $question->feedbackOBJ = 
new $feedbackObjectClassname($question, $ilCtrl, 
$ilDB, $lng);
  1808         return self::_getSolutionMaxPass($this->
getId(), $active_id);
  1820         $ilDB = $DIC[
'ilDB'];
  1822         $result = 
$ilDB->queryF(
  1823             "SELECT MAX(pass) maxpass FROM tst_test_result WHERE active_fi = %s AND question_fi = %s",
  1824             [
'integer',
'integer'],
  1825             [$active_id, $question_id]
  1827         if ($result->numRows() === 1) {
  1828             $row = 
$ilDB->fetchAssoc($result);
  1829             return $row[
"maxpass"];
  1843         $requests_statistic_data = $hint_tracking->getRequestStatisticData();
  1844         return $reached_points - $requests_statistic_data->getRequestsPoints();
  1859         return $points > 0.0 ? 
$points : 0.0;
  1883         if ($count_system == 1) {
  1889         if ($score_cutting == 0) {
  1900         if (preg_match(
"/.*\.(png|jpg|gif|jpeg)$/i", $plain_image_filename, $matches)) {
  1901             $extension = 
"." . $matches[1];
  1905             $plain_image_filename = uniqid($plain_image_filename . microtime(
true), 
true);
  1908         return md5($plain_image_filename) . $extension;
  1921     public static function _setReachedPoints(
  1930         $ilDB = $DIC[
'ilDB'];
  1932         $question_properties_repository = QuestionPoolDIC::dic()[
'question.general_properties.repository'];
  1934         if ($points > $maxpoints) {
  1938         if ($pass === null) {
  1943         $result = 
$ilDB->queryF(
  1944             "SELECT points FROM tst_test_result WHERE active_fi = %s AND question_fi = %s AND pass = %s",
  1945             [
'integer',
'integer',
'integer'],
  1946             [$active_id, $question_id, $pass]
  1948         $manual = ($manualscoring) ? 1 : 0;
  1949         $rowsnum = $result->numRows();
  1951             $row = 
$ilDB->fetchAssoc($result);
  1952             $old_points = $row[
'points'];
  1953             if ($old_points !== $points) {
  1954                 $affectedRows = 
$ilDB->manipulateF(
  1955                     "UPDATE tst_test_result SET points = %s, manual = %s, tstamp = %s WHERE active_fi = %s AND question_fi = %s AND pass = %s",
  1956                     [
'float', 
'integer', 
'integer', 
'integer', 
'integer', 
'integer'],
  1957                     [$points, $manual, time(), $active_id, $question_id, $pass]
  1961             $next_id = 
$ilDB->nextId(
'tst_test_result');
  1962             $affectedRows = 
$ilDB->manipulateF(
  1963                 "INSERT INTO tst_test_result (test_result_id, active_fi, question_fi, points, pass, manual, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s)",
  1964                 [
'integer', 
'integer',
'integer', 
'float', 
'integer', 
'integer',
'integer'],
  1965                 [$next_id, $active_id, $question_id, $points, $pass, $manual, time()]
  1969         if (!self::isForcePassResultUpdateEnabled() && $old_points === $points && $rowsnum !== 0) {
  1973         $test_obj_id = $question_properties_repository->getForQuestionId($question_id)
  1974             ->getParentObjectId();
  1975         if ($test_obj_id === null) {
  1982         $test->updateTestPassResults($active_id, $pass);
  2000             || !(
new ilSetting(
'advanced_editing'))->
get(
'advanced_editing_javascript_editor') === 
'tinymce') {
  2001             $purified_content = nl2br($purified_content);
  2017         $result = $this->db->queryF(
  2018             "SELECT question_type_id FROM qpl_qst_type WHERE type_tag = %s",
  2022         if ($this->db->numRows($result) == 1) {
  2023             $row = $this->db->fetchAssoc($result);
  2024             return (
int) $row[
"question_type_id"];
  2030         int $source_question_id,
  2031         int $target_question_id
  2034         $this->db->manipulateF(
  2035             "DELETE FROM qpl_hints WHERE qht_question_fi = %s",
  2037             [$target_question_id]
  2041         $result = $this->db->queryF(
  2042             "SELECT * FROM qpl_hints WHERE qht_question_fi = %s",
  2044             [$source_question_id]
  2048         if ($this->db->numRows($result) < 1) {
  2052         while ($row = $this->db->fetchAssoc($result)) {
  2053             $next_id = $this->db->nextId(
'qpl_hints');
  2057                     'qht_hint_id' => [
'integer', $next_id],
  2058                     'qht_question_fi' => [
'integer', $target_question_id],
  2059                     'qht_hint_index' => [
'integer', $row[
"qht_hint_index"]],
  2060                     'qht_hint_points' => [
'float', $row[
"qht_hint_points"]],
  2061                     'qht_hint_text' => [
'text', $row[
"qht_hint_text"]],
  2072         $collected .= $this->feedbackOBJ->getGenericFeedbackContent($this->
getId(), 
false);
  2073         $collected .= $this->feedbackOBJ->getGenericFeedbackContent($this->
getId(), 
true);
  2074         $collected .= $this->feedbackOBJ->getAllSpecificAnswerFeedbackContents($this->
getId());
  2077         foreach ($questionHintList as $questionHint) {
  2079             $collected .= $questionHint->getText();
  2093         $result = $this->db->queryF(
  2094             "SELECT question_id FROM qpl_questions WHERE original_id = %s",
  2100         while ($row = $this->db->fetchAssoc($result)) {
  2101             $ids[] = $row[
"question_id"];
  2103         foreach ($ids as $question_id) {
  2105             $result = $this->db->queryF(
  2106                 "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",
  2110             while ($row = $this->db->fetchAssoc($result)) {
  2114             $result = $this->db->queryF(
  2115                 "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",
  2119             while ($row = $this->db->fetchAssoc($result)) {
  2123         foreach ($instances as $key => $value) {
  2137         $result = $this->db->queryF(
  2138             "SELECT * FROM tst_active WHERE active_id = %s",
  2142         if ($this->db->numRows($result)) {
  2143             $row = $this->db->fetchAssoc($result);
  2144             return [
"user_id" => $row[
"user_fi"], 
"test_id" => $row[
"test_fi"]];
  2152         return static::HAS_SPECIFIC_FEEDBACK;
  2157         return str_replace(
'ass', 
'ilAss', $questionType) . 
'Feedback';
  2162     public static function instantiateQuestionGUI(
int $question_id): ?
assQuestionGUI  2166         $ilCtrl = $DIC[
'ilCtrl'];
  2167         $ilDB = $DIC[
'ilDB'];
  2169         $ilUser = $DIC[
'ilUser'];
  2170         $ilLog = $DIC[
'ilLog'];
  2172         if ($question_id <= 0) {
  2173             $ilLog->warning(
'Instantiate question called without question id. (instantiateQuestionGUI@assQuestion)');
  2174             throw new InvalidArgumentException(
'Instantiate question called without question id. (instantiateQuestionGUI@assQuestion)');
  2177         $questionrepository = QuestionPoolDIC::dic()[
'question.general_properties.repository'];
  2178         $question_type = $questionrepository->
getForQuestionId($question_id)?->getClassName();
  2180         if ($question_type === null) {
  2184         $question_type_gui = $question_type . 
'GUI';
  2185         $question_gui = 
new $question_type_gui($question_id);
  2187         $feedback_object_classname = self::getFeedbackClassNameByQuestionType($question_type);
  2188         $question = $question_gui->getObject();
  2189         $question->feedbackOBJ = 
new $feedback_object_classname($question, $ilCtrl, 
$ilDB, $lng);
  2191         $assSettings = 
new ilSetting(
'assessment');
  2193         $processLockerFactory->setQuestionId($question_gui->getObject()->getId());
  2194         $processLockerFactory->setUserId($ilUser->getId());
  2195         $question->setProcessLocker($processLockerFactory->getLocker());
  2196         $question_gui->setObject($question);
  2198         return $question_gui;
  2208         $this->nr_of_tries = $a_nr_of_tries;
  2213         $this->export_image_path = 
$path;
  2219         $ilDB = $DIC[
'ilDB'];
  2221         if ($question_id < 1) {
  2225         $result = 
$ilDB->queryF(
  2226             "SELECT question_fi FROM tst_test_question WHERE question_fi = %s AND test_fi = %s",
  2227             [
'integer', 
'integer'],
  2228             [$question_id, $test_id]
  2230         return $ilDB->numRows($result) == 1;
  2240         return new \ilAssSelfAssessmentQuestionFormatter();
  2261         $this->feedbackOBJ->migrateContentForLearningModule($migrator, $this->
getId());
  2299         $ilDB = $DIC[
'ilDB'];
  2301         $query = 
"SELECT obj_fi FROM qpl_questions WHERE question_id = %s";
  2303         $res = 
$ilDB->queryF($query, [
'integer'], [$question_id]);
  2306         return $row[
'obj_fi'] ?? null;
  2314             foreach ($hintIds as $originalHintId => $duplicateHintId) {
  2317                 $originalXML = $originalPageObject->getXMLContent();
  2320                 $duplicatePageObject->setId($duplicateHintId);
  2321                 $duplicatePageObject->setParentId($this->
getId());
  2322                 $duplicatePageObject->setXMLContent($originalXML);
  2323                 $duplicatePageObject->createFromXML();
  2331         $assignmentList->setParentObjId($srcParentId);
  2332         $assignmentList->setQuestionIdFilter($srcQuestionId);
  2333         $assignmentList->loadFromDb();
  2335         foreach ($assignmentList->getAssignmentsByQuestionId($srcQuestionId) as $assignment) {
  2336             $assignment->setParentObjId($trgParentId);
  2337             $assignment->setQuestionId($trgQuestionId);
  2338             $assignment->saveToDb();
  2341             $this->skillUsageService->addUsage(
  2343                 $assignment->getSkillBaseId(),
  2344                 $assignment->getSkillTrefId()
  2349     public function syncSkillAssignments(
int $srcParentId, 
int $srcQuestionId, 
int $trgParentId, 
int $trgQuestionId): void
  2352         $assignmentList->setParentObjId($trgParentId);
  2353         $assignmentList->setQuestionIdFilter($trgQuestionId);
  2354         $assignmentList->loadFromDb();
  2356         foreach ($assignmentList->getAssignmentsByQuestionId($trgQuestionId) as $assignment) {
  2357             $assignment->deleteFromDb();
  2360             if (!$assignment->isSkillUsed()) {
  2361                 $this->skillUsageService->removeUsage(
  2362                     $assignment->getParentObjId(),
  2363                     $assignment->getSkillBaseId(),
  2364                     $assignment->getSkillTrefId()
  2376             $pageObject->setParentId($this->
getId());
  2377             $pageObject->setId($pageObjectId);
  2378             $pageObject->createFromXML();
  2385         return $numExistingSolutionRecords > 0;
  2391         $ilDB = $DIC[
'ilDB'];
  2394                         SELECT          count(active_fi) cnt  2398                         WHERE           active_fi = %s  2399                         AND                     question_fi = %s  2405             [
'integer',
'integer',
'integer'],
  2406             [$activeId, $questionId, $pass]
  2411         return (
int) $row[
'cnt'];
  2445             self::ADDITIONAL_CONTENT_EDITING_MODE_RTE,
  2446             self::ADDITIONAL_CONTENT_EDITING_MODE_IPE
  2469                         SELECT          qpl_questions.*,  2470                                                 {$this->getAdditionalTableName()}.*  2472                         LEFT JOIN       {$this->getAdditionalTableName()}  2473                         ON                      {$this->getAdditionalTableName()}.question_fi = qpl_questions.question_id  2474                         WHERE                   qpl_questions.question_id = %s  2490         if ($this->
getStep() !== null) {
  2494                                 WHERE active_fi = %s  2495                                 AND question_fi = %s  2501             return $this->db->queryF(
  2503                 [
'integer', 
'integer', 
'integer', 
'integer', 
'integer'],
  2504                 [$active_id, $this->
getId(), $pass, $this->
getStep(), (
int) $authorized]
  2511             WHERE active_fi = %s  2512             AND question_fi = %s  2517         return $this->db->queryF(
  2519             [
'integer', 
'integer', 
'integer', 
'integer'],
  2520             [$active_id, $this->
getId(), $pass, (
int) $authorized]
  2526         return $this->db->manipulateF(
  2527             "DELETE FROM tst_solutions WHERE solution_id = %s",
  2539         $result = $this->db->queryF(
  2540             "SELECT * FROM tst_solutions WHERE solution_id = %s",
  2545         if ($this->db->numRows($result) > 0) {
  2546             return $this->db->fetchAssoc($result);
  2554         $this->
getProcessLocker()->executeUserSolutionUpdateLockOperation(
function () use ($active_id, $pass) {
  2564         if ($this->
getStep() !== null) {
  2566                                 DELETE FROM tst_solutions  2567                                 WHERE active_fi = %s  2568                                 AND question_fi = %s  2574             return $this->db->manipulateF(
  2576                 [
'integer', 
'integer', 
'integer', 
'integer', 
'integer'],
  2577                 [$active_id, $this->
getId(), $pass, $this->
getStep(), (
int) $authorized]
  2582             DELETE FROM tst_solutions  2583             WHERE active_fi = %s  2584             AND question_fi = %s  2589         return $this->db->manipulateF(
  2591             [
'integer', 
'integer', 
'integer', 
'integer'],
  2592             [$active_id, $this->
getId(), $pass, (
int) $authorized]
  2599         $next_id = $this->db->nextId(
"tst_solutions");
  2602             "solution_id" => [
"integer", $next_id],
  2603             "active_fi" => [
"integer", $active_id],
  2604             "question_fi" => [
"integer", $this->
getId()],
  2605             "value1" => [
"clob", $value1],
  2606             "value2" => [
"clob", $value2],
  2607             "pass" => [
"integer", $pass],
  2608             "tstamp" => [
"integer", ((
int) $tstamp > 0) ? (
int) $tstamp : time()],
  2609             'authorized' => [
'integer', (
int) $authorized]
  2612         if ($this->
getStep() !== null) {
  2613             $fieldData[
'step'] = [
"integer", $this->
getStep()];
  2616         return $this->db->insert(
"tst_solutions", $fieldData);
  2623             "value1" => [
"clob", $value1],
  2624             "value2" => [
"clob", $value2],
  2625             "tstamp" => [
"integer", time()],
  2626             'authorized' => [
'integer', (
int) $authorized]
  2629         if ($this->
getStep() !== null) {
  2630             $fieldData[
'step'] = [
"integer", $this->
getStep()];
  2633         return $this->db->update(
"tst_solutions", $fieldData, [
  2634             'solution_id' => [
'integer', $solutionId]
  2642             'authorized' => [
'integer', (
int) $authorized]
  2646             $fieldData[
'tstamp'] = [
'integer', time()];
  2650             'question_fi' => [
'integer', $this->
getId()],
  2651             'active_fi' => [
'integer', $activeId],
  2652             'pass' => [
'integer', $pass]
  2655         if ($this->
getStep() !== null) {
  2656             $whereData[
'step'] = [
"integer", $this->
getStep()];
  2659         return $this->db->update(
'tst_solutions', $fieldData, $whereData);
  2678         foreach ($this->
getSolutionValues($activeId, $passIndex, 
false) as $solutionRec) {
  2679             if ($solutionRec[
'value1'] == 
'' && $solutionRec[
'value2'] == 
'') {
  2687         return !strlen($solutionRecord[
'value1']) && !strlen($solutionRecord[
'value2']);
  2692         $types = [
"integer", 
"integer", 
"integer", 
"integer"];
  2693         $values = [$activeId, $this->
getId(), $passIndex, (
int) $authorized];
  2694         $valuesCondition = [];
  2696         foreach ($matchValues as $valueField => $value) {
  2697             switch ($valueField) {
  2700                     $valuesCondition[] = 
"{$valueField} = %s";
  2710         $valuesCondition = implode(
' AND ', $valuesCondition);
  2713                         DELETE FROM tst_solutions  2714                         WHERE active_fi = %s  2715                         AND question_fi = %s  2718                         AND $valuesCondition  2721         if ($this->
getStep() !== null) {
  2722             $query .= 
" AND step = %s ";
  2723             $types[] = 
'integer';
  2727         $this->db->manipulateF($query, $types, $values);
  2733             $this->
saveCurrentSolution($activeId, $passIndex, $rec[
'value1'], $rec[
'value2'], 
true, $rec[
'tstamp']);
  2741         if (!count($intermediateSolution)) {
  2747             if ($considerDummyRecordCreation) {
  2761         $this->step = 
$step;
  2775         $time_array = explode(
':', $time);
  2776         if (count($time_array) == 3) {
  2777             $sec += (
int) $time_array[0] * 3600;
  2778             $sec += (
int) $time_array[1] * 60;
  2779             $sec += (
int) $time_array[2];
  2786         return json_encode([]);
  2793         return (
bool) $solutionAvailability[
'intermediate'];
  2798         if ($pass === null) {
  2802         return (
bool) $solutionAvailability[
'authorized'];
  2808         return $solutionAvailability[
'authorized'] || $solutionAvailability[
'intermediate'];
  2814         $result = $this->db->queryF(
  2815             "SELECT MAX(step) max_step FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
  2816             [
"integer", 
"integer", 
"integer"],
  2817             [$active_id, $pass, $this->
getId()]
  2820         $row = $this->db->fetchAssoc($result);
  2822         return (
int) $row[
'max_step'];
  2833             'authorized' => 
false,
  2834             'intermediate' => false
  2838                         SELECT authorized, COUNT(*) cnt  2840                         WHERE active_fi = %s  2841                         AND question_fi = %s  2845         if ($this->
getStep() !== null) {
  2846             $query .= 
" AND step = " . $this->db->quote((
int) $this->
getStep(), 
'integer') . 
" ";
  2853         $result = $this->db->queryF($query, [
'integer', 
'integer', 
'integer'], [$activeId, $this->
getId(), $pass]);
  2855         while ($row = $this->db->fetchAssoc($result)) {
  2856             if ($row[
'authorized']) {
  2857                 $return[
'authorized'] = $row[
'cnt'] > 0;
  2859                 $return[
'intermediate'] = $row[
'cnt'] > 0;
  2877         $query = 
"DELETE FROM tst_solutions WHERE question_fi = %s";
  2878         $this->db->manipulateF($query, [
'integer'], [$this->
getId()]);
  2884                         DELETE FROM tst_solutions  2885                         WHERE active_fi = %s  2886                         AND question_fi = %s  2890         if ($this->
getStep() !== null) {
  2891             $query .= 
" AND step = " . $this->db->quote((
int) $this->
getStep(), 
'integer') . 
" ";
  2894         return $this->db->manipulateF(
  2896             [
'integer', 
'integer', 
'integer'],
  2897             [$activeId, $this->
getId(), $pass]
  2910         $test->updateTestPassResults(
  2921                         DELETE FROM tst_test_result  2922                         WHERE active_fi = %s  2923                         AND question_fi = %s  2927         if ($this->
getStep() !== null) {
  2928             $query .= 
" AND step = " . $this->db->quote((
int) $this->
getStep(), 
'integer') . 
" ";
  2931         return $this->db->manipulateF(
  2933             [
'integer', 
'integer', 
'integer'],
  2934             [$activeId, $this->
getId(), $pass]
  2942         foreach ($indexedValues as $value1 => $value2) {
  2943             $valuePairs[] = [
'value1' => $value1, 
'value2' => $value2];
  2951         $indexed_values = [];
  2953         foreach ($value_pairs as $valuePair) {
  2954             $indexed_values[$valuePair[
'value1']] = $valuePair[
'value2'];
  2957         return $indexed_values;
  2962         $this->db->manipulateF(
  2963             "UPDATE qpl_questions SET tstamp = %s  WHERE question_id = %s",
  2964             [
'integer', 
'integer'],
  2965             [time(), $this->
getId()]
  2971         if ($this->test_question_config === null) {
  2990         $question_id = $this->
getId();
  3010         return preg_replace(self::TRIM_PATTERN, 
'', $value);
  3015         return !is_null($this->original_id)
  3016             && $this->questionrepository->questionExistsInPool($this->original_id)
  3031             $this->current_user->getId(),
  3035             $this->
answerToLog($additional_info, $active_id, $pass)
  3047             $this->current_user->getId(),
  3050             $this->
toLog($additional_info)
  3060             AdditionalInformationGenerator::KEY_PASS => $pass,
  3061             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
 
static getListByQuestionId($questionId)
instantiates a question hint list for the passed question id 
 
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)
 
cloneHints(int $source_question_id, int $target_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()
 
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
 
ensureHintPageObjectExists($pageObjectId)
 
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 deleteHintsByQuestionIds(array $questionIds)
Deletes all question hints relating to questions included in given question ids. 
 
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()
 
importHint(int $question_id, array $hint_array)
 
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)
 
duplicateQuestionHints(int $original_question_id, int $duplicate_question_id)
 
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)
 
fromXML(string $importdirectory, int $user_id, ilQTIItem $item, int $questionpool_id, ?int $tst_id, ?ilObject &$tst_object, int &$question_counter, array $import_mapping, array $solutionhints=[])
 
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)
 
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)
 
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
 
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. 
 
deductHintPointsFromReachedPoints(ilAssQuestionPreviewSession $preview_session, $reached_points)
 
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()
 
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...
 
static duplicateListForQuestion($originalQuestionId, $duplicateQuestionId)
duplicates a hint list from given original question id to given duplicate question id and returns an ...
 
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)
 
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