19declare(strict_types=1);
160 public function __construct(
int $id = 0,
bool $a_call_by_reference =
true)
166 $this->
ctrl = $DIC[
'ilCtrl'];
168 $this->
settings = $DIC[
'ilSetting'];
169 $this->bench =
$DIC[
'ilBench'];
170 $this->component_repository =
$DIC[
'component.repository'];
171 $this->component_factory =
$DIC[
'component.factory'];
172 $this->filesystem_web =
$DIC->filesystem()->web();
173 $this->lo_metadata =
$DIC->learningObjectMetadata();
174 $this->media_object_repository =
$DIC->mediaObjects()->internal()->repo()->mediaObject();
175 $this->irss =
$DIC->resourceStorage();
178 $this->participant_access_filter = $local_dic[
'participant.access_filter.factory'];
179 $this->test_man_scoring_done_helper = $local_dic[
'scoring.manual.done_helper'];
180 $this->
logger = $local_dic[
'logging.logger'];
181 $this->log_viewer = $local_dic[
'logging.viewer'];
182 $this->global_settings_repo = $local_dic[
'settings.global.repository'];
183 $this->marks_repository = $local_dic[
'marks.repository'];
184 $this->settings_factory = $local_dic[
'settings.factory'];
185 $this->questionrepository = $local_dic[
'question.general_properties.repository'];
186 $this->testrequest = $local_dic[
'request_data_collector'];
187 $this->participant_repository = $local_dic[
'participant.repository'];
188 $this->export_factory = $local_dic[
'exportimport.factory'];
189 $this->test_result_repository = $local_dic[
'results.data.repository'];
190 $this->main_settings_repository = $local_dic[
'settings.main.repository'];
191 $this->score_settings_repository = $local_dic[
'settings.scoring.repository'];
195 $this->
lng->loadLanguageModule(
"assessment");
196 $this->score_settings =
null;
203 $this->component_repository,
205 $this->questionrepository
211 return TestDIC::dic();
226 return $this->question_set_config_factory->getQuestionSetConfig();
249 $id = parent::create();
256 if (!parent::update()) {
268 $this->main_settings =
null;
269 $this->score_settings =
null;
270 $this->mark_schema =
null;
274 public function delete():
bool
277 if (!parent::delete()) {
288 $qsaImportFails->deleteRegisteredImportFails();
290 $sltImportFails->deleteRegisteredImportFails();
292 if ($this->
logger->isLoggingEnabled()) {
293 $this->
logger->logTestAdministrationInteraction(
294 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
296 $this->user->getId(),
299 AdditionalInformationGenerator::KEY_TEST_TITLE => $test_title = $this->title
311 $participantData->load($this->
getTestId());
314 $this->db->manipulateF(
315 "DELETE FROM tst_mark WHERE test_fi = %s",
320 $this->db->manipulateF(
321 "DELETE FROM tst_tests WHERE test_id = %s",
326 $this->db->manipulateF(
327 "DELETE FROM tst_test_settings WHERE id = %s",
333 $directory = $tst_data_dir .
"/tst_" . $this->
getId();
334 if (is_dir($directory)) {
342 foreach ($mobs as $mob) {
360 if (!is_writable($tst_data_dir)) {
361 $this->
ilias->raiseError(
"Test Data Directory (" . $tst_data_dir
362 .
") not writeable.", $this->
ilias->error_obj->MESSAGE);
366 $tst_dir = $tst_data_dir .
"/tst_" . $this->
getId();
368 if (!@is_dir($tst_dir)) {
369 $this->
ilias->raiseError(
"Creation of Test Directory failed.", $this->
ilias->error_obj->MESSAGE);
372 $export_dir = $tst_dir .
"/export";
374 if (!@is_dir($export_dir)) {
375 $this->
ilias->raiseError(
"Creation of Export Directory failed.", $this->
ilias->error_obj->MESSAGE);
385 public function getExportFiles(
string $dir =
''): array
388 if (!@is_dir($dir) || !is_writable($dir)) {
393 foreach (
new DirectoryIterator($dir) as $file) {
397 if ($file->isDir()) {
401 $files[] = $file->getBasename();
421 if (!is_writable($tst_data_dir)) {
423 .
") not writeable.",
$ilias->error_obj->FATAL);
427 $tst_dir = $tst_data_dir .
"/tst_import";
429 if (!@is_dir($tst_dir)) {
456 if ($this->
isComplete($test_question_set_config)) {
460 $this->db->manipulateF(
461 'UPDATE tst_tests SET complete = %s WHERE test_id = %s',
463 [$complete, $this->test_id]
468 public function saveToDb(
bool $properties_only =
false): void
470 if ($this->test_id === -1) {
472 $next_id = $this->db->nextId(
'tst_tests');
477 'test_id' => [
'integer', $next_id],
478 'obj_fi' => [
'integer', $this->
getId()],
479 'created' => [
'integer', time()],
480 'tstamp' => [
'integer', time()],
485 $this->test_id = $next_id;
488 $this->settings_factory->createDefaultMainSettings(),
496 $aresult = $this->db->queryF(
497 "SELECT active_id FROM tst_active WHERE test_fi = %s AND tries >= %s AND submitted = %s",
498 [
'integer',
'integer',
'integer'],
501 while ($row = $this->db->fetchAssoc($aresult)) {
502 $this->db->manipulateF(
503 "UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
504 [
'integer',
'timestamp',
'integer'],
505 [1, date(
'Y-m-d H:i:s'), $row[
"active_id"]]
510 $aresult = $this->db->queryF(
511 "SELECT active_id FROM tst_active WHERE test_fi = %s AND tries < %s AND submitted = %s",
512 [
'integer',
'integer',
'integer'],
515 while ($row = $this->db->fetchAssoc($aresult)) {
516 $this->db->manipulateF(
517 "UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
518 [
'integer',
'timestamp',
'integer'],
519 [0,
null, $row[
"active_id"]]
524 $aresult = $this->db->queryF(
525 "SELECT active_id FROM tst_active WHERE test_fi = %s AND submitted = %s",
526 [
'integer',
'integer'],
529 while ($row = $this->db->fetchAssoc($aresult)) {
530 $this->db->manipulateF(
531 "UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
532 [
'integer',
'timestamp',
'integer'],
533 [0,
null, $row[
"active_id"]]
540 if ($properties_only) {
548 $this->marks_repository->storeMarkSchema($this->
getMarkSchema());
553 $this->db->manipulateF(
554 'DELETE FROM tst_test_question WHERE test_fi = %s',
558 foreach ($this->questions as $key => $value) {
559 $next_id = $this->db->nextId(
'tst_test_question');
560 $this->db->insert(
'tst_test_question', [
561 'test_question_id' => [
'integer', $next_id],
562 'test_fi' => [
'integer', $this->
getTestId()],
563 'question_fi' => [
'integer', $value],
564 'sequence' => [
'integer', $key],
565 'tstamp' => [
'integer', time()]
578 foreach ($question_ids as
$id) {
579 $question = assQuestion::instantiateQuestionGUI(
$id);
581 $title = $question->getObject()->getTitle();
583 while (in_array(
$title .
' (' . $i .
')', $question_titles)) {
587 $title .=
' (' . $i .
')';
589 $question_titles[] =
$title;
591 $new_id = $question->getObject()->duplicate(
false,
$title);
593 $clone = assQuestion::instantiateQuestionGUI($new_id);
594 $question = $clone->getObject();
595 $question->setObjId($this->
getId());
596 $clone->setObject($question);
597 $clone->getObject()->saveToDb();
611 $result = $this->db->queryF(
612 "SELECT test_result_id FROM tst_test_result WHERE active_fi = %s AND pass = %s",
613 [
'integer',
'integer'],
616 return $result->numRows();
621 $result = $this->db->queryF(
622 "SELECT test_id FROM tst_tests WHERE obj_fi = %s",
626 if ($result->numRows() === 1) {
627 $data = $this->db->fetchObject($result);
639 $this->questions = [];
641 if ($active_id === 0) {
644 if (is_null($pass)) {
647 $result = $this->db->queryF(
648 'SELECT tst_test_rnd_qst.* '
649 .
'FROM tst_test_rnd_qst, qpl_questions '
650 .
'WHERE tst_test_rnd_qst.active_fi = %s '
651 .
'AND qpl_questions.question_id = tst_test_rnd_qst.question_fi '
652 .
'AND tst_test_rnd_qst.pass = %s '
653 .
'ORDER BY sequence',
654 [
'integer',
'integer'],
658 $result = $this->db->queryF(
659 'SELECT tst_test_question.* '
660 .
'FROM tst_test_question, qpl_questions '
661 .
'WHERE tst_test_question.test_fi = %s '
662 .
'AND qpl_questions.question_id = tst_test_question.question_fi '
663 .
'ORDER BY sequence',
669 if ($this->test_id !== -1) {
671 while (
$data = $this->db->fetchAssoc($result)) {
672 $this->questions[$index++] =
$data[
"question_fi"];
679 $page_id = $this->
getMainSettings()->getIntroductionSettings()->getIntroductionPageId();
680 return $page_id !==
null
687 $page_id = $this->
getMainSettings()->getIntroductionSettings()->getIntroductionPageId();
688 if ($page_id ===
null) {
696 $page_id = $this->
getMainSettings()->getFinishingSettings()->getConcludingRemarksPageId();
697 return $page_id !==
null
704 $page_id = $this->
getMainSettings()->getFinishingSettings()->getConcludingRemarksPageId();
705 if ($page_id ===
null) {
714 $page_object->setParentId($this->
getId());
715 $new_page_id = $page_object->createPageWithNextId();
716 (
new ilTestPage($source_page_id))->copy($new_page_id);
730 return $this->
getMainSettings()->getParticipantFunctionalitySettings()->getPostponedQuestionsMoveToEnd();
735 return $this->
getScoreSettings()->getResultSummarySettings()->getScoreReporting()->isReportingEnabled();
740 return $this->
getMainSettings()->getQuestionBehaviourSettings()->getInstantFeedbackPointsEnabled();
745 return $this->
getMainSettings()->getQuestionBehaviourSettings()->getInstantFeedbackGenericEnabled();
750 return $this->
getMainSettings()->getQuestionBehaviourSettings()->getInstantFeedbackSolutionEnabled();
763 return TestDIC::dic()[
'settings.scoring.repository']->getFor(
810 if ($this->mark_schema ===
null) {
811 $this->mark_schema = $this->marks_repository->getMarkSchemaFor($this->
getTestId());
820 $this->mark_schema =
null;
825 return $this->
getMainSettings()->getTestBehaviourSettings()->getNumberOfTries();
830 return $this->
getMainSettings()->getTestBehaviourSettings()->getBlockAfterPassedEnabled();
835 return $this->
getMainSettings()->getTestBehaviourSettings()->getKioskModeEnabled();
840 return $this->
getMainSettings()->getTestBehaviourSettings()->getShowTitleInKioskMode();
844 return $this->
getMainSettings()->getTestBehaviourSettings()->getShowParticipantNameInKioskMode();
849 return $this->
getMainSettings()->getParticipantFunctionalitySettings()->getUsePreviousAnswerAllowed();
854 return $this->
getMainSettings()->getQuestionBehaviourSettings()->getQuestionTitleOutputMode();
864 return $this->
getMainSettings()->getTestBehaviourSettings()->getProcessingTime();
869 $processing_time = $this->
getMainSettings()->getTestBehaviourSettings()->getProcessingTime();
870 if ($processing_time ===
null
871 || $processing_time ===
''
872 || !preg_match(
'/(\d{2}):(\d{2}):(\d{2})/is', $processing_time, $matches)
887 $processing_time = $this->
getMainSettings()->getTestBehaviourSettings()->getProcessingTime() ??
'';
888 if (preg_match(
"/(\d{2}):(\d{2}):(\d{2})/", (
string) $processing_time, $matches)) {
890 return ($matches[1] * 3600) + ($matches[2] * 60) + $matches[3] + $extratime;
898 return $this->
getMainSettings()->getTestBehaviourSettings()->getProcessingTimeEnabled();
903 return $this->
getMainSettings()->getTestBehaviourSettings()->getResetProcessingTime();
908 return $this->
getMainSettings()->getAccessSettings()->getStartTimeEnabled();
913 $start_time = $this->
getMainSettings()->getAccessSettings()->getStartTime();
914 return $start_time !==
null ? $start_time->getTimestamp() : 0;
919 return $this->
getMainSettings()->getAccessSettings()->getEndTimeEnabled();
924 $end_time = $this->
getMainSettings()->getAccessSettings()->getEndTime();
925 return $end_time !==
null ? $end_time->getTimestamp() : 0;
930 return $this->
getMainSettings()->getAccessSettings()->getPasswordEnabled();
944 $this->test_result_repository
958 $participant_data->load($this->test_id);
960 $question->removeAllExistingSolutions();
967 $participant_data->getActiveIds(),
968 $this->reindexFixedQuestionOrdering()
974 $question->delete($question_id);
983 if ($this->
logger->isLoggingEnabled()) {
984 $this->
logger->logTestAdministrationInteraction(
985 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
987 $this->user->getId(),
988 TestAdministrationInteractionTypes::QUESTION_REMOVED_IN_CORRECTIONS,
990 AdditionalInformationGenerator::KEY_QUESTION_TITLE => $question->getTitleForHTMLOutput(),
991 AdditionalInformationGenerator::KEY_QUESTION_TEXT => $question->getQuestion(),
992 AdditionalInformationGenerator::KEY_QUESTION_ID => $question->getId(),
993 AdditionalInformationGenerator::KEY_QUESTION_TYPE => $question->getQuestionType()
1011 $this->questionrepository
1014 foreach ($active_ids as $active_id) {
1016 $passSelector->setActiveId($active_id);
1018 foreach ($passSelector->getExistingPasses() as $pass) {
1019 $test_sequence = $test_sequence_factory->getSequenceByActiveIdAndPass($active_id, $pass);
1022 $test_sequence->removeQuestion($question_id, $reindexed_sequence_position_map);
1033 foreach ($question_ids as $question_id) {
1034 $this->removeQuestion((
int) $question_id);
1037 $this->reindexFixedQuestionOrdering();
1043 $question = self::_instanciateQuestion($question_id);
1044 $question_title = $question->getTitleForHTMLOutput();
1045 $question->delete($question_id);
1046 if ($this->
logger->isLoggingEnabled()) {
1047 $this->
logger->logTestAdministrationInteraction(
1048 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
1050 $this->user->getId(),
1051 TestAdministrationInteractionTypes::QUESTION_REMOVED,
1053 AdditionalInformationGenerator::KEY_QUESTION_TITLE => $question_title
1058 }
catch (InvalidArgumentException
$e) {
1059 $this->
logger->error($e->getMessage());
1060 $this->
logger->error($e->getTraceAsString());
1074 $this->removeTestResultsByUserIds($user_ids);
1077 $participantData->setUserIdsFilter($user_ids);
1078 $participantData->load($this->getTestId());
1080 $this->removeTestActives($participantData->getActiveIds());
1083 if ($this->
logger->isLoggingEnabled()) {
1084 $this->
logger->logTestAdministrationInteraction(
1085 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
1087 $this->user->getId(),
1088 TestAdministrationInteractionTypes::PARTICIPANT_DATA_REMOVED,
1090 AdditionalInformationGenerator::KEY_USERS => $participantData->getUserIds()
1102 $user_ids = array_map(
1106 $this->participant_repository->removeExtraTimeByUserId($this->getTestId(), $user_ids);
1109 if ($participant_data->
getUserIds() !== []) {
1112 if ($test_lp instanceof
ilTestLP) {
1113 $test_lp->setTestObject($this);
1114 $test_lp->resetLPDataForUserIds($participant_data->
getUserIds(),
false);
1117 $this->participant_repository->removeExtraTimeByUserId($this->getTestId(), $participant_data->
getUserIds());
1121 $this->removeTestActives($participant_data->
getActiveIds());
1123 $user_ids = array_map(
1127 $this->participant_repository->removeExtraTimeByUserId($this->getTestId(), $user_ids);
1130 if ($this->
logger->isLoggingEnabled()) {
1131 $this->
logger->logTestAdministrationInteraction(
1132 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
1134 $this->user->getId(),
1135 TestAdministrationInteractionTypes::PARTICIPANT_DATA_REMOVED,
1137 AdditionalInformationGenerator::KEY_USERS => $participant_data->
getUserIds(),
1148 $participantData->setUserIdsFilter($user_ids);
1149 $participantData->load($this->getTestId());
1151 $in_user_ids = $this->db->in(
'usr_id', $participantData->getUserIds(),
false,
'integer');
1152 $this->db->manipulateF(
1153 "DELETE FROM usr_pref WHERE {$in_user_ids} AND keyword = %s",
1155 [
'tst_password_' . $this->getTestId()]
1158 if ($participantData->getActiveIds() !== []) {
1159 $this->removeTestResultsByActiveIds($participantData->getActiveIds());
1165 $in_active_ids = $this->db->in(
'active_fi', $active_ids,
false,
'integer');
1167 $this->db->manipulate(
"DELETE FROM tst_solutions WHERE {$in_active_ids}");
1168 $this->db->manipulate(
"DELETE FROM tst_qst_solved WHERE {$in_active_ids}");
1169 $this->db->manipulate(
"DELETE FROM tst_test_result WHERE {$in_active_ids}");
1170 $this->db->manipulate(
"DELETE FROM tst_pass_result WHERE {$in_active_ids}");
1171 $this->db->manipulate(
"DELETE FROM tst_result_cache WHERE {$in_active_ids}");
1172 $this->db->manipulate(
"DELETE FROM tst_sequence WHERE {$in_active_ids}");
1173 $this->db->manipulate(
"DELETE FROM tst_times WHERE {$in_active_ids}");
1174 $this->db->manipulate(
1176 .
' WHERE ' . $this->db->in(
'active_id', $active_ids,
false,
'integer')
1179 if ($this->isRandomTest()) {
1180 $this->db->manipulate(
"DELETE FROM tst_test_rnd_qst WHERE {$in_active_ids}");
1183 $this->test_result_repository->removeTestResults($active_ids, $this->
getId());
1185 foreach ($active_ids as $active_id) {
1187 if (is_dir(
CLIENT_WEB_DIR .
"/assessment/tst_" . $this->getTestId() .
"/$active_id")) {
1198 $IN_activeIds = $this->db->in(
'active_id', $active_ids,
false,
'integer');
1199 $this->db->manipulate(
"DELETE FROM tst_active WHERE $IN_activeIds");
1212 $result = $this->db->queryF(
1213 "SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
1214 [
'integer',
'integer'],
1215 [$this->getTestId(), $question_id]
1217 $data = $this->db->fetchObject($result);
1218 if (
$data->sequence > 1) {
1220 $result = $this->db->queryF(
1221 "SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
1222 [
'integer',
'integer'],
1223 [$this->getTestId(),
$data->sequence - 1]
1225 $data_previous = $this->db->fetchObject($result);
1227 $this->db->manipulateF(
1228 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
1229 [
'integer',
'integer'],
1230 [
$data->sequence, $data_previous->test_question_id]
1233 $this->db->manipulateF(
1234 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
1235 [
'integer',
'integer'],
1236 [
$data->sequence - 1,
$data->test_question_id]
1239 $this->loadQuestions();
1251 $current_question_result = $this->db->queryF(
1252 "SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
1253 [
'integer',
'integer'],
1254 [$this->getTestId(), $question_id]
1256 $current_question_data = $this->db->fetchObject($current_question_result);
1257 $next_question_result = $this->db->queryF(
1258 "SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
1259 [
'integer',
'integer'],
1260 [$this->getTestId(), $current_question_data->sequence + 1]
1262 if ($this->db->numRows($next_question_result) === 1) {
1264 $next_question_data = $this->db->fetchObject($next_question_result);
1266 $this->db->manipulateF(
1267 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
1268 [
'integer',
'integer'],
1269 [$current_question_data->sequence, $next_question_data->test_question_id]
1272 $this->db->manipulateF(
1273 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
1274 [
'integer',
'integer'],
1275 [$current_question_data->sequence + 1, $current_question_data->test_question_id]
1278 $this->loadQuestions();
1290 $duplicate_id = $question->duplicate(
true,
'',
'', -1, $this->
getId());
1291 return $duplicate_id;
1297 $duplicate_id = $question_id;
1299 $duplicate_id = $this->duplicateQuestionForTest($question_id);
1303 $result = $this->db->queryF(
1304 "SELECT MAX(sequence) seq FROM tst_test_question WHERE test_fi=%s",
1306 [$this->getTestId()]
1310 if ($result->numRows() == 1) {
1311 $data = $this->db->fetchObject($result);
1312 $sequence =
$data->seq + 1;
1315 $next_id = $this->db->nextId(
'tst_test_question');
1316 $this->db->manipulateF(
1317 "INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
1318 [
'integer',
'integer',
'integer',
'integer',
'integer'],
1319 [$next_id, $this->getTestId(), $duplicate_id, $sequence, time()]
1322 $this->db->manipulateF(
1323 "DELETE FROM tst_active WHERE test_fi = %s",
1325 [$this->getTestId()]
1327 $this->loadQuestions();
1328 $this->saveCompleteStatus($this->question_set_config_factory->getQuestionSetConfig());
1330 if ($this->
logger->isLoggingEnabled()) {
1331 $this->
logger->logTestAdministrationInteraction(
1332 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
1334 $this->user->getId(),
1335 TestAdministrationInteractionTypes::QUESTION_ADDED,
1337 AdditionalInformationGenerator::KEY_QUESTION_ID => $question_id
1339 ->toLog($this->logger->getAdditionalInformationGenerator())
1344 return $duplicate_id;
1350 if ($this->getQuestionSetType() === self::QUESTION_SET_TYPE_FIXED) {
1351 $result = $this->db->queryF(
1352 'SELECT qpl_questions.title FROM tst_test_question, qpl_questions '
1353 .
'WHERE tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id '
1354 .
'ORDER BY tst_test_question.sequence',
1356 [$this->getTestId()]
1358 while ($row = $this->db->fetchAssoc($result)) {
1359 array_push($titles, $row[
'title']);
1375 if ($this->getQuestionSetType() == self::QUESTION_SET_TYPE_FIXED) {
1376 $result = $this->db->queryF(
1377 'SELECT qpl_questions.title, qpl_questions.question_id '
1378 .
'FROM tst_test_question, qpl_questions '
1379 .
'WHERE tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id '
1380 .
'ORDER BY tst_test_question.sequence',
1382 [$this->getTestId()]
1384 while ($row = $this->db->fetchAssoc($result)) {
1385 $titles[$row[
'question_id']] = $row[
"title"];
1403 switch ($this->getTitleOutput()) {
1410 return $this->
lng->txt(
"ass_question") .
' ' . $nr;
1412 return $this->
lng->txt(
"ass_question");
1416 $txt = $this->
lng->txt(
"ass_question") .
' ' . $nr;
1418 $txt = $this->
lng->txt(
"ass_question");
1420 if ($points !=
'') {
1421 $lngv = $this->
lng->txt(
'points');
1423 $lngv = $this->
lng->txt(
'point');
1425 $txt .=
' - ' . $points .
' ' . $lngv;
1431 return $this->
lng->txt(
"ass_question");
1445 $result = $this->db->queryF(
1446 "SELECT qpl_questions.*, qpl_qst_type.type_tag FROM qpl_questions, qpl_qst_type WHERE qpl_questions.question_id = %s AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id",
1450 $row = $this->db->fetchObject($result);
1462 $existing_questions = [];
1463 $active_id = $this->getActiveIdOfUser($this->
user->getId());
1464 if ($this->isRandomTest()) {
1465 if (is_null($pass)) {
1468 $result = $this->db->queryF(
1469 "SELECT qpl_questions.original_id FROM qpl_questions, tst_test_rnd_qst WHERE tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.question_fi = qpl_questions.question_id AND tst_test_rnd_qst.pass = %s",
1470 [
'integer',
'integer'],
1474 $result = $this->db->queryF(
1475 "SELECT qpl_questions.original_id FROM qpl_questions, tst_test_question WHERE tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id",
1477 [$this->getTestId()]
1480 while (
$data = $this->db->fetchObject($result)) {
1481 if (
$data->original_id ===
null) {
1485 array_push($existing_questions,
$data->original_id);
1487 return $existing_questions;
1499 if ($question_id < 1) {
1502 $result = $this->db->queryF(
1503 "SELECT type_tag FROM qpl_questions, qpl_qst_type WHERE qpl_questions.question_id = %s AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id",
1507 if ($result->numRows() == 1) {
1508 $data = $this->db->fetchObject($result);
1509 return $data->type_tag;
1523 $next_id = $this->db->nextId(
'tst_times');
1524 $affectedRows = $this->db->manipulateF(
1525 "INSERT INTO tst_times (times_id, active_fi, started, finished, pass, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
1526 [
'integer',
'integer',
'timestamp',
'timestamp',
'integer',
'integer'],
1527 [$next_id, $active_id, date(
"Y-m-d H:i:s"), date(
"Y-m-d H:i:s"), $pass, time()]
1540 $affectedRows = $this->db->manipulateF(
1541 "UPDATE tst_times SET finished = %s, tstamp = %s WHERE times_id = %s",
1542 [
'timestamp',
'integer',
'integer'],
1543 [date(
'Y-m-d H:i:s'), time(), $times_id]
1555 if (is_null($pass)) {
1556 $result = $this->db->queryF(
1557 "SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
1558 [
'integer',
'integer'],
1562 $result = $this->db->queryF(
1563 "SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
1564 [
'integer',
'integer'],
1569 while ($row = $this->db->fetchAssoc($result)) {
1570 array_push($result_array, $row[
"question_fi"]);
1572 return $result_array;
1586 return ((($currentpass > 0) && ($num == 0)) || $this->isTestFinished($active_id)) ?
true :
false;
1597 if ($this->isRandomTest()) {
1598 $active_id = $this->getActiveIdOfUser($this->
user->getId());
1599 if ($active_id ===
null) {
1602 $this->loadQuestions($active_id, $pass);
1603 if (count($this->questions) === 0) {
1606 if (is_null($pass)) {
1607 $pass = self::_getPass($active_id);
1609 $result = $this->db->queryF(
1610 "SELECT qpl_questions.* FROM qpl_questions, tst_test_rnd_qst WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id AND tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.pass = %s AND " . $this->db->in(
'qpl_questions.question_id', $this->questions,
false,
'integer'),
1611 [
'integer',
'integer'],
1615 if (count($this->questions) === 0) {
1618 $result = $this->db->query(
"SELECT qpl_questions.* FROM qpl_questions, tst_test_question WHERE tst_test_question.question_fi = qpl_questions.question_id AND " . $this->db->in(
'qpl_questions.question_id', $this->questions,
false,
'integer'));
1621 while ($row = $this->db->fetchAssoc($result)) {
1622 $result_array[$row[
"question_id"]] = $row;
1624 return $result_array;
1642 if (is_array($tst_access_code) &&
1644 isset($tst_access_code[$this->getTestId()]) &&
1645 $tst_access_code[$this->getTestId()] !==
'') {
1646 $result = $this->db->queryF(
1647 'SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s',
1648 [
'integer',
'integer',
'text'],
1649 [
$user_id, $this->test_id, $tst_access_code[$this->getTestId()]]
1651 } elseif ((
string) $anonymous_id !==
'') {
1652 $result = $this->db->queryF(
1653 'SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s',
1654 [
'integer',
'integer',
'text'],
1655 [
$user_id, $this->test_id, $anonymous_id]
1661 $result = $this->db->queryF(
1662 'SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s',
1663 [
'integer',
'integer'],
1668 if ($result->numRows()) {
1669 $row = $this->db->fetchAssoc($result);
1670 return (
int) $row[
'active_id'];
1680 $ilUser =
$DIC[
'ilUser'];
1688 $result =
$ilDB->queryF(
1689 "SELECT tst_active.active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
1690 [
'integer',
'integer'],
1693 if ($result->numRows()) {
1694 $row =
$ilDB->fetchAssoc($result);
1695 return $row[
"active_id"];
1709 $keys = array_keys($array);
1712 foreach ($keys as $key) {
1713 $result[$key] = $array[$key];
1726 ?
int $attempt =
null,
1727 bool $ordered_sequence =
false,
1728 bool $consider_hidden_questions =
true,
1729 bool $consider_optional_questions =
true
1731 $test_result = $this->test_result_repository->getTestResult($active_id);
1733 if ($test_result ===
null) {
1734 $test_result = $this->test_result_repository->updateTestResultCache($active_id);
1737 if ($attempt ===
null) {
1738 $attempt = $test_result->getAttempt();
1742 $test_sequence = $test_sequence_factory->getSequenceByActiveIdAndPass($active_id, $attempt);
1744 $test_sequence->setConsiderHiddenQuestionsEnabled($consider_hidden_questions);
1745 $test_sequence->setConsiderOptionalQuestionsEnabled($consider_optional_questions);
1747 $test_sequence->loadFromDb();
1748 $test_sequence->loadQuestions();
1750 if ($ordered_sequence) {
1751 $sequence = $test_sequence->getOrderedSequenceQuestions();
1753 $sequence = $test_sequence->getUserSequenceQuestions();
1760 tst_test_result.question_fi,
1761 tst_test_result.points reached,
1762 tst_test_result.answered answered,
1763 tst_manual_fb.finalized_evaluation finalized_evaluation
1765 FROM tst_test_result
1767 LEFT JOIN tst_solutions
1768 ON tst_solutions.active_fi = tst_test_result.active_fi
1769 AND tst_solutions.question_fi = tst_test_result.question_fi
1771 LEFT JOIN tst_manual_fb
1772 ON tst_test_result.active_fi = tst_manual_fb.active_fi
1773 AND tst_test_result.question_fi = tst_manual_fb.question_fi
1775 WHERE tst_test_result.active_fi = %s
1776 AND tst_test_result.pass = %s
1779 $solutionresult = $this->db->queryF(
1781 [
'integer',
'integer'],
1782 [$active_id, $attempt]
1785 while ($row = $this->db->fetchAssoc($solutionresult)) {
1786 $arr_results[ $row[
'question_fi'] ] = $row;
1789 $result = $this->db->query(
1790 'SELECT qpl_questions.*, qpl_qst_type.type_tag, qpl_sol_sug.question_fi has_sug_sol' . PHP_EOL
1791 .
'FROM qpl_qst_type, qpl_questions' . PHP_EOL
1792 .
'LEFT JOIN qpl_sol_sug' . PHP_EOL
1793 .
'ON qpl_sol_sug.question_fi = qpl_questions.question_id' . PHP_EOL
1794 .
'WHERE qpl_qst_type.question_type_id = qpl_questions.question_type_fi' . PHP_EOL
1795 .
'AND ' . $this->db->in(
'qpl_questions.question_id', $sequence,
false,
'integer')
1800 while ($row = $this->db->fetchAssoc($result)) {
1801 if (!isset($arr_results[ $row[
'question_id'] ])) {
1802 $percentvalue = 0.0;
1805 $row[
'points'] ? $arr_results[$row[
'question_id']][
'reached'] / $row[
'points'] : 0
1808 if ($percentvalue < 0) {
1809 $percentvalue = 0.0;
1815 'max' => round($row[
'points'], 2),
1816 'reached' => round($arr_results[$row[
'question_id']][
'reached'] ?? 0, 2),
1817 'percent' => sprintf(
'%2.2f ', ($percentvalue) * 100) .
'%',
1819 'type' => $row[
'type_tag'],
1820 'qid' => $row[
'question_id'],
1821 'original_id' => $row[
'original_id'],
1822 'workedthrough' => isset($arr_results[$row[
'question_id']]) ? 1 : 0,
1823 'answered' => $arr_results[$row[
'question_id']][
'answered'] ?? 0,
1824 'finalized_evaluation' => $arr_results[$row[
'question_id']][
'finalized_evaluation'] ?? 0,
1827 $unordered[ $row[
'question_id'] ] =
$data;
1835 foreach ($sequence as $qid) {
1838 $pass_max += round($unordered[$qid][
'max'], 2);
1839 $pass_reached += round($unordered[$qid][
'reached'], 2);
1840 $found[] = $unordered[$qid];
1843 if ($this->getScoreCutting() == 1) {
1844 if ($pass_reached < 0) {
1849 $found[
'pass'][
'total_max_points'] = $pass_max;
1850 $found[
'pass'][
'total_reached_points'] = $pass_reached;
1851 $found[
'pass'][
'percent'] = ($pass_max > 0) ? $pass_reached / $pass_max : 0;
1852 $found[
'pass'][
'num_workedthrough'] = count($arr_results);
1853 $found[
'pass'][
'num_questions_total'] = count($unordered);
1855 $found[
'test'][
'total_max_points'] = $test_result->getMaxPoints();
1856 $found[
'test'][
'total_reached_points'] = $test_result->getReachedPoints();
1857 $found[
'test'][
'result_pass'] = $attempt;
1858 $found[
'test'][
'result_tstamp'] = $test_result->getTimestamp();
1859 $found[
'test'][
'passed'] = $test_result->isPassed();
1872 $result = $this->db->queryF(
1873 'SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s',
1875 [$this->getTestId()]
1877 $row = $this->db->fetchAssoc($result);
1878 return $row[
'total'];
1889 $result = $this->db->queryF(
1890 "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi AND tst_active.user_fi = %s",
1891 [
'integer',
'integer'],
1895 while ($row = $this->db->fetchAssoc($result)) {
1896 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"started"], $matches);
1905 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"finished"], $matches);
1914 $time += ($epoch_2 - $epoch_1);
1927 return $this->_getCompleteWorkingTimeOfParticipants($this->getTestId());
1939 $result = $this->db->queryF(
1940 "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi ORDER BY tst_times.active_fi, tst_times.started",
1946 while ($row = $this->db->fetchAssoc($result)) {
1947 if (!array_key_exists($row[
"active_fi"], $times)) {
1948 $times[$row[
"active_fi"]] = 0;
1950 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"started"], $matches);
1959 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"finished"], $matches);
1968 $times[$row[
"active_fi"]] += ($epoch_2 - $epoch_1);
1981 $result = $this->db->queryF(
1982 "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi AND tst_active.active_id = %s ORDER BY tst_times.active_fi, tst_times.started",
1983 [
'integer',
'integer'],
1984 [$this->getTestId(), $active_id]
1987 while ($row = $this->db->fetchAssoc($result)) {
1988 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"started"], $matches);
1997 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"finished"], $matches);
2006 $time += ($epoch_2 - $epoch_1);
2016 return $this->test_result_repository->fetchWorkingTime($active_id, $pass);
2025 $test_result = &$this->getTestResult($active_id, $pass);
2026 $result = $this->db->queryF(
2027 "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.active_id = %s AND tst_active.active_id = tst_times.active_fi",
2034 while ($row = $this->db->fetchObject($result)) {
2035 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
2044 if (!$first_visit) {
2045 $first_visit = $epoch_1;
2047 if ($epoch_1 < $first_visit) {
2048 $first_visit = $epoch_1;
2050 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
2060 $last_visit = $epoch_2;
2062 if ($epoch_2 > $last_visit) {
2063 $last_visit = $epoch_2;
2065 $times[$row->active_fi] += ($epoch_2 - $epoch_1);
2068 foreach ($times as $key => $value) {
2069 $max_time += $value;
2071 if ((!$test_result[
"test"][
"total_reached_points"]) or (!$test_result[
"test"][
"total_max_points"])) {
2074 $percentage = ($test_result[
"test"][
"total_reached_points"] / $test_result[
"test"][
"total_max_points"]) * 100.0;
2075 if ($percentage < 0) {
2079 $mark_obj = $this->getMarkSchema()->getMatchingMark($percentage);
2080 $first_date = getdate($first_visit);
2081 $last_date = getdate($last_visit);
2082 $qworkedthrough = 0;
2083 foreach ($test_result as $key => $value) {
2084 if (preg_match(
"/\d+/", $key)) {
2085 $qworkedthrough += $value[
"workedthrough"];
2088 if (!$qworkedthrough) {
2091 $atimeofwork = $max_time / $qworkedthrough;
2097 if ($mark_obj !==
null) {
2098 $result_mark = $mark_obj->getShortName();
2100 if ($mark_obj->getPassed()) {
2106 $percent_worked_through = 0;
2107 if (count($this->questions)) {
2108 $percent_worked_through = $qworkedthrough / count($this->questions);
2111 "qworkedthrough" => $qworkedthrough,
2112 "qmax" => count($this->questions),
2113 "pworkedthrough" => $percent_worked_through,
2114 "timeofwork" => $max_time,
2115 "atimeofwork" => $atimeofwork,
2116 "firstvisit" => $first_date,
2117 "lastvisit" => $last_date,
2118 "resultspoints" => $test_result[
"test"][
"total_reached_points"],
2119 "maxpoints" => $test_result[
"test"][
"total_max_points"],
2120 "resultsmarks" => $result_mark,
2121 "passed" => $passed,
2122 "distancemedian" =>
"0"
2124 foreach ($test_result as $key => $value) {
2125 if (preg_match(
"/\d+/", $key)) {
2126 $result_array[$key] = $value;
2129 return $result_array;
2141 $totalpoints_array = [];
2142 $all_users = $this->evalTotalParticipantsArray();
2143 foreach ($all_users as $active_id => $user_name) {
2144 $test_result = &$this->getTestResult($active_id);
2145 $reached = $test_result[
"test"][
"total_reached_points"];
2146 $total = $test_result[
"test"][
"total_max_points"];
2147 $percentage = $total != 0 ? $reached / $total : 0;
2148 $mark = $this->getMarkSchema()->getMatchingMark($percentage * 100.0);
2150 if ($mark !==
null && $mark->getPassed()) {
2151 array_push($totalpoints_array, $test_result[
"test"][
"total_reached_points"]);
2154 return $totalpoints_array;
2164 $result = $this->db->queryF(
2165 "SELECT tst_active.active_id, usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login FROM tst_active LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id WHERE tst_active.test_fi = %s ORDER BY usr_data.lastname ASC",
2167 [$this->getTestId()]
2169 $persons_array = [];
2170 while ($row = $this->db->fetchAssoc($result)) {
2171 $name = $this->
lng->txt(
"anonymous");
2172 $fullname = $this->
lng->txt(
"anonymous");
2174 if (!$this->getAnonymity()) {
2175 if (strlen($row[
"firstname"] . $row[
"lastname"] . $row[
"title"]) == 0) {
2176 $name = $this->
lng->txt(
"deleted_user");
2177 $fullname = $this->
lng->txt(
"deleted_user");
2178 $login = $this->
lng->txt(
"unknown");
2180 $login = $row[
"login"];
2182 $name = $this->
lng->txt(
"anonymous");
2183 $fullname = $this->
lng->txt(
"anonymous");
2185 $name = trim($row[
"lastname"] .
", " . $row[
"firstname"] .
" " . $row[
"title"]);
2186 $fullname = trim($row[
"title"] .
" " . $row[
"firstname"] .
" " . $row[
"lastname"]);
2190 $persons_array[$row[
"active_id"]] = [
2192 "fullname" => $fullname,
2196 return $persons_array;
2201 $result = $this->db->queryF(
2202 "SELECT tst_active.user_fi, tst_active.active_id, usr_data.firstname, usr_data.lastname, usr_data.title FROM tst_active LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id WHERE tst_active.test_fi = %s ORDER BY usr_data.lastname " . strtoupper($name_sort_order),
2204 [$this->getTestId()]
2206 $persons_array = [];
2207 while ($row = $this->db->fetchAssoc($result)) {
2208 if ($this->getAccessFilteredParticipantList() && !$this->getAccessFilteredParticipantList()->isActiveIdInList($row[
"active_id"])) {
2212 if ($this->getAnonymity()) {
2213 $persons_array[$row[
"active_id"]] = $this->
lng->txt(
"anonymous");
2215 if (strlen($row[
"firstname"] . $row[
"lastname"] . $row[
"title"]) == 0) {
2216 $persons_array[$row[
"active_id"]] = $this->
lng->txt(
"deleted_user");
2219 $persons_array[$row[
"active_id"]] = $row[
"lastname"];
2221 $persons_array[$row[
"active_id"]] = trim($row[
"lastname"] .
", " . $row[
"firstname"] .
" " . $row[
"title"]);
2226 return $persons_array;
2231 $result = $this->db->queryF(
2232 'SELECT tst_active.user_fi, tst_active.active_id, usr_data.login, '
2233 .
'usr_data.firstname, usr_data.lastname, usr_data.title FROM tst_active '
2234 .
'LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id '
2235 .
'WHERE tst_active.test_fi = %s '
2236 .
'ORDER BY usr_data.lastname ' . strtoupper($name_sort_order),
2238 [$this->getTestId()]
2240 $persons_array = [];
2241 while ($row = $this->db->fetchAssoc($result)) {
2242 if ($this->getAnonymity()) {
2243 $persons_array[$row[
'active_id']] = [
'name' => $this->
lng->txt(
"anonymous")];
2245 if (strlen($row[
'firstname'] . $row[
'lastname'] . $row[
"title"]) == 0) {
2246 $persons_array[$row[
'active_id']] = [
'name' => $this->
lng->txt(
'deleted_user')];
2249 $persons_array[$row[
'active_id']] = [
'name' => $row[
'lastname']];
2251 $persons_array[$row[
'active_id']] = [
2252 'name' => trim($row[
'lastname'] .
', ' . $row[
'firstname']
2253 .
' ' . $row[
'title']),
2254 'login' => $row[
'login']
2260 return $persons_array;
2265 if ($this->isRandomTest()) {
2266 $this->db->setLimit($this->getQuestionCount(), 0);
2267 $result = $this->db->queryF(
2268 'SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, '
2269 .
'tst_test_rnd_qst.pass, qpl_questions.points '
2270 .
'FROM tst_test_rnd_qst, qpl_questions '
2271 .
'WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id '
2272 .
'AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence',
2277 $result = $this->db->queryF(
2278 'SELECT tst_test_question.sequence, tst_test_question.question_fi, '
2279 .
'qpl_questions.points '
2280 .
'FROM tst_test_question, tst_active, qpl_questions '
2281 .
'WHERE tst_test_question.question_fi = qpl_questions.question_id '
2282 .
'AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi',
2288 if ($result->numRows()) {
2289 while ($row = $this->db->fetchAssoc($result)) {
2290 array_push($qtest, $row);
2298 if ($this->isRandomTest()) {
2299 $this->db->setLimit($this->getQuestionCount(), 0);
2300 $result = $this->db->queryF(
2301 'SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, '
2302 .
'qpl_questions.points '
2303 .
'FROM tst_test_rnd_qst, qpl_questions '
2304 .
'WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id '
2305 .
'AND tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.pass = %s '
2306 .
'ORDER BY tst_test_rnd_qst.sequence',
2307 [
'integer',
'integer'],
2311 $result = $this->db->queryF(
2312 'SELECT tst_test_question.sequence, tst_test_question.question_fi, '
2313 .
'qpl_questions.points '
2314 .
'FROM tst_test_question, tst_active, qpl_questions '
2315 .
'WHERE tst_test_question.question_fi = qpl_questions.question_id '
2316 .
'AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi',
2322 if ($result->numRows()) {
2323 while ($row = $this->db->fetchAssoc($result)) {
2324 array_push($qpass, $row);
2332 return $this->access_filtered_participant_list;
2337 $this->access_filtered_participant_list = $access_filtered_participant_list;
2343 $list->initializeFromDbRows($this->getTestParticipants());
2345 return $list->getAccessFilteredList(
2346 $this->participant_access_filter->getAccessStatisticsUserFilter($this->getRefId())
2356 $list->initializeFromDbRows($this->getTestParticipants());
2357 if ($this->getAnonymity()) {
2358 return $list->getAllUserIds();
2360 return $list->getAccessFilteredList(
2361 $this->participant_access_filter->getAnonOnlyParticipantsUserFilter($this->getRefId())
2368 ->getEvaluationData();
2373 $question_set_type = $this->lookupQuestionSetTypeByActiveId($active_id);
2375 switch ($question_set_type) {
2377 $res = $this->db->queryF(
2379 SELECT tst_test_rnd_qst.pass,
2380 COUNT(tst_test_rnd_qst.question_fi) qcount,
2381 SUM(qpl_questions.points) qsum
2383 FROM tst_test_rnd_qst,
2386 WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id
2387 AND tst_test_rnd_qst.active_fi = %s
2390 GROUP BY tst_test_rnd_qst.active_fi,
2391 tst_test_rnd_qst.pass
2393 [
'integer',
'integer'],
2399 $res = $this->db->queryF(
2401 SELECT COUNT(tst_test_question.question_fi) qcount,
2402 SUM(qpl_questions.points) qsum
2404 FROM tst_test_question,
2408 WHERE tst_test_question.question_fi = qpl_questions.question_id
2409 AND tst_test_question.test_fi = tst_active.test_fi
2410 AND tst_active.active_id = %s
2412 GROUP BY tst_test_question.test_fi
2420 throw new ilTestException(
"not supported question set type: $question_set_type");
2423 $row = $this->db->fetchAssoc(
$res);
2425 if (is_array($row)) {
2426 return [
"count" => $row[
"qcount"],
"points" => $row[
"qsum"]];
2429 return [
"count" => 0,
"points" => 0];
2434 $data = $this->getUnfilteredEvaluationData();
2435 $data->setFilter($filterby, $filtertext);
2456 || $firstname . $lastname ===
'') {
2457 return $this->
lng->txt(
'deleted_user');
2460 if ($this->getAnonymity()) {
2461 return $this->
lng->txt(
'anonymous');
2468 return trim($lastname .
', ' . $firstname);
2473 $query =
"SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi";
2475 if ($active_ids_to_filter !==
null && $active_ids_to_filter !== []) {
2476 $query .=
" AND " . $this->db->in(
'active_id', $active_ids_to_filter,
false,
'integer');
2479 $result = $this->db->queryF($query, [
'integer'], [$this->getTestId()]);
2481 while ($row = $this->db->fetchObject($result)) {
2482 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
2491 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
2500 if (isset($times[$row->active_fi])) {
2501 $times[$row->active_fi] += ($epoch_2 - $epoch_1);
2503 $times[$row->active_fi] = ($epoch_2 - $epoch_1);
2508 foreach ($times as $value) {
2509 $max_time += $value;
2515 return (
int) round($max_time /
$counter);
2525 bool $use_object_id =
false,
2526 ?
bool $equal_points =
false,
2527 bool $could_be_offline =
false,
2528 bool $show_path =
false,
2529 bool $with_questioncount =
false,
2530 string $permission =
'read'
2534 $equal_points ?? false,
2537 $with_questioncount,
2579 if ((!$question_type) and ($question_id > 0)) {
2580 $question_type = $this->getQuestionType($question_id);
2583 if (!strlen($question_type)) {
2587 if ($question_id > 0) {
2588 $question_gui = assQuestion::instantiateQuestionGUI($question_id);
2590 $question_type_gui = $question_type .
'GUI';
2591 $question_gui =
new $question_type_gui();
2594 return $question_gui;
2605 if (strcmp((
string) $question_id,
"") !== 0) {
2616 public function moveQuestions(array $move_questions,
int $target_index,
int $insert_mode): void
2618 $this->questions = array_values($this->questions);
2619 $array_pos = array_search($target_index, $this->questions);
2620 if ($insert_mode == 0) {
2621 $part1 = array_slice($this->questions, 0, $array_pos);
2622 $part2 = array_slice($this->questions, $array_pos);
2623 } elseif ($insert_mode == 1) {
2624 $part1 = array_slice($this->questions, 0, $array_pos + 1);
2625 $part2 = array_slice($this->questions, $array_pos + 1);
2627 foreach ($move_questions as $question_id) {
2628 if (!(array_search($question_id, $part1) ===
false)) {
2629 unset($part1[array_search($question_id, $part1)]);
2631 if (!(array_search($question_id, $part2) ===
false)) {
2632 unset($part2[array_search($question_id, $part2)]);
2635 $part1 = array_values($part1);
2636 $part2 = array_values($part2);
2637 $new_array = array_values(array_merge($part1, $move_questions, $part2));
2638 $this->questions = [];
2640 foreach ($new_array as $question_id) {
2641 $this->questions[
$counter] = $question_id;
2644 $this->saveQuestionsToDb();
2646 if ($this->
logger->isLoggingEnabled()) {
2647 $this->
logger->logTestAdministrationInteraction(
2648 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
2650 $this->user->getId(),
2651 TestAdministrationInteractionTypes::QUESTION_MOVED,
2653 AdditionalInformationGenerator::KEY_QUESTION_ORDER => $this->questions
2670 if ($this->isStartingTimeEnabled() && $this->getStartingTime() != 0) {
2672 if ($now < $this->getStartingTime()) {
2688 if ($this->isEndingTimeEnabled() && $this->getEndingTime() != 0) {
2690 if ($now > $this->getEndingTime()) {
2706 if (count($available_pools)) {
2707 $available =
" AND " . $this->db->in(
'qpl_questions.obj_fi', $available_pools,
false,
'integer');
2711 if ($completeonly) {
2712 $available .=
" AND qpl_questions.complete = " . $this->db->quote(
"1",
'text');
2716 if (is_array($arr_filter)) {
2717 if (array_key_exists(
'title', $arr_filter) && strlen($arr_filter[
'title'])) {
2718 $where .=
" AND " . $this->db->like(
'qpl_questions.title',
'text',
"%%" . $arr_filter[
'title'] .
"%%");
2720 if (array_key_exists(
'description', $arr_filter) && strlen($arr_filter[
'description'])) {
2721 $where .=
" AND " . $this->db->like(
'qpl_questions.description',
'text',
"%%" . $arr_filter[
'description'] .
"%%");
2723 if (array_key_exists(
'author', $arr_filter) && strlen($arr_filter[
'author'])) {
2724 $where .=
" AND " . $this->db->like(
'qpl_questions.author',
'text',
"%%" . $arr_filter[
'author'] .
"%%");
2726 if (array_key_exists(
'type', $arr_filter) && strlen($arr_filter[
'type'])) {
2727 $where .=
" AND qpl_qst_type.type_tag = " . $this->db->quote($arr_filter[
'type'],
'text');
2729 if (array_key_exists(
'qpl', $arr_filter) && strlen($arr_filter[
'qpl'])) {
2730 $where .=
" AND " . $this->db->like(
'object_data.title',
'text',
"%%" . $arr_filter[
'qpl'] .
"%%");
2734 $original_ids = &$this->getExistingQuestions();
2735 $original_clause =
" qpl_questions.original_id IS NULL";
2736 if (count($original_ids)) {
2737 $original_clause =
" qpl_questions.original_id IS NULL AND " . $this->db->in(
'qpl_questions.question_id', $original_ids,
true,
'integer');
2740 $query_result = $this->db->query(
"
2741 SELECT qpl_questions.*, qpl_questions.tstamp,
2742 qpl_qst_type.type_tag, qpl_qst_type.plugin, qpl_qst_type.plugin_name,
2743 object_data.title parent_title
2744 FROM qpl_questions, qpl_qst_type, object_data
2745 WHERE $original_clause $available
2746 AND object_data.obj_id = qpl_questions.obj_fi
2747 AND qpl_questions.tstamp > 0
2748 AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id
2753 if ($query_result->numRows()) {
2754 while ($row = $this->db->fetchAssoc($query_result)) {
2757 if (!$row[
'plugin']) {
2758 $row[
'ttype' ] = $this->
lng->txt($row[
"type_tag" ]);
2764 $plugin = $this->component_repository->getPluginByName($row[
'plugin_name']);
2769 $pl = $this->component_factory->getPlugin(
$plugin->getId());
2770 $row[
'ttype' ] = $pl->getQuestionTypeTranslation();
2784 $this->saveToDb(
true);
2786 $main_settings = $this->getMainSettings();
2796 $introduction_settings = $introduction_settings->withIntroductionEnabled(
false);
2797 foreach ($assessment->objectives as
$objectives) {
2799 $introduction_settings = $this->addIntroductionToSettingsFromImport(
2800 $introduction_settings,
2801 $this->qtiMaterialToArray($material),
2810 $finishing_settings = $this->addConcludingRemarksToSettingsFromImport(
2811 $finishing_settings,
2812 $this->qtiMaterialToArray(
2819 $score_settings = $this->getScoreSettings();
2826 foreach ($assessment->qtimetadata as $metadata) {
2827 switch ($metadata[
"label"]) {
2828 case "solution_details":
2829 $result_details_settings = $result_details_settings->withShowPassDetails((
bool) $metadata[
"entry"]);
2831 case "show_solution_list_comparison":
2832 $result_details_settings = $result_details_settings->withShowSolutionListComparison((
bool) $metadata[
"entry"]);
2834 case "print_bs_with_res":
2835 $result_details_settings = $result_details_settings->withShowSolutionListComparison((
bool) $metadata[
"entry"]);
2838 $this->saveAuthorToMetadata($metadata[
"entry"]);
2841 $test_behaviour_settings = $test_behaviour_settings->withNumberOfTries((
int) $metadata[
"entry"]);
2843 case 'block_after_passed':
2844 $test_behaviour_settings = $test_behaviour_settings->withBlockAfterPassedEnabled((
bool) $metadata[
'entry']);
2846 case "pass_waiting":
2847 $test_behaviour_settings = $test_behaviour_settings->withPassWaiting($metadata[
"entry"]);
2850 $test_behaviour_settings = $test_behaviour_settings->withKioskMode((
int) $metadata[
"entry"]);
2852 case 'show_introduction':
2853 $introduction_settings = $introduction_settings->withIntroductionEnabled((
bool) $metadata[
'entry']);
2855 case "showfinalstatement":
2856 case 'show_concluding_remarks':
2857 $finishing_settings = $finishing_settings->withConcludingRemarksEnabled((
bool) $metadata[
"entry"]);
2859 case 'exam_conditions':
2860 $introduction_settings = $introduction_settings->withExamConditionsCheckboxEnabled($metadata[
'entry'] ===
'1');
2862 case "highscore_enabled":
2863 $gamification_settings = $gamification_settings->withHighscoreEnabled((
bool) $metadata[
"entry"]);
2865 case "highscore_anon":
2866 $gamification_settings = $gamification_settings->withHighscoreAnon((
bool) $metadata[
"entry"]);
2868 case "highscore_achieved_ts":
2869 $gamification_settings = $gamification_settings->withHighscoreAchievedTS((
bool) $metadata[
"entry"]);
2871 case "highscore_score":
2872 $gamification_settings = $gamification_settings->withHighscoreScore((
bool) $metadata[
"entry"]);
2874 case "highscore_percentage":
2875 $gamification_settings = $gamification_settings->withHighscorePercentage((
bool) $metadata[
"entry"]);
2877 case "highscore_wtime":
2878 $gamification_settings = $gamification_settings->withHighscoreWTime((
bool) $metadata[
"entry"]);
2880 case "highscore_own_table":
2881 $gamification_settings = $gamification_settings->withHighscoreOwnTable((
bool) $metadata[
"entry"]);
2883 case "highscore_top_table":
2884 $gamification_settings = $gamification_settings->withHighscoreTopTable((
bool) $metadata[
"entry"]);
2886 case "highscore_top_num":
2887 $gamification_settings = $gamification_settings->withHighscoreTopNum((
int) $metadata[
"entry"]);
2889 case "use_previous_answers":
2890 $participant_functionality_settings = $participant_functionality_settings->withUsePreviousAnswerAllowed((
bool) $metadata[
"entry"]);
2892 case 'question_list_enabled':
2893 $participant_functionality_settings = $participant_functionality_settings->withQuestionListEnabled((
bool) $metadata[
'entry']);
2895 case "title_output":
2896 $question_behaviour_settings = $question_behaviour_settings->withQuestionTitleOutputMode((
int) $metadata[
"entry"]);
2898 case "question_set_type":
2899 if ($metadata[
'entry'] === self::QUESTION_SET_TYPE_RANDOM) {
2900 $this->questions = [];
2902 $general_settings = $general_settings->withQuestionSetType($metadata[
"entry"]);
2905 $general_settings = $general_settings->withAnonymity((
bool) $metadata[
"entry"]);
2907 case "results_presentation":
2908 $result_details_settings = $result_details_settings->withResultsPresentation((
int) $metadata[
"entry"]);
2910 case "reset_processing_time":
2911 $test_behaviour_settings = $test_behaviour_settings->withResetProcessingTime($metadata[
"entry"] ===
'1');
2913 case "answer_feedback_points":
2914 $question_behaviour_settings = $question_behaviour_settings->withInstantFeedbackPointsEnabled((
bool) $metadata[
"entry"]);
2916 case "answer_feedback":
2917 $question_behaviour_settings = $question_behaviour_settings->withInstantFeedbackGenericEnabled((
bool) $metadata[
"entry"]);
2919 case 'instant_feedback_specific':
2920 $question_behaviour_settings = $question_behaviour_settings->withInstantFeedbackSpecificEnabled((
bool) $metadata[
'entry']);
2922 case "instant_verification":
2923 $question_behaviour_settings = $question_behaviour_settings->withInstantFeedbackSolutionEnabled((
bool) $metadata[
"entry"]);
2925 case "force_instant_feedback":
2926 $question_behaviour_settings = $question_behaviour_settings->withForceInstantFeedbackOnNextQuestion((
bool) $metadata[
"entry"]);
2928 case "follow_qst_answer_fixation":
2929 $question_behaviour_settings = $question_behaviour_settings->withLockAnswerOnNextQuestionEnabled((
bool) $metadata[
"entry"]);
2931 case "instant_feedback_answer_fixation":
2932 $question_behaviour_settings = $question_behaviour_settings->withLockAnswerOnInstantFeedbackEnabled((
bool) $metadata[
"entry"]);
2935 case "suspend_test_allowed":
2936 $participant_functionality_settings = $participant_functionality_settings->withSuspendTestAllowed((
bool) $metadata[
"entry"]);
2938 case "sequence_settings":
2939 $participant_functionality_settings = $participant_functionality_settings->withPostponedQuestionsMoveToEnd((
bool) $metadata[
"entry"]);
2942 $participant_functionality_settings = $participant_functionality_settings->withQuestionMarkingEnabled((
bool) $metadata[
"entry"]);
2944 case "fixed_participants":
2945 $access_settings = $access_settings->withFixedParticipants((
bool) $metadata[
"entry"]);
2947 case "score_reporting":
2948 if ($metadata[
'entry'] !==
null) {
2949 $result_summary_settings = $result_summary_settings->withScoreReporting(
2950 ScoreReportingTypes::tryFrom((
int) $metadata[
'entry']) ?? ScoreReportingTypes::SCORE_REPORTING_DISABLED
2954 case "shuffle_questions":
2955 $question_behaviour_settings = $question_behaviour_settings->withShuffleQuestions((
bool) $metadata[
"entry"]);
2957 case "count_system":
2958 $scoring_settings = $scoring_settings->withCountSystem((
int) $metadata[
"entry"]);
2960 case "exportsettings":
2961 $result_details_settings = $result_details_settings->withExportSettings((
int) $metadata[
"entry"]);
2963 case "score_cutting":
2964 $scoring_settings = $scoring_settings->withScoreCutting((
int) $metadata[
"entry"]);
2967 $access_settings = $access_settings->withPasswordEnabled(
2968 $metadata[
"entry"] !==
null && $metadata[
"entry"] !==
''
2969 )->withPassword($metadata[
"entry"]);
2971 case 'ip_range_from':
2972 if ($metadata[
'entry'] !==
'') {
2973 $access_settings = $access_settings->withIpRangeFrom($metadata[
'entry']);
2977 if ($metadata[
'entry'] !==
'') {
2978 $access_settings = $access_settings->withIpRangeTo($metadata[
'entry']);
2981 case "pass_scoring":
2982 $scoring_settings = $scoring_settings->withPassScoring((
int) $metadata[
"entry"]);
2984 case 'pass_deletion_allowed':
2985 $result_summary_settings = $result_summary_settings->withPassDeletionAllowed((
bool) $metadata[
"entry"]);
2987 case "usr_pass_overview_mode":
2988 $participant_functionality_settings = $participant_functionality_settings->withUsrPassOverviewMode((
int) $metadata[
"entry"]);
2990 case "question_list":
2991 $participant_functionality_settings = $participant_functionality_settings->withQuestionListEnabled((
bool) $metadata[
"entry"]);
2994 case "reporting_date":
2995 $reporting_date = $this->buildDateTimeImmutableFromPeriod($metadata[
'entry']);
2996 if ($reporting_date !==
null) {
2997 $result_summary_settings = $result_summary_settings->withReportingDate($reporting_date);
3000 case 'enable_processing_time':
3001 $test_behaviour_settings = $test_behaviour_settings->withProcessingTimeEnabled((
bool) $metadata[
'entry']);
3003 case "processing_time":
3004 $test_behaviour_settings = $test_behaviour_settings->withProcessingTime($metadata[
'entry']);
3006 case "starting_time":
3007 $starting_time = $this->buildDateTimeImmutableFromPeriod($metadata[
'entry']);
3008 if ($starting_time !==
null) {
3009 $access_settings = $access_settings->withStartTime($starting_time)
3010 ->withStartTimeEnabled(
true);
3014 $ending_time = $this->buildDateTimeImmutableFromPeriod($metadata[
'entry']);
3015 if ($ending_time !==
null) {
3016 $access_settings = $access_settings->withEndTime($ending_time)
3017 ->withStartTimeEnabled(
true);
3020 case "enable_examview":
3021 $finishing_settings = $finishing_settings->withShowAnswerOverview((
bool) $metadata[
"entry"]);
3023 case 'redirection_mode':
3024 $finishing_settings = $finishing_settings->withRedirectionMode(
3025 RedirectionModes::tryFrom((
int) ($metadata[
'entry'] ?? 0)) ?? RedirectionModes::NONE
3028 case 'redirection_url':
3029 $finishing_settings = $finishing_settings->withRedirectionUrl($metadata[
'entry']);
3031 case 'examid_in_test_pass':
3032 $test_behaviour_settings = $test_behaviour_settings->withExamIdInTestAttemptEnabled((
bool) $metadata[
'entry']);
3034 case 'examid_in_test_res':
3035 $result_details_settings = $result_details_settings->withShowExamIdInTestResults((
bool) $metadata[
"entry"]);
3037 case 'skill_service':
3038 $additional_settings = $additional_settings->withSkillsServiceEnabled((
bool) $metadata[
'entry']);
3040 case 'show_grading_status':
3041 $result_summary_settings = $result_summary_settings->withShowGradingStatusEnabled((
bool) $metadata[
"entry"]);
3043 case 'show_grading_mark':
3044 $result_summary_settings = $result_summary_settings->withShowGradingMarkEnabled((
bool) $metadata[
"entry"]);
3047 $question_behaviour_settings = $question_behaviour_settings->withAutosaveEnabled((
bool) $metadata[
'entry']);
3049 case 'autosave_ival':
3050 $question_behaviour_settings = $question_behaviour_settings->withAutosaveInterval((
int) $metadata[
'entry']);
3052 case 'show_summary':
3053 $participant_functionality_settings = $participant_functionality_settings->withQuestionListEnabled(($metadata[
'entry'] & 1) > 0)
3054 ->withUsrPassOverviewMode((
int) $metadata[
'entry']);
3057 case 'hide_info_tab':
3058 $additional_settings = $additional_settings->withHideInfoTab($metadata[
'entry'] ===
'1');
3060 if (preg_match(
"/mark_step_\d+/", $metadata[
"label"])) {
3061 $xmlmark = $metadata[
"entry"];
3062 preg_match(
"/<short>(.*?)<\/short>/", $xmlmark, $matches);
3063 $mark_short = $matches[1];
3064 preg_match(
"/<official>(.*?)<\/official>/", $xmlmark, $matches);
3065 $mark_official = $matches[1];
3066 preg_match(
"/<percentage>(.*?)<\/percentage>/", $xmlmark, $matches);
3067 $mark_percentage = (float) $matches[1];
3068 preg_match(
"/<passed>(.*?)<\/passed>/", $xmlmark, $matches);
3069 $mark_passed = (bool) $matches[1];
3070 $mark_steps[] =
new Mark($mark_short, $mark_official, $mark_percentage, $mark_passed);
3073 $this->mark_schema = $this->getMarkSchema()->withMarkSteps($mark_steps);
3075 $this->getObjectProperties()->storePropertyTitleAndDescription(
3076 $this->getObjectProperties()->getPropertyTitleAndDescription()
3080 $this->addToNewsOnOnline(
false, $this->getObjectProperties()->getPropertyIsOnline()->getIsOnline());
3081 $main_settings = $main_settings
3083 ->withIntroductionSettings($introduction_settings)
3084 ->withAccessSettings($access_settings)
3085 ->withParticipantFunctionalitySettings($participant_functionality_settings)
3086 ->withTestBehaviourSettings($test_behaviour_settings)
3087 ->withQuestionBehaviourSettings($question_behaviour_settings)
3088 ->withFinishingSettings($finishing_settings)
3089 ->withAdditionalSettings($additional_settings);
3090 $this->getMainSettingsRepository()->store($main_settings);
3091 $this->main_settings = $main_settings;
3093 $score_settings = $score_settings
3095 ->withScoringSettings($scoring_settings)
3096 ->withResultDetailsSettings($result_details_settings)
3097 ->withResultSummarySettings($result_summary_settings);
3098 $this->getScoreSettingsRepository()->store($score_settings);
3099 $this->score_settings = $score_settings;
3100 $this->loadFromDb();
3108 if (!str_starts_with($material[
'text'],
'<PageObject>')) {
3112 $text = $this->replaceFilesInPageImports(
3113 $this->replaceMobsInPageImports(
3115 $mappings[
'components/ILIAS/MediaObjects'][
'mob'] ?? []
3117 $mappings[
'components/ILIAS/File'][
'file'] ?? []
3121 $page_object->setParentId($this->
getId());
3122 $page_object->setXMLContent(
$text);
3123 $new_page_id = $page_object->createPageWithNextId();
3124 return $settings->withIntroductionPageId($new_page_id);
3132 if (!str_starts_with($material[
'text'],
'<PageObject>')) {
3136 $text = $this->replaceFilesInPageImports(
3137 $this->replaceMobsInPageImports(
3139 $mappings[
'components/ILIAS/MediaObjects'][
'mob'] ?? []
3141 $mappings[
'components/ILIAS/File'][
'file'] ?? []
3145 $page_object->setParentId($this->
getId());
3146 $page_object->setXMLContent(
$text);
3147 $new_page_id = $page_object->createPageWithNextId();
3148 return $settings->withConcludingRemarksPageId($new_page_id);
3153 preg_match_all(
'/il_(\d+)_mob_(\d+)/',
$text, $matches);
3154 foreach ($matches[0] as $index => $match) {
3155 if (empty($mappings[$matches[2][$index]])) {
3158 $text = str_replace($match,
"il__mob_{$mappings[$matches[2][$index]]}",
$text);
3166 preg_match_all(
'/il_(\d+)_file_(\d+)/',
$text, $matches);
3167 foreach ($matches[0] as $index => $match) {
3168 if (empty($mappings[$matches[2][$index]])) {
3171 $text = str_replace($match,
"il__file_{$mappings[$matches[2][$index]]}",
$text);
3183 $main_settings = $this->getMainSettings();
3186 $a_xml_writer->xmlHeader();
3187 $a_xml_writer->xmlSetDtdDef(
"<!DOCTYPE questestinterop SYSTEM \"ims_qtiasiv1p2p1.dtd\">");
3188 $a_xml_writer->xmlStartTag(
"questestinterop");
3191 "ident" =>
"il_" .
IL_INST_ID .
"_tst_" . $this->getTestId(),
3194 $a_xml_writer->xmlStartTag(
"assessment", $attrs);
3195 $a_xml_writer->xmlElement(
"qticomment",
null, $this->getDescription());
3198 $a_xml_writer->xmlElement(
3201 $this->getProcessingTimeForXML()
3205 $a_xml_writer->xmlStartTag(
"qtimetadata");
3206 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3207 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"ILIAS_VERSION");
3208 $a_xml_writer->xmlElement(
"fieldentry",
null,
ILIAS_VERSION);
3209 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3211 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3212 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"anonymity");
3213 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getGeneralSettings()->getAnonymity() ? 1 : 0);
3214 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3216 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3217 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"question_set_type");
3218 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getGeneralSettings()->getQuestionSetType());
3219 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3221 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3222 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"sequence_settings");
3224 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3226 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3227 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"author");
3228 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getAuthor());
3229 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3231 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3232 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"reset_processing_time");
3233 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getTestBehaviourSettings()->getResetProcessingTime() ? 1 : 0);
3234 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3236 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3237 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"count_system");
3238 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getCountSystem());
3239 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3241 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3242 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"score_cutting");
3243 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getScoreCutting());
3244 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3246 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3247 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"password");
3248 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getAccessSettings()->getPassword() ??
'');
3249 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3251 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3252 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"ip_range_from");
3253 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getAccessSettings()->getIpRangeFrom());
3254 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3256 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3257 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"ip_range_to");
3258 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getAccessSettings()->getIpRangeTo());
3259 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3261 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3262 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"pass_scoring");
3263 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getPassScoring());
3264 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3266 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3267 $a_xml_writer->xmlElement(
'fieldlabel',
null,
'pass_deletion_allowed');
3268 $a_xml_writer->xmlElement(
'fieldentry',
null, $this->isPassDeletionAllowed() ? 1 : 0);
3269 $a_xml_writer->xmlEndTag(
'qtimetadatafield');
3271 if ($this->getScoreSettings()->getResultSummarySettings()->getReportingDate() !==
null) {
3272 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3273 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"reporting_date");
3274 $a_xml_writer->xmlElement(
3277 $this->buildIso8601PeriodForExportCompatibility(
3278 $this->getScoreSettings()->getResultSummarySettings()->getReportingDate(),
3281 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3284 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3285 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"nr_of_tries");
3287 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3289 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3290 $a_xml_writer->xmlElement(
'fieldlabel',
null,
'block_after_passed');
3291 $a_xml_writer->xmlElement(
'fieldentry',
null, $main_settings->
getTestBehaviourSettings()->getBlockAfterPassedEnabled() ? 1 : 0);
3292 $a_xml_writer->xmlEndTag(
'qtimetadatafield');
3294 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3295 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"pass_waiting");
3297 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3299 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3300 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"kiosk");
3302 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3304 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3305 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"redirection_mode");
3306 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getFinishingSettings()->getRedirectionMode()->value);
3307 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3309 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3310 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"redirection_url");
3311 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getFinishingSettings()->getRedirectionUrl());
3312 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3314 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3315 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"use_previous_answers");
3317 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3319 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3320 $a_xml_writer->xmlElement(
'fieldlabel',
null,
'question_list_enabled');
3322 $a_xml_writer->xmlEndTag(
'qtimetadatafield');
3324 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3325 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"title_output");
3327 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3329 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3330 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"results_presentation");
3331 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getScoreSettings()->getResultDetailsSettings()->getResultsPresentation());
3332 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3334 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3335 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"examid_in_test_pass");
3336 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getTestBehaviourSettings()->getExamIdInTestAttemptEnabled() ? 1 : 0);
3337 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3339 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3340 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"examid_in_test_res");
3341 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getScoreSettings()->getResultDetailsSettings()->getShowExamIdInTestResults() ? 1 : 0);
3342 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3344 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3345 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"usr_pass_overview_mode");
3347 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3349 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3350 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"score_reporting");
3351 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getScoreSettings()->getResultSummarySettings()->getScoreReporting()->value);
3352 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3354 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3355 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_solution_list_comparison");
3356 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->score_settings->getResultDetailsSettings()->getShowSolutionListComparison() ? 1 : 0);
3357 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3359 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3360 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"instant_verification");
3361 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getInstantFeedbackSolutionEnabled() ? 1 : 0);
3362 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3364 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3365 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"answer_feedback");
3366 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getInstantFeedbackGenericEnabled() ? 1 : 0);
3367 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3369 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3370 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"instant_feedback_specific");
3371 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getInstantFeedbackSpecificEnabled() ? 1 : 0);
3372 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3374 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3375 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"answer_feedback_points");
3376 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getInstantFeedbackPointsEnabled() ? 1 : 0);
3377 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3379 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3380 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"follow_qst_answer_fixation");
3381 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getLockAnswerOnNextQuestionEnabled() ? 1 : 0);
3382 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3384 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3385 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"instant_feedback_answer_fixation");
3386 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getLockAnswerOnInstantFeedbackEnabled() ? 1 : 0);
3387 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3389 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3390 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"force_instant_feedback");
3391 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getForceInstantFeedbackOnNextQuestion() ? 1 : 0);
3392 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3394 $highscore_metadata = [
3395 'highscore_enabled' => $this->getHighscoreEnabled(),
3396 'highscore_anon' => $this->getHighscoreAnon(),
3397 'highscore_achieved_ts' => $this->getHighscoreAchievedTS(),
3398 'highscore_score' => $this->getHighscoreScore(),
3399 'highscore_percentage' => $this->getHighscorePercentage(),
3400 'highscore_wtime' => $this->getHighscoreWTime(),
3401 'highscore_own_table' => $this->getHighscoreOwnTable(),
3402 'highscore_top_table' => $this->getHighscoreTopTable(),
3404 foreach ($highscore_metadata as $label => $value) {
3405 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3406 $a_xml_writer->xmlElement(
"fieldlabel",
null, $label);
3407 $a_xml_writer->xmlElement(
"fieldentry",
null, $value ? 1 : 0);
3408 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3410 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3411 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"highscore_top_num");
3412 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getHighscoreTopNum());
3413 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3415 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3416 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"suspend_test_allowed");
3418 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3420 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3421 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_marker");
3423 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3425 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3426 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"fixed_participants");
3427 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getAccessSettings()->getFixedParticipants() ? 1 : 0);
3428 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3430 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3431 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_introduction");
3432 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getIntroductionSettings()->getIntroductionEnabled() ? 1 : 0);
3433 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3435 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3436 $a_xml_writer->xmlElement(
"fieldlabel",
null,
'exam_conditions');
3437 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getIntroductionSettings()->getExamConditionsCheckboxEnabled() ? 1 : 0);
3438 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3440 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3441 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_concluding_remarks");
3442 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getFinishingSettings()->getConcludingRemarksEnabled() ? 1 : 0);
3443 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3445 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3446 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"exportsettings");
3447 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getExportSettings());
3448 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3450 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3451 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"shuffle_questions");
3453 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3455 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3456 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"processing_time");
3458 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3460 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3461 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"enable_examview");
3462 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getFinishingSettings()->getShowAnswerOverview() ? 1 : 0);
3463 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3465 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3466 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"skill_service");
3467 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getAdditionalSettings()->getSkillsServiceEnabled() ? 1 : 0);
3468 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3470 if ($this->getInstantFeedbackSolution() == 1) {
3472 "solutionswitch" =>
"Yes"
3477 $a_xml_writer->xmlElement(
"assessmentcontrol", $attrs,
null);
3479 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3480 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_grading_status");
3481 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->isShowGradingStatusEnabled() ? 1 : 0);
3482 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3484 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3485 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_grading_mark");
3486 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->isShowGradingMarkEnabled() ? 1 : 0);
3487 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3489 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3490 $a_xml_writer->xmlElement(
'fieldlabel',
null,
'hide_info_tab');
3491 $a_xml_writer->xmlElement(
'fieldentry',
null, $this->getMainSettings()->getAdditionalSettings()->getHideInfoTab() ? 1 : 0);
3492 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3494 if ($this->getStartingTime() > 0) {
3495 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3496 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"starting_time");
3497 $a_xml_writer->xmlElement(
3500 $this->buildIso8601PeriodForExportCompatibility(
3501 (
new DateTimeImmutable())->setTimestamp($this->getStartingTime()),
3504 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3507 if ($this->getEndingTime() > 0) {
3508 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3509 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"ending_time");
3510 $a_xml_writer->xmlElement(
3513 $this->buildIso8601PeriodForExportCompatibility(
3514 (
new DateTimeImmutable())->setTimestamp($this->getEndingTime()),
3517 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3520 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3521 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"autosave");
3523 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3525 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3526 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"autosave_ival");
3528 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3530 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3531 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"instant_feedback_specific");
3532 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getInstantFeedbackSpecificEnabled() ? 1 : 0);
3533 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3535 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3536 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"instant_feedback_answer_fixation");
3537 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getLockAnswerOnInstantFeedbackEnabled() ? 1 : 0);
3538 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3540 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3541 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"enable_processing_time");
3542 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getTestBehaviourSettings()->getProcessingTimeEnabled() ? 1 : 0);
3543 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3545 foreach ($this->getMarkSchema()->getMarkSteps() as $index => $mark) {
3546 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3547 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"mark_step_$index");
3548 $a_xml_writer->xmlElement(
"fieldentry",
null, sprintf(
3549 "<short>%s</short><official>%s</official><percentage>%.2f</percentage><passed>%d</passed>",
3550 $mark->getShortName(),
3551 $mark->getOfficialName(),
3552 $mark->getMinimumLevel(),
3555 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3557 $a_xml_writer->xmlEndTag(
"qtimetadata");
3560 $introduction = $page_id !==
null
3561 ? (
new ilTestPage($page_id))->getXMLContent()
3564 $a_xml_writer->xmlStartTag(
"objectives");
3565 $this->addQTIMaterial($a_xml_writer, $page_id, $introduction);
3566 $a_xml_writer->xmlEndTag(
"objectives");
3568 if ($this->getInstantFeedbackSolution() == 1) {
3570 "solutionswitch" =>
"Yes"
3575 $a_xml_writer->xmlElement(
"assessmentcontrol", $attrs,
null);
3577 if (strlen($this->getFinalStatement())) {
3579 $concluding_remarks = $page_id !==
null
3580 ? (
new ilTestPage($page_id))->getXMLContent()
3583 $a_xml_writer->xmlStartTag(
"presentation_material");
3584 $a_xml_writer->xmlStartTag(
"flow_mat");
3585 $this->addQTIMaterial($a_xml_writer, $page_id, $concluding_remarks);
3586 $a_xml_writer->xmlEndTag(
"flow_mat");
3587 $a_xml_writer->xmlEndTag(
"presentation_material");
3593 $a_xml_writer->xmlElement(
"section", $attrs,
null);
3594 $a_xml_writer->xmlEndTag(
"assessment");
3595 $a_xml_writer->xmlEndTag(
"questestinterop");
3597 $xml = $a_xml_writer->xmlDumpMem(
false);
3603 return $date_time->setTimezone(
new DateTimeZone(
'UTC'))->format(
'\PY\Yn\Mj\D\TG\Hi\Ms\S');
3608 if ($period ===
null) {
3611 if (preg_match(
"/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $period, $matches)) {
3612 return new DateTimeImmutable(
3614 "%02d-%02d-%02d %02d:%02d:%02d",
3622 new \DateTimeZone(
'UTC')
3634 public function exportPagesXML(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog): void
3636 $this->mob_ids = [];
3639 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Start Export Page Objects");
3640 $this->bench->start(
"ContentObjectExport",
"exportPageObjects");
3641 $this->exportXMLPageObjects($a_xml_writer, $a_inst, $expLog);
3642 $this->bench->stop(
"ContentObjectExport",
"exportPageObjects");
3643 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Finished Export Page Objects");
3646 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Start Export Media Objects");
3647 $this->bench->start(
"ContentObjectExport",
"exportMediaObjects");
3648 $this->exportXMLMediaObjects($a_xml_writer, $a_inst, $a_target_dir, $expLog);
3649 $this->bench->stop(
"ContentObjectExport",
"exportMediaObjects");
3650 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Finished Export Media Objects");
3653 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Start Export File Items");
3654 $this->bench->start(
"ContentObjectExport",
"exportFileItems");
3655 $this->exportFileItems($a_target_dir, $expLog);
3656 $this->bench->stop(
"ContentObjectExport",
"exportFileItems");
3657 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Finished Export File Items");
3667 if ($a_tag ==
"Identifier" && $a_param ==
"Entry") {
3683 foreach ($this->questions as $question_id) {
3684 $this->bench->start(
"ContentObjectExport",
"exportPageObject");
3685 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Page Object " . $question_id);
3688 $a_xml_writer->xmlStartTag(
"PageObject", $attrs);
3692 $this->bench->start(
"ContentObjectExport",
"exportPageObject_XML");
3694 $page_object->buildDom();
3695 $page_object->insertInstIntoIDs((
string) $inst);
3696 $mob_ids = $page_object->collectMediaObjects(
false);
3698 $xml = $page_object->getXMLFromDom(
false,
false,
false,
"",
true);
3699 $xml = str_replace(
"&",
"&", $xml);
3700 $a_xml_writer->appendXML($xml);
3701 $page_object->freeDom();
3702 unset($page_object);
3704 $this->bench->stop(
"ContentObjectExport",
"exportPageObject_XML");
3707 $this->bench->start(
"ContentObjectExport",
"exportPageObject_CollectMedia");
3709 foreach ($mob_ids as $mob_id) {
3710 $this->mob_ids[$mob_id] = $mob_id;
3712 $this->bench->stop(
"ContentObjectExport",
"exportPageObject_CollectMedia");
3715 $this->bench->start(
"ContentObjectExport",
"exportPageObject_CollectFileItems");
3717 foreach ($file_ids as $file_id) {
3718 $this->file_ids[$file_id] = $file_id;
3720 $this->bench->stop(
"ContentObjectExport",
"exportPageObject_CollectFileItems");
3722 $a_xml_writer->xmlEndTag(
"PageObject");
3725 $this->bench->stop(
"ContentObjectExport",
"exportPageObject");
3732 public function exportXMLMediaObjects(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
3734 foreach ($this->mob_ids as $mob_id) {
3735 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Media Object " . $mob_id);
3737 $target_dir = $a_target_dir . DIRECTORY_SEPARATOR .
'objects'
3738 . DIRECTORY_SEPARATOR .
'il_' .
IL_INST_ID .
'_mob_' . $mob_id;
3741 $media_obj->exportXML($a_xml_writer, (
int) $a_inst);
3743 foreach ($media_obj->getMediaItems() as $item) {
3744 $rid = $this->media_object_repository->getById($item->getMobId())[
'rid'] ??
null;
3746 $expLog->write(date(
'[y-m-d H:i:s] ') .
"The resource for Media Object {$item->getMobId()} does not exist (skipping)");
3749 $stream = $item->getLocationStream();
3750 file_put_contents($target_dir . DIRECTORY_SEPARATOR . $item->getLocation(), $stream);
3764 foreach ($this->file_ids as $file_id) {
3765 $expLog->write(date(
"[y-m-d H:i:s] ") .
"File Item " . $file_id);
3766 $file_dir = $target_dir .
'/objects/il_' .
IL_INST_ID .
'_file_' . $file_id;
3768 $file_obj =
new ilObjFile((
int) $file_id,
false);
3769 $source_file = $file_obj->getFile($file_obj->getVersion());
3770 if (!is_file($source_file)) {
3771 $source_file = $file_obj->getFile();
3773 if (is_file($source_file)) {
3774 copy($source_file, $file_dir .
'/' . $file_obj->getFileName());
3793 $this->saveCompleteStatus($this->question_set_config_factory->getQuestionSetConfig());
3795 if ($this->participantDataExist()) {
3796 $this->recalculateScores(
true);
3805 $total = $this->evalTotalPersons();
3806 $results_summary_settings = $this->getScoreSettings()->getResultSummarySettings();
3808 || $results_summary_settings->getScoreReporting()->isReportingEnabled() ===
false) {
3812 if ($results_summary_settings->getScoreReporting() === ScoreReportingTypes::SCORE_REPORTING_DATE) {
3813 return $results_summary_settings->getReportingDate()
3814 >=
new DateTimeImmutable(
'now',
new DateTimeZone(
'UTC'));
3831 $path_to_lifecycle = $this->lo_metadata->paths()->custom()->withNextStep(
'lifeCycle')->get();
3832 $path_to_authors = $this->lo_metadata->paths()->authors();
3834 $reader = $this->lo_metadata->read($this->
getId(), 0, $this->getType(), $path_to_lifecycle);
3835 if (!is_null($reader->allData($path_to_lifecycle)->current())) {
3839 if ($author ===
'') {
3840 $author = $this->
user->getFullname();
3842 $this->lo_metadata->manipulate($this->
getId(), 0, $this->getType())
3843 ->prepareCreateOrUpdate($path_to_authors, $author)
3852 $this->saveAuthorToMetadata();
3864 $path_to_authors = $this->lo_metadata->paths()->authors();
3865 $author_data = $this->lo_metadata->read($this->
getId(), 0, $this->getType(), $path_to_authors)
3866 ->allData($path_to_authors);
3868 return $this->lo_metadata->dataHelper()->makePresentableAsList(
', ', ...$author_data);
3882 $lo_metadata =
$DIC->learningObjectMetadata();
3884 $path_to_authors = $lo_metadata->paths()->authors();
3885 $author_data = $lo_metadata->read($obj_id, 0,
"tst", $path_to_authors)
3886 ->allData($path_to_authors);
3888 return $lo_metadata->dataHelper()->makePresentableAsList(
',', ...$author_data);
3900 $ilUser =
$DIC[
'ilUser'];
3903 $tests = array_slice(
3911 if (count($tests)) {
3914 if ($use_object_id) {
3916 $result_array[$obj_id] = $titles[
$ref_id];
3922 return $result_array;
3935 $this->loadFromDb();
3937 $new_obj = parent::cloneObject($target_id, $copy_id, $omit_tree);
3938 $new_obj->setTmpCopyWizardCopyId($copy_id);
3939 $this->cloneMetaData($new_obj);
3941 $new_obj->saveToDb();
3942 $new_obj->addToNewsOnOnline(
false, $new_obj->getObjectProperties()->getPropertyIsOnline()->getIsOnline());
3944 $new_main_settings = $this->getMainSettings()
3945 ->withIntroductionSettings(
3946 $this->getMainSettings()->getIntroductionSettings()->withIntroductionPageId(
3947 $this->cloneIntroduction()
3949 )->withFinishingSettings(
3950 $this->getMainSettings()->getFinishingSettings()->withConcludingRemarksPageId(
3951 $this->cloneConcludingRemarks()
3955 $new_main_settings = $this->getMainSettingsRepository()->store($new_main_settings, $new_obj->getTestId());
3956 $this->getScoreSettingsRepository()->store(
3957 $this->getScoreSettings()->withId($new_main_settings->getId())
3959 $this->marks_repository->storeMarkSchema(
3960 $this->getMarkSchema()->withTestId($new_obj->getTestId())
3963 $new_obj->setTemplate($this->getTemplate());
3972 $templateRepository,
3976 $cloneAction->cloneCertificate($this, $new_obj);
3978 $this->question_set_config_factory->getQuestionSetConfig()->cloneQuestionSetRelatedData($new_obj);
3979 $new_obj->saveQuestionsToDb();
3982 $skillLevelThresholdList->setTestId($this->getTestId());
3983 $skillLevelThresholdList->loadFromDb();
3984 $skillLevelThresholdList->cloneListForTest($new_obj->getTestId());
3987 $obj_settings->cloneSettings($new_obj->getId());
3989 if ($new_obj->getTestLogger()->isLoggingEnabled()) {
3990 $new_obj->getTestLogger()->logTestAdministrationInteraction(
3991 $new_obj->getTestLogger()->getInteractionFactory()->buildTestAdministrationInteraction(
3992 $new_obj->getRefId(),
3993 $this->user->getId(),
3994 TestAdministrationInteractionTypes::NEW_TEST_CREATED,
4007 if ($this->isRandomTest()) {
4013 $this->component_repository,
4015 $this->questionrepository
4018 $questionSetConfig->loadFromDb();
4020 if ($questionSetConfig->isQuestionAmountConfigurationModePerPool()) {
4027 $sourcePoolDefinitionList->loadDefinitions();
4029 if (is_int($sourcePoolDefinitionList->getQuestionAmount())) {
4030 $num = $sourcePoolDefinitionList->getQuestionAmount();
4032 } elseif (is_int($questionSetConfig->getQuestionAmountPerTest())) {
4033 $num = $questionSetConfig->getQuestionAmountPerTest();
4036 $this->loadQuestions();
4037 $num = count($this->questions);
4045 if ($this->isRandomTest()) {
4046 return $this->getQuestionCount();
4048 return count($this->questions);
4063 $result =
$ilDB->queryF(
4064 "SELECT obj_fi FROM tst_tests WHERE test_id = %s",
4068 if ($result->numRows()) {
4069 $row =
$ilDB->fetchAssoc($result);
4070 $object_id = $row[
"obj_fi"];
4087 $result =
$ilDB->queryF(
4088 "SELECT tst_tests.obj_fi FROM tst_tests, tst_active WHERE tst_tests.test_id = tst_active.test_fi AND tst_active.active_id = %s",
4092 if ($result->numRows()) {
4093 $row =
$ilDB->fetchAssoc($result);
4094 $object_id = $row[
"obj_fi"];
4111 $result =
$ilDB->queryF(
4112 "SELECT test_id FROM tst_tests WHERE obj_fi = %s",
4116 if ($result->numRows()) {
4117 $row =
$ilDB->fetchAssoc($result);
4118 $test_id = $row[
"test_id"];
4133 if (($active_id) && ($question_id)) {
4134 if ($pass ===
null) {
4137 if ($pass ===
null) {
4140 $query = $this->db->queryF(
4141 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
4142 [
'integer',
'integer',
'integer'],
4143 [$active_id, $question_id, $pass]
4145 $result = $this->db->fetchAll($query);
4146 if (count($result) == 1) {
4147 return $result[0][
"value1"];
4164 $result = $this->db->queryF(
4165 "SELECT question_text FROM qpl_questions WHERE question_id = %s",
4169 if ($result->numRows() == 1) {
4170 $row = $this->db->fetchAssoc($result);
4171 $res = $row[
"question_text"];
4180 $participant_list->initializeFromDbRows($this->getTestParticipants());
4182 return $participant_list;
4195 if ($this->getAnonymity()) {
4197 $result = $this->db->queryF(
4198 "SELECT tst_active.active_id, tst_active.tries, usr_id, %s login, %s lastname, %s firstname, " .
4199 "tst_active.submitted test_finished, matriculation, COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes FROM usr_data, tst_invited_user " .
4200 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
4201 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
4203 [
'text',
'text',
'text',
'integer',
'integer'],
4204 [
'', $this->
lng->txt(
'anonymous'),
'', $this->getTestId(),
$user_id]
4207 $result = $this->db->queryF(
4208 "SELECT tst_active.active_id, tst_active.tries, usr_id, %s login, %s lastname, %s firstname, " .
4209 "tst_active.submitted test_finished, matriculation, COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes FROM usr_data, tst_invited_user " .
4210 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
4211 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
4213 [
'text',
'text',
'text',
'integer'],
4214 [
'', $this->
lng->txt(
'anonymous'),
'', $this->getTestId()]
4219 $result = $this->db->queryF(
4220 "SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, " .
4221 "tst_active.submitted test_finished, matriculation, COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes FROM usr_data, tst_invited_user " .
4222 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
4223 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
4225 [
'integer',
'integer'],
4229 $result = $this->db->queryF(
4230 "SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, " .
4231 "tst_active.submitted test_finished, matriculation, COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes FROM usr_data, tst_invited_user " .
4232 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
4233 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
4236 [$this->getTestId()]
4241 while ($row = $this->db->fetchAssoc($result)) {
4242 $result_array[$row[
'usr_id']] = $row;
4244 return $result_array;
4251 SELECT tst_active.active_id,
4253 tst_active.user_fi usr_id,
4257 tst_active.submitted test_finished,
4258 usr_data.matriculation,
4260 tst_active.lastindex,
4261 COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes
4264 ON tst_active.user_fi = usr_data.usr_id
4265 WHERE tst_active.test_fi = %s
4266 ORDER BY usr_data.lastname
4268 $result = $this->db->queryF(
4270 [
'text',
'text',
'text',
'integer'],
4271 [
'', $this->
lng->txt(
"anonymous"),
"", $this->getTestId()]
4275 SELECT tst_active.active_id,
4277 tst_active.user_fi usr_id,
4281 tst_active.submitted test_finished,
4282 usr_data.matriculation,
4284 tst_active.lastindex,
4285 COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes
4288 ON tst_active.user_fi = usr_data.usr_id
4289 WHERE tst_active.test_fi = %s
4290 ORDER BY usr_data.lastname
4292 $result = $this->db->queryF(
4295 [$this->getTestId()]
4299 while ($row = $this->db->fetchAssoc($result)) {
4300 $data[$row[
'active_id']] = $row;
4302 foreach (
$data as $index => $participant) {
4303 if (strlen(trim($participant[
"firstname"] . $participant[
"lastname"])) == 0) {
4304 $data[$index][
"lastname"] = $this->
lng->txt(
"deleted_user");
4312 if (!$this->getGlobalSettings()->isManualScoringEnabled()) {
4316 $filtered_participants = [];
4317 foreach ($this->getTestParticipants() as $active_id => $participant) {
4318 if ($participant[
'tries'] > 0) {
4321 if ($this->test_man_scoring_done_helper->isDone((
int) $active_id)) {
4322 $filtered_participants[$active_id] = $participant;
4326 if (!$this->test_man_scoring_done_helper->isDone((
int) $active_id)) {
4327 $filtered_participants[$active_id] = $participant;
4331 $filtered_participants[$active_id] = $participant;
4335 return $filtered_participants;
4346 if (!is_array($ids) || count($ids) == 0) {
4350 if ($this->getAnonymity()) {
4351 $result = $this->db->queryF(
4352 "SELECT usr_id, %s login, %s lastname, %s firstname, client_ip clientip FROM usr_data WHERE " . $this->db->in(
'usr_id', $ids,
false,
'integer') .
" ORDER BY login",
4353 [
'text',
'text',
'text'],
4354 [
"", $this->lng->txt(
"anonymous"),
""]
4357 $result = $this->db->query(
"SELECT usr_id, login, lastname, firstname, client_ip clientip FROM usr_data WHERE " . $this->db->in(
'usr_id', $ids,
false,
'integer') .
" ORDER BY login");
4361 while ($row = $this->db->fetchAssoc($result)) {
4362 $result_array[$row[
"usr_id"]] = $row;
4364 return $result_array;
4369 if (!is_array($ids) || count($ids) == 0) {
4382 if (!is_array($ids) || count($ids) == 0) {
4386 foreach ($ids as $obj_id) {
4400 $this->db->manipulateF(
4401 "DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
4402 [
'integer',
'integer'],
4405 $this->db->manipulateF(
4406 "INSERT INTO tst_invited_user (test_fi, user_fi, ip_range_from, ip_range_to, tstamp) VALUES (%s, %s, %s, %s, %s)",
4407 [
'integer',
'integer',
'text',
'text',
'integer'],
4408 [$this->getTestId(),
$user_id, (strlen($client_ip)) ? $client_ip :
null, (strlen($client_ip)) ? $client_ip :
null,time()]
4421 if (is_numeric($question_fi)) {
4422 $result =
$ilDB->queryF(
4423 "SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s AND question_fi=%s",
4424 [
'integer',
'integer'],
4425 [$active_id, $question_fi]
4428 $result =
$ilDB->queryF(
4429 "SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s",
4435 while ($row =
$ilDB->fetchAssoc($result)) {
4436 $result_array[$row[
"question_fi"]] = $row;
4438 return $result_array;
4447 $active_id = $this->getActiveIdOfUser(
$user_id);
4448 $this->db->manipulateF(
4449 "DELETE FROM tst_qst_solved WHERE active_fi = %s AND question_fi = %s",
4450 [
'integer',
'integer'],
4451 [$active_id, $question_id]
4453 $this->db->manipulateF(
4454 "INSERT INTO tst_qst_solved (solved, question_fi, active_fi) VALUES (%s, %s, %s)",
4455 [
'integer',
'integer',
'integer'],
4456 [$value, $question_id, $active_id]
4465 $result = $this->db->queryF(
4466 "SELECT submitted FROM tst_active WHERE active_id=%s AND submitted=%s",
4467 [
'integer',
'integer'],
4470 return $result->numRows() == 1;
4482 $result = $this->db->queryF(
4483 "SELECT submitted FROM tst_active WHERE test_fi=%s AND user_fi=%s AND submitted=%s",
4484 [
'integer',
'integer',
'integer'],
4487 return $result->numRows() == 1;
4495 return $this->getNrOfTries() != 0;
4506 return $tries >= $this->getNrOfTries();
4521 "user_id" => $this->
lng->txt(
"user_id"),
4522 "matriculation" => $this->
lng->txt(
"matriculation"),
4523 "lastname" => $this->
lng->txt(
"lastname"),
4524 "firstname" => $this->
lng->txt(
"firstname"),
4525 "login" => $this->
lng->txt(
"login"),
4526 "reached_points" => $this->
lng->txt(
"tst_reached_points"),
4527 "max_points" => $this->
lng->txt(
"tst_maximum_points"),
4528 "percent_value" => $this->
lng->txt(
"tst_percent_solved"),
4529 "mark" => $this->
lng->txt(
"tst_mark"),
4530 "passed" => $this->
lng->txt(
"tst_mark_passed"),
4533 if (count($participants)) {
4534 foreach ($participants as $active_id => $user_rec) {
4537 $reached_points = 0;
4541 if (!is_int($pass)) {
4544 foreach ($this->questions as $value) {
4546 if (is_object($question)) {
4547 $max_points += $question->getMaximumPoints();
4548 $reached_points += $question->getReachedPoints($active_id, $pass);
4551 if ($max_points > 0) {
4552 $percentvalue = $reached_points / $max_points;
4553 if ($percentvalue < 0) {
4554 $percentvalue = 0.0;
4559 $mark_obj = $this->getMarkSchema()->getMatchingMark($percentvalue * 100);
4561 if ($mark_obj !==
null) {
4562 $mark = $mark_obj->getOfficialName();
4564 if ($this->getAnonymity()) {
4565 $user_rec[
'firstname'] =
"";
4566 $user_rec[
'lastname'] = $this->
lng->txt(
"anonymous");
4569 "user_id" => $user_rec[
'usr_id'],
4570 "matriculation" => $user_rec[
'matriculation'],
4571 "lastname" => $user_rec[
'lastname'],
4572 "firstname" => $user_rec[
'firstname'],
4573 "login" => $user_rec[
'login'],
4574 "reached_points" => $reached_points,
4575 "max_points" => $max_points,
4576 "percent_value" => $percentvalue,
4578 "passed" => $user_rec[
'passed'] ?
'1' :
'0',
4597 $result =
$ilDB->queryF(
4598 "SELECT tries FROM tst_active WHERE active_id = %s",
4602 if ($result->numRows()) {
4603 $row =
$ilDB->fetchAssoc($result);
4604 return $row[
"tries"];
4623 $result =
$ilDB->queryF(
4624 "SELECT MAX(pass) maxpass FROM tst_pass_result WHERE active_fi = %s",
4629 if ($result->numRows()) {
4630 $row =
$ilDB->fetchAssoc($result);
4631 return $row[
"maxpass"];
4647 $result =
$ilDB->queryF(
4648 "SELECT * FROM tst_pass_result WHERE active_fi = %s",
4653 if (!$result->numRows()) {
4659 while ($row =
$ilDB->fetchAssoc($result)) {
4660 if ($row[
"maxpoints"] > 0.0) {
4661 $factor = (float) ($row[
"points"] / $row[
"maxpoints"]);
4665 if ($factor === 0.0 && $bestfactor === 0.0
4666 || $factor > $bestfactor) {
4668 $bestfactor = $factor;
4672 if (is_array($bestrow)) {
4673 return $bestrow[
"pass"];
4689 $counted_pass =
null;
4695 return $counted_pass;
4709 if ($this->isRandomTest()) {
4710 $this->loadQuestions($active_id, $pass);
4713 foreach ($this->questions as $value) {
4714 if ($this->questionrepository->lookupResultRecordExist($active_id, $value, $pass)) {
4715 $workedthrough += 1;
4718 return $workedthrough;
4732 if (is_null($pass)) {
4737 SELECT tst_pass_result.tstamp pass_res_tstamp,
4738 tst_test_result.tstamp quest_res_tstamp
4740 FROM tst_pass_result
4742 LEFT JOIN tst_test_result
4743 ON tst_test_result.active_fi = tst_pass_result.active_fi
4744 AND tst_test_result.pass = tst_pass_result.pass
4746 WHERE tst_pass_result.active_fi = %s
4747 AND tst_pass_result.pass = %s
4749 ORDER BY tst_test_result.tstamp DESC
4752 $result =
$ilDB->queryF(
4754 [
'integer',
'integer'],
4758 while ($row =
$ilDB->fetchAssoc($result)) {
4759 if ($row[
'quest_res_tstamp']) {
4760 return $row[
'quest_res_tstamp'];
4763 return $row[
'pass_res_tstamp'];
4780 "executable" =>
true,
4781 "errormessage" =>
""
4784 if (!$this->getObjectProperties()->getPropertyIsOnline()->getIsOnline()) {
4785 $result[
"executable"] =
false;
4786 $result[
"errormessage"] = $this->
lng->txt(
'autosave_failed') .
': ' . $this->
lng->txt(
'offline');
4790 if (!$this->startingTimeReached()) {
4791 $result[
"executable"] =
false;
4795 if ($this->endingTimeReached()) {
4796 $result[
"executable"] =
false;
4801 $active_id = $this->getActiveIdOfUser(
$user_id);
4803 if ($this->getEnableProcessingTime()
4805 && ($starting_time = $this->getStartingTimeOfUser($active_id)) !==
false
4806 && $this->isMaxProcessingTimeReached($starting_time, $active_id)) {
4807 $result[
"executable"] =
false;
4808 $result[
"errormessage"] = $this->
lng->txt(
"detail_max_processing_time_reached");
4813 $testPassesSelector->setActiveId($active_id);
4814 $testPassesSelector->setLastFinishedPass($test_session->getLastFinishedPass());
4816 if ($this->hasNrOfTriesRestriction() && ($active_id > 0)) {
4817 $closedPasses = $testPassesSelector->getClosedPasses();
4819 if (count($closedPasses) >= $this->getNrOfTries()) {
4820 $result[
"executable"] =
false;
4821 $result[
"errormessage"] = $this->
lng->txt(
"maximum_nr_of_tries_reached");
4825 if ($this->isBlockPassesAfterPassedEnabled() && !$testPassesSelector->openPassExists()) {
4826 if ($this->test_result_repository->isPassed(
$user_id, $this->getId())) {
4827 $result[
'executable'] =
false;
4828 $result[
'errormessage'] = $this->
lng->txt(
"tst_addit_passes_blocked_after_passed_msg");
4834 $next_pass_allowed_timestamp = 0;
4835 if (!$this->isNextPassAllowed($testPassesSelector, $next_pass_allowed_timestamp)) {
4838 $result[
'executable'] =
false;
4839 $result[
'errormessage'] = sprintf($this->
lng->txt(
'wait_for_next_pass_hint_msg'), $date);
4847 $waiting_between_passes = $this->getMainSettings()->getTestBehaviourSettings()->getPassWaiting();
4851 $this->getMainSettings()->getTestBehaviourSettings()->getPassWaitingEnabled()
4852 && ($waiting_between_passes !==
'')
4854 && ($last_finished_pass_timestamp !==
null)
4856 $time_values = explode(
':', $waiting_between_passes);
4857 $next_pass_allowed_timestamp = strtotime(
'+ ' . $time_values[0] .
' Days + ' . $time_values[1] .
' Hours' . $time_values[2] .
' Minutes', $last_finished_pass_timestamp);
4858 return (time() > $next_pass_allowed_timestamp);
4868 $pass_selector->setActiveId($test_session->
getActiveId());
4871 return $pass_selector->hasReportablePasses();
4878 $pass_selector->setActiveId($test_session->
getActiveId());
4881 return $pass_selector->hasExistingPasses();
4893 if ($active_id < 1) {
4896 if ($pass ===
null) {
4897 $pass = ($this->getResetProcessingTime()) ? self::_getPass($active_id) : 0;
4899 $result = $this->db->queryF(
4900 "SELECT tst_times.started FROM tst_times WHERE tst_times.active_fi = %s AND tst_times.pass = %s ORDER BY tst_times.started",
4901 [
'integer',
'integer'],
4904 if ($result->numRows()) {
4905 $row = $this->db->fetchAssoc($result);
4906 if (preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"started"], $matches)) {
4931 if (!$this->getEnableProcessingTime()) {
4935 $processing_time = $this->getProcessingTimeInSeconds($active_id);
4937 if ($now > ($starting_time + $processing_time)) {
4946 $tags_trafo = $this->
refinery->string()->stripTags();
4950 questtypes.type_tag,
4952 origquest.obj_fi orig_obj_fi
4954 FROM qpl_questions questions
4956 INNER JOIN qpl_qst_type questtypes
4957 ON questtypes.question_type_id = questions.question_type_fi
4959 INNER JOIN tst_test_question tstquest
4960 ON tstquest.question_fi = questions.question_id
4962 LEFT JOIN qpl_questions origquest
4963 ON origquest.question_id = questions.original_id
4965 WHERE tstquest.test_fi = %s
4967 ORDER BY tstquest.sequence
4970 $query_result = $this->db->queryF(
4973 [$this->getTestId()]
4978 while ($row = $this->db->fetchAssoc($query_result)) {
4979 $row[
'title'] = $tags_trafo->transform($row[
'title']);
4980 $row[
'description'] = $tags_trafo->transform($row[
'description'] !==
'' && $row[
'description'] !==
null ? $row[
'description'] :
' ');
4981 $row[
'author'] = $tags_trafo->transform($row[
'author']);
4983 $questions[] = $row;
4991 foreach ($this->getTestQuestions() as $questionData) {
4992 if ($questionData[
'question_id'] != $question_id) {
5004 $row = $this->db->fetchAssoc($this->db->queryF(
5005 "SELECT COUNT(question_id) cnt FROM qpl_questions WHERE question_id = %s AND obj_fi = %s",
5006 [
'integer',
'integer'],
5007 [$question_id, $this->getId()]
5010 return (
bool) $row[
'cnt'];
5017 foreach ($this->getTestQuestions() as $question_data) {
5018 $points += $question_data[
'points'];
5031 questtypes.type_tag,
5032 origquest.obj_fi orig_obj_fi
5034 FROM qpl_questions questions
5036 INNER JOIN qpl_qst_type questtypes
5037 ON questtypes.question_type_id = questions.question_type_fi
5039 INNER JOIN tst_rnd_cpy tstquest
5040 ON tstquest.qst_fi = questions.question_id
5042 LEFT JOIN qpl_questions origquest
5043 ON origquest.question_id = questions.original_id
5045 WHERE tstquest.tst_fi = %s
5048 $query_result = $this->db->queryF(
5051 [$this->getTestId()]
5054 return $this->db->fetchAll($query_result);
5059 return $this->getMainSettings()->getQuestionBehaviourSettings()->getShuffleQuestions();
5075 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getUsrPassOverviewMode();
5080 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getQuestionListEnabled();
5085 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getUsrPassOverviewEnabled();
5090 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getShownQuestionListAtBeginning();
5095 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getShownQuestionListAtEnd();
5100 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getShowDescriptionInQuestionList();
5108 return $this->getScoreSettings()->getResultDetailsSettings()->getShowPassDetails();
5116 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionPrintview();
5123 return $this->getShowSolutionPrintview();
5131 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionFeedback();
5139 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionAnswersOnly();
5147 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionSignature();
5155 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionSuggested();
5164 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionListComparison();
5169 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionListOwnAnswers();
5179 $result =
$ilDB->queryF(
5180 "SELECT user_fi FROM tst_active WHERE active_id = %s",
5184 if ($result->numRows()) {
5185 $row =
$ilDB->fetchAssoc($result);
5186 return $row[
"user_fi"];
5194 $result = $this->db->queryF(
5195 "SELECT finished FROM tst_times WHERE active_fi = %s ORDER BY finished DESC",
5199 if ($result->numRows()) {
5200 $row = $this->db->fetchAssoc($result);
5201 return $row[
"finished"];
5206 public static function lookupLastTestPassAccess(
int $active_id,
int $pass_index): ?
int
5213 SELECT MAX(tst_times.tstamp) as last_pass_access
5215 WHERE active_fi = %s
5221 [
'integer',
'integer'],
5222 [$active_id, $pass_index]
5225 while ($row =
$ilDB->fetchAssoc(
$res)) {
5226 return $row[
'last_pass_access'];
5241 if (preg_match(
"/<[^>]*?>/", $a_text)) {
5258 for ($i = 0; $i < $a_material->getMaterialCount(); $i++) {
5259 $material = $a_material->getMaterial($i);
5260 if ($material[
'type'] ===
'mattext') {
5261 $result .= $material[
'material']->getContent();
5263 if ($material[
'type'] ===
'matimage') {
5264 $matimage = $material[
'material'];
5265 if (preg_match(
'/(il_([0-9]+)_mob_([0-9]+))/', $matimage->getLabel(), $matches)) {
5267 'mob' => $matimage->getLabel(),
5268 'uri' => $matimage->getUri()
5274 $decoded_result = base64_decode($result);
5275 if (str_starts_with($decoded_result,
'<PageObject>')) {
5276 $result = $decoded_result;
5289 'texttype' =>
'text/plain'
5293 if ($page_id !==
null) {
5294 $attrs[
'texttype'] =
'text/xml';
5297 $page_object->buildDom();
5298 $page_object->insertInstIntoIDs((
string)
IL_INST_ID);
5299 $material = base64_encode($page_object->getXMLFromDom());
5301 foreach ($file_ids as $file_id) {
5302 $this->file_ids[] = (
int) $file_id;
5305 } elseif ($this->isHTML($material)) {
5306 $attrs[
'texttype'] =
'text/xhtml';
5308 $mob_string =
'mm_';
5311 $xml_writer->
xmlElement(
'mattext', $attrs, $material);
5312 foreach ($mobs as $mob) {
5313 $mob_id_string = (string) $mob;
5314 $moblabel =
'il_' .
IL_INST_ID .
'_mob_' . $mob_id_string;
5315 if (strpos($material, $mob_string . $mob_id_string) !==
false) {
5319 'label' => $moblabel,
5320 'uri' =>
'objects/' .
'il_' .
IL_INST_ID .
'_mob_' . $mob_id_string .
'/' . $mob_obj->getTitle()
5323 $xml_writer->
xmlElement(
'matimage', $imgattrs,
null);
5337 if ($txt_output ==
null) {
5342 $prepare_for_latex_output,
5343 $omitNl2BrWhenTextArea
5349 return $this->getMainSettings()->getGeneralSettings()->getAnonymity();
5354 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getSuspendTestAllowed();
5359 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getQuestionMarkingEnabled();
5364 return $this->getMainSettings()->getAccessSettings()->getFixedParticipants();
5369 return $this->main_settings_repository->getFor(
5370 self::_getTestIDFromObjectID(self::_getObjectIDFromActiveID($active_id)),
5371 )->getGeneralSettings()->getQuestionSetType();
5386 if ($this->getAnonymity() && !$overwrite_anonymity) {
5387 return $this->
lng->txt(
"anonymous") . $suffix;
5390 if (strlen($uname[
"firstname"] . $uname[
"lastname"]) == 0) {
5391 $uname[
"firstname"] = $this->
lng->txt(
"deleted_user");
5393 if ($sorted_order) {
5394 return trim($uname[
"lastname"] .
", " . $uname[
"firstname"]) . $suffix;
5396 return trim($uname[
"firstname"] .
" " . $uname[
"lastname"]) . $suffix;
5402 DateTimeImmutable|
int|
null $date_time
5403 ): ?DateTimeImmutable {
5404 if ($date_time === null || $date_time instanceof DateTimeImmutable) {
5408 return DateTimeImmutable::createFromFormat(
'U', (
string) $date_time);
5420 if (extension_loaded(
"tidy")) {
5423 "output-xml" =>
true,
5424 "numeric-entities" =>
true
5427 $tidy->parseString($print_output, $config,
'utf8');
5428 $tidy->cleanRepair();
5429 $print_output = tidy_get_output($tidy);
5430 $print_output = preg_replace(
"/^.*?(<html)/",
"\\1", $print_output);
5432 $print_output = str_replace(
" ",
" ", $print_output);
5433 $print_output = str_replace(
"⊗",
"X", $print_output);
5435 $xsl = file_get_contents(
"./components/ILIAS/Test/xml/question2fo.xsl");
5440 'font-family="Helvetica, unifont"',
5441 'font-family="' . $this->
settings->get(
'rpc_pdf_font',
'Helvetica, unifont') .
'"',
5445 $args = [
'/_xml' => $print_output,
'/_xsl' => $xsl ];
5446 $xh = xslt_create();
5448 $output = xslt_process($xh,
"arg:/_xml",
"arg:/_xsl",
null, $args,
$params);
5462 $content = preg_replace(
"/href=\".*?\"/",
"", $content);
5463 $printbody =
new ilTemplate(
"tpl.il_as_tst_print_body.html",
true,
true,
"components/ILIAS/Test");
5465 $printbody->setVariable(
"ADM_CONTENT", $content);
5466 $printbody->setCurrentBlock(
"css_file");
5468 $printbody->parseCurrentBlock();
5469 $printoutput = $printbody->get();
5470 $html = str_replace(
"href=\"./",
"href=\"" . ILIAS_HTTP_PATH .
"/", $printoutput);
5471 $html = preg_replace(
"/<div id=\"dontprint\">.*?<\\/div>/ims",
"", $html);
5472 if (extension_loaded(
"tidy")) {
5475 "output-xml" =>
true,
5476 "numeric-entities" =>
true
5479 $tidy->parseString($html, $config,
'utf8');
5480 $tidy->cleanRepair();
5481 $html = tidy_get_output($tidy);
5482 $html = preg_replace(
"/^.*?(<html)/",
"\\1", $html);
5484 $html = str_replace(
" ",
" ", $html);
5485 $html = str_replace(
"⊗",
"X", $html);
5487 $html = preg_replace(
"/src=\".\\//ims",
"src=\"" . ILIAS_HTTP_PATH .
"/", $html);
5488 $this->deliverPDFfromFO($this->processPrintoutput2FO($html), $title);
5499 $fp = fopen($fo_file,
"w");
5508 $pdf_base64->scalar,
5513 }
catch (Exception
$e) {
5514 $this->
logger->info(__METHOD__ .
': ' .
$e->getMessage());
5530 if ($pass ===
null) {
5534 $row = self::getSingleManualFeedback((
int) $active_id, (
int) $question_id, (
int) $pass);
5537 $feedback = $row[
'feedback'] ??
'';
5548 $result =
$ilDB->queryF(
5549 "SELECT * FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
5550 [
'integer',
'integer',
'integer'],
5551 [$active_id, $question_id, $pass]
5554 if (
$ilDB->numRows($result) === 1) {
5555 $row =
$ilDB->fetchAssoc($result);
5557 } elseif (
$ilDB->numRows($result) > 1) {
5558 $DIC->logger()->root()->warning(
5559 "WARNING: Multiple feedback entries on tst_manual_fb for " .
5560 "active_fi = $active_id , question_fi = $question_id and pass = $pass"
5580 $result =
$ilDB->queryF(
5581 "SELECT * FROM tst_manual_fb WHERE question_fi = %s",
5586 while ($row =
$ilDB->fetchAssoc($result)) {
5587 $active = $row[
'active_fi'];
5588 $pass = $row[
'pass'];
5589 $question = $row[
'question_fi'];
5593 $feedback[$active][$pass][$question] = $row;
5604 bool $finalized =
false
5606 $feedback_old = self::getSingleManualFeedback($active_id, $question_id, $pass);
5607 $this->db->manipulateF(
5608 'DELETE FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s',
5609 [
'integer',
'integer',
'integer'],
5610 [$active_id, $question_id, $pass]
5613 $this->insertManualFeedback($active_id, $question_id, $pass, $feedback, $finalized, $feedback_old);
5625 $next_id = $this->db->nextId(
'tst_manual_fb');
5626 $user = $this->
user->getId();
5627 $finalized_time = time();
5630 'manual_feedback_id' => [
'integer', $next_id],
5631 'active_fi' => [
'integer', $active_id],
5632 'question_fi' => [
'integer', $question_id],
5633 'pass' => [
'integer', $pass],
5635 'tstamp' => [
'integer', time()]
5638 if ($feedback_old !== [] && (
int) $feedback_old[
'finalized_evaluation'] === 1) {
5639 $user = $feedback_old[
'finalized_by_usr_id'];
5640 $finalized_time = $feedback_old[
'finalized_tstamp'];
5643 if ($finalized ===
false) {
5644 $update_default[
'finalized_evaluation'] = [
'integer', 0];
5645 $update_default[
'finalized_by_usr_id'] = [
'integer', 0];
5646 $update_default[
'finalized_tstamp'] = [
'integer', 0];
5647 } elseif ($finalized ===
true) {
5648 $update_default[
'finalized_evaluation'] = [
'integer', 1];
5649 $update_default[
'finalized_by_usr_id'] = [
'integer', $user];
5650 $update_default[
'finalized_tstamp'] = [
'integer', $finalized_time];
5653 $this->db->insert(
'tst_manual_fb', $update_default);
5655 if ($this->
logger->isLoggingEnabled()) {
5656 $this->
logger->logScoringInteraction(
5657 $this->
logger->getInteractionFactory()->buildScoringInteraction(
5660 $this->user->getId(),
5661 self::_getUserIdFromActiveId($active_id),
5662 TestScoringInteractionTypes::QUESTION_GRADED,
5664 AdditionalInformationGenerator::KEY_EVAL_FINALIZED => $this->logger
5665 ->getAdditionalInformationGenerator()->getTrueFalseTagForBool($finalized),
5687 $this->test_sequence =
new ilTestSequence($active_id, $pass, $this->isRandomTest(), $this->questionrepository);
5697 $this->test_id = $a_id;
5710 if (count($participants)) {
5711 foreach ($participants as $active_id => $user_rec) {
5713 $reached_points = 0;
5716 foreach ($this->questions as $value) {
5718 if (is_object($question)) {
5719 $max_points += $question->getMaximumPoints();
5720 $reached_points += $question->getReachedPoints($active_id, $pass);
5721 if ($max_points > 0) {
5722 $percentvalue = $reached_points / $max_points;
5723 if ($percentvalue < 0) {
5724 $percentvalue = 0.0;
5729 if ($this->getAnonymity()) {
5730 $user_rec[
'firstname'] =
"";
5731 $user_rec[
'lastname'] = $this->
lng->txt(
"anonymous");
5734 "user_id" => $user_rec[
'usr_id'],
5735 "matriculation" => $user_rec[
'matriculation'],
5736 "lastname" => $user_rec[
'lastname'],
5737 "firstname" => $user_rec[
'firstname'],
5738 "login" => $user_rec[
'login'],
5739 "question_id" => $question->getId(),
5740 "question_title" => $question->getTitle(),
5741 "reached_points" => $reached_points,
5742 "max_points" => $max_points,
5743 "passed" => $user_rec[
'passed'] ?
'1' :
'0',
5760 $result =
$ilDB->queryF(
5761 'SELECT t.obj_fi obj_id FROM tst_test_question q, tst_tests t WHERE q.test_fi = t.test_id AND q.question_fi = %s',
5765 $rec =
$ilDB->fetchAssoc($result);
5766 return $rec[
'obj_id'] ??
null;
5777 if (!$this->component_repository->getComponentByTypeAndName(
5780 )->getPluginSlotById(
'qst')->hasPluginName($a_pname)) {
5784 return $this->component_repository
5785 ->getComponentByTypeAndName(
5789 ->getPluginSlotById(
5803 SELECT tst_test_result.active_fi, tst_test_result.question_fi, tst_test_result.pass
5804 FROM tst_test_result
5805 INNER JOIN tst_active ON tst_active.active_id = tst_test_result.active_fi AND tst_active.test_fi = %s
5806 INNER JOIN qpl_questions ON qpl_questions.question_id = tst_test_result.question_fi
5807 LEFT JOIN usr_data ON usr_data.usr_id = tst_active.user_fi
5808 WHERE tst_test_result.question_fi = %s
5809 ORDER BY usr_data.lastname ASC, usr_data.firstname ASC
5812 $result = $this->db->queryF(
5814 [
'integer',
'integer'],
5815 [$test_id, $question_id]
5819 while ($row = $this->db->fetchAssoc($result)) {
5820 if ($this->getAccessFilteredParticipantList() && !$this->getAccessFilteredParticipantList()->isActiveIdInList($row[
"active_fi"])) {
5824 if (!array_key_exists($row[
"active_fi"], $foundusers)) {
5825 $foundusers[$row[
"active_fi"]] = [];
5827 array_push($foundusers[$row[
"active_fi"]], [
"pass" => $row[
"pass"],
"qid" => $row[
"question_fi"]]);
5834 $data = $this->getCompleteEvaluationData();
5835 $found_participants =
$data->getParticipants();
5836 $results = [
'overview' => [],
'questions' => []];
5837 if ($found_participants !== []) {
5838 $results[
'overview'][
'tst_stat_result_mark_median'] =
$data->getStatistics()->getEvaluationDataOfMedianUser()?->getMark()?->getShortName() ??
'';
5839 $results[
'overview'][
'tst_stat_result_rank_median'] =
$data->getStatistics()->rankMedian();
5840 $results[
'overview'][
'tst_stat_result_total_participants'] =
$data->getStatistics()->count();
5841 $results[
'overview'][
'tst_stat_result_median'] =
$data->getStatistics()->median();
5842 $results[
'overview'][
'tst_eval_total_persons'] = count($found_participants);
5843 $total_finished =
$data->getTotalFinishedParticipants();
5844 $results[
'overview'][
'tst_eval_total_finished'] = $total_finished;
5845 $results[
'overview'][
'tst_eval_total_finished_average_time'] =
5846 $this->secondsToHoursMinutesSecondsString(
5847 $this->evalTotalStartedAverageTime(
$data->getParticipantIds())
5850 $total_passed_reached = 0;
5851 $total_passed_max = 0;
5852 $total_passed_time = 0;
5853 foreach ($found_participants as $userdata) {
5854 if ($userdata->getMark()?->getPassed()) {
5856 $total_passed_reached += $userdata->getReached();
5857 $total_passed_max += $userdata->getMaxpoints();
5858 $total_passed_time += $userdata->getTimeOnTask();
5861 $average_passed_reached = $total_passed ? $total_passed_reached / $total_passed : 0;
5862 $average_passed_max = $total_passed ? $total_passed_max / $total_passed : 0;
5863 $average_passed_time = $total_passed ? $total_passed_time / $total_passed : 0;
5864 $results[
'overview'][
'tst_eval_total_passed'] = $total_passed;
5865 $results[
'overview'][
'tst_eval_total_passed_average_points'] = sprintf(
'%2.2f', $average_passed_reached)
5866 .
' ' . strtolower(
'of') .
' ' . sprintf(
'%2.2f', $average_passed_max);
5867 $results[
'overview'][
'tst_eval_total_passed_average_time'] =
5868 $this->secondsToHoursMinutesSecondsString((
int) $average_passed_time);
5871 foreach (
$data->getQuestionTitles() as $question_id => $question_title) {
5875 foreach ($found_participants as $userdata) {
5876 for ($i = 0; $i <= $userdata->getLastPass(); $i++) {
5877 if (is_object($userdata->getPass($i))) {
5878 $question = $userdata->getPass($i)->getAnsweredQuestionByQuestionId($question_id);
5879 if (is_array($question)) {
5881 $reached += $question[
'reached'];
5882 $max += $question[
'points'];
5887 $percent = $max ? $reached / $max * 100.0 : 0;
5888 $results[
'questions'][$question_id] = [
5890 sprintf(
'%.2f', $answered ? $reached / $answered : 0) .
' ' . strtolower($this->
lng->txt(
'of')) .
' ' . sprintf(
'%.2f', $answered ? $max / $answered : 0),
5891 sprintf(
'%.2f', $percent) .
'%',
5893 sprintf(
'%.2f', $answered ? $reached / $answered : 0),
5894 sprintf(
'%.2f', $answered ? $max / $answered : 0),
5903 $diff_hours = floor($seconds / 3600);
5904 $seconds -= $diff_hours * 3600;
5905 $diff_minutes = floor($seconds / 60);
5906 $seconds -= $diff_minutes * 60;
5907 return sprintf(
'%02d:%02d:%02d', $diff_hours, $diff_minutes, $seconds);
5915 return $this->export_factory->getExporter($this,
'xml')
5921 return $this->getScoreSettings()->getResultDetailsSettings()->getExportSettings();
5926 $this->template_id = $template_id;
5931 return $this->template_id;
5936 $question_set_config = $this->question_set_config_factory->getQuestionSetConfig();
5937 $reindexed_sequence_position_map = $question_set_config->reindexQuestionOrdering();
5939 $this->loadQuestions();
5941 return $reindexed_sequence_position_map;
5950 foreach (array_keys($order) as
$id) {
5954 UPDATE tst_test_question
5956 WHERE question_fi = %s
5959 $this->db->manipulateF(
5961 [
'integer',
'integer'],
5966 if ($this->
logger->isLoggingEnabled()) {
5967 $this->
logger->logTestAdministrationInteraction(
5968 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
5970 $this->user->getId(),
5971 TestAdministrationInteractionTypes::QUESTION_MOVED,
5973 AdditionalInformationGenerator::KEY_QUESTION_ORDER => $order
5979 $this->loadQuestions();
5984 $questions = $this->getQuestionTitlesAndIndexes();
5986 $IN_questions = $this->db->in(
'q1.question_id', array_keys($questions),
false,
'integer');
5989 SELECT count(q1.question_id) cnt
5991 FROM qpl_questions q1
5993 INNER JOIN qpl_questions q2
5994 ON q2.question_id = q1.original_id
5997 AND q1.obj_fi = q2.obj_fi
5999 $rset = $this->db->query($query);
6000 $row = $this->db->fetchAssoc($rset);
6002 return $row[
'cnt'] > 0;
6016 $result =
$ilDB->queryF(
6017 "SELECT test_fi,MAX(pass) AS pass FROM tst_active" .
6018 " JOIN tst_pass_result ON (tst_pass_result.active_fi = tst_active.active_id)" .
6019 " WHERE user_fi=%s" .
6020 " GROUP BY test_fi",
6021 [
'integer',
'integer'],
6025 while ($row =
$ilDB->fetchAssoc($result)) {
6026 $obj_id = self::_getObjectIDFromTestID($row[
"test_fi"]);
6027 $all[$obj_id] = (bool) $row[
"pass"];
6034 return $this->questions;
6039 return $this->online;
6044 $page_id = $this->getMainSettings()->getIntroductionSettings()->getIntroductionPageId();
6045 if ($page_id !==
null) {
6050 $page_object->setParentId($this->
getId());
6051 $new_page_id = $page_object->createPageWithNextId();
6052 $settings = $this->getMainSettings()->getIntroductionSettings()
6053 ->withIntroductionPageId($new_page_id);
6054 $this->getMainSettingsRepository()->store(
6055 $this->getMainSettings()->withIntroductionSettings($settings)
6057 return $new_page_id;
6062 $page_id = $this->getMainSettings()->getFinishingSettings()->getConcludingRemarksPageId();
6063 if ($page_id !==
null) {
6068 $page_object->setParentId($this->
getId());
6069 $new_page_id = $page_object->createPageWithNextId();
6070 $settings = $this->getMainSettings()->getFinishingSettings()
6071 ->withConcludingRemarksPageId($new_page_id);
6072 $this->getMainSettingsRepository()->store(
6073 $this->getMainSettings()->withFinishingSettings($settings)
6075 return $new_page_id;
6080 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreEnabled();
6094 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreAnon();
6107 return $this->getAnonymity() == 1 || $this->getHighscoreAnon();
6115 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreAchievedTS();
6123 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreScore();
6131 return $this->getScoreSettings()->getGamificationSettings()->getHighscorePercentage();
6139 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreWTime();
6147 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreOwnTable();
6155 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreTopTable();
6164 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreTopNum();
6169 return $this->getScoreSettings()->getGamificationSettings()->getHighScoreMode();
6174 return $this->getMainSettings()->getQuestionBehaviourSettings()->getInstantFeedbackSpecificEnabled();
6179 return $this->getMainSettings()->getQuestionBehaviourSettings()->getAutosaveEnabled();
6184 return $this->getScoreSettings()->getResultSummarySettings()->getPassDeletionAllowed();
6189 return $this->getMainSettings()->getFinishingSettings()->getShowAnswerOverview();
6201 $result = $this->db->queryF(
6202 "SELECT tst_times.active_fi, tst_times.started FROM tst_times, tst_active WHERE tst_times.active_fi = tst_active.active_id AND tst_active.test_fi = %s ORDER BY tst_times.tstamp DESC",
6204 [$this->getTestId()]
6206 while ($row = $this->db->fetchAssoc($result)) {
6207 $times[$row[
'active_fi']] = $row[
'started'];
6215 $result = $this->db->queryF(
6216 "SELECT tst_addtime.active_fi, tst_addtime.additionaltime FROM tst_addtime, tst_active WHERE tst_addtime.active_fi = tst_active.active_id AND tst_active.test_fi = %s",
6218 [$this->getTestId()]
6220 while ($row = $this->db->fetchAssoc($result)) {
6221 $times[$row[
'active_fi']] = $row[
'additionaltime'];
6228 if ($active_id === 0) {
6231 return $this->participant_repository
6232 ->getParticipantByActiveId($this->getTestId(), $active_id)
6233 ?->getExtraTime() ?? 0;
6239 SELECT MAX(tst_pass_result.pass) + 1 max_res
6240 FROM tst_pass_result
6241 INNER JOIN tst_active ON tst_active.active_id = tst_pass_result.active_fi
6242 WHERE test_fi = ' . $this->db->quote($this->getTestId(),
'integer') .
'
6244 $res = $this->db->query($query);
6246 return (
int)
$data[
'max_res'];
6254 $exam_id_query =
'SELECT exam_id FROM tst_pass_result WHERE active_fi = %s AND pass = %s';
6255 $exam_id_result =
$ilDB->queryF($exam_id_query, [
'integer',
'integer' ], [ $active_id, $pass ]);
6256 if (
$ilDB->numRows($exam_id_result) == 1) {
6257 $exam_id_row =
$ilDB->fetchAssoc($exam_id_result);
6259 if ($exam_id_row[
'exam_id'] !=
null) {
6260 return $exam_id_row[
'exam_id'];
6267 public static function buildExamId($active_id, $pass, $test_obj_id =
null): string
6274 if ($test_obj_id ===
null) {
6275 $obj_id = self::_getObjectIDFromActiveID($active_id);
6277 $obj_id = $test_obj_id;
6280 $examId =
'I' . $inst_id .
'_T' . $obj_id .
'_A' . $active_id .
'_P' . $pass;
6287 return $this->getMainSettings()->getTestBehaviourSettings()->getExamIdInTestAttemptEnabled();
6292 return $this->getScoreSettings()->getResultDetailsSettings()->getShowExamIdInTestResults();
6298 $this->main_settings = $this->getMainSettings()->withGeneralSettings(
6300 ->withQuestionSetType($question_set_type)
6306 return $this->getMainSettings()->getGeneralSettings()->getQuestionSetType();
6316 return $this->getQuestionSetType() == self::QUESTION_SET_TYPE_FIXED;
6326 return $this->getQuestionSetType() == self::QUESTION_SET_TYPE_RANDOM;
6331 switch ($questionSetType) {
6333 return $lng->txt(
'tst_question_set_type_fixed');
6336 return $lng->txt(
'tst_question_set_type_random');
6339 throw new ilTestException(
'invalid question set type value given: ' . $questionSetType);
6344 if ($this->participantDataExist ===
null) {
6345 $this->participantDataExist = (bool) $this->evalTotalPersons();
6348 return $this->participantDataExist;
6357 $this->test_result_repository
6359 $scoring->setPreserveManualScores($preserve_manscoring);
6360 $scoring->recalculateSolutions();
6371 INNER JOIN tst_tests
6372 ON test_id = test_fi
6376 $res =
$ilDB->queryF($query, [
'integer'], [$userId]);
6380 while ($row =
$ilDB->fetchAssoc(
$res)) {
6381 $objIds[] = (
int) $row[
'obj_fi'];
6389 return $this->getMainSettings()->getAdditionalSettings()->getSkillsServiceEnabled();
6405 if (!$this->getMainSettings()->getAdditionalSettings()->getSkillsServiceEnabled()) {
6409 if (!self::isSkillManagementGloballyActivated()) {
6416 private static $isSkillManagementGloballyActivated =
null;
6420 if (self::$isSkillManagementGloballyActivated ===
null) {
6423 self::$isSkillManagementGloballyActivated = $skmgSet->isActivated();
6426 return self::$isSkillManagementGloballyActivated;
6431 return $this->getScoreSettings()->getResultSummarySettings()->getShowGradingStatusEnabled();
6436 return $this->getScoreSettings()->getResultSummarySettings()->getShowGradingMarkEnabled();
6441 return $this->getMainSettings()->getQuestionBehaviourSettings()->getLockAnswerOnNextQuestionEnabled();
6446 return $this->getMainSettings()->getQuestionBehaviourSettings()->getLockAnswerOnInstantFeedbackEnabled();
6451 return $this->getMainSettings()->getQuestionBehaviourSettings()->getForceInstantFeedbackOnNextQuestion();
6459 $ilUser =
$DIC[
'ilUser'];
6463 $active_id = $test_obj->getActiveIdOfUser(
$user_id);
6468 $test_session_factory->reset();
6470 $test_sequence_factory =
new ilTestSequenceFactory($test_obj,
$ilDB, TestDIC::dic()[
'question.general_properties.repository']);
6472 $test_session = $test_session_factory->getSession($active_id);
6473 $test_sequence = $test_sequence_factory->getSequenceByActiveIdAndPass($active_id, $test_session->getPass());
6474 $test_sequence->loadFromDb();
6476 return $test_sequence->hasSequence();
6482 SELECT COUNT(test_question_id) cnt
6483 FROM tst_test_question
6488 $questRes = $this->db->queryF($query, [
'integer'], [$this->getTestId()]);
6490 $row = $this->db->fetchAssoc($questRes);
6491 $questCount = $row[
'cnt'];
6493 if ($this->getShuffleQuestions()) {
6497 INNER JOIN tst_sequence tseq
6498 ON tseq.active_fi = tac.active_id
6499 WHERE tac.test_fi = %s
6502 $partRes = $this->db->queryF(
6505 [$this->getTestId()]
6508 while ($row = $this->db->fetchAssoc($partRes)) {
6509 $sequence = @unserialize($row[
'sequence']);
6515 $sequence = array_filter($sequence,
function ($value) use ($questCount) {
6516 return $value <= $questCount;
6519 $num_seq = count($sequence);
6520 if ($questCount > $num_seq) {
6521 $diff = $questCount - $num_seq;
6522 for ($i = 1; $i <= $diff; $i++) {
6523 $sequence[$num_seq + $i - 1] = $num_seq + $i;
6527 $new_sequence = serialize($sequence);
6529 $this->db->update(
'tst_sequence', [
6530 'sequence' => [
'clob', $new_sequence]
6532 'active_fi' => [
'integer', $row[
'active_fi']],
6533 'pass' => [
'integer', $row[
'pass']]
6537 $new_sequence = serialize($questCount > 0 ? range(1, $questCount) : []);
6542 INNER JOIN tst_sequence tseq
6543 ON tseq.active_fi = tac.active_id
6544 WHERE tac.test_fi = %s
6547 $part_rest = $this->db->queryF(
6550 [$this->getTestId()]
6553 while ($row = $this->db->fetchAssoc($part_rest)) {
6554 $this->db->update(
'tst_sequence', [
6555 'sequence' => [
'clob', $new_sequence]
6557 'active_fi' => [
'integer', $row[
'active_fi']],
6558 'pass' => [
'integer', $row[
'pass']]
6574 return $this->questionrepository;
6579 return $this->global_settings_repo->getGlobalSettings();
6584 if (!$this->main_settings) {
6585 $this->main_settings = $this->getMainSettingsRepository()
6586 ->getFor($this->getTestId());
6588 return $this->main_settings;
6593 return $this->main_settings_repository;
6598 if (!$this->score_settings) {
6599 $this->score_settings = $this->getScoreSettingsRepository()
6600 ->getFor($this->getTestId());
6602 return $this->score_settings;
6607 return $this->score_settings_repository;
6611 bool $old_online_status,
6612 bool $new_online_status
6614 if (!$old_online_status && $new_online_status) {
6616 $newsItem->setContext($this->
getId(),
'tst');
6618 $newsItem->setTitle(
'new_test_online');
6619 $newsItem->setContentIsLangVar(
true);
6620 $newsItem->setContent(
'');
6621 $newsItem->setUserId($this->
user->getId());
6623 $newsItem->create();
6627 if ($old_online_status && !$new_online_status) {
6633 if (!$new_online_status && $newsId > 0) {
6635 $newsItem->setTitle(
'new_test_online');
6636 $newsItem->setContentIsLangVar(
true);
6637 $newsItem->setContent(
'');
6638 $newsItem->update();
6647 return TestDIC::dic()[
'settings.main.repository']->getFor(
6649 )->getGeneralSettings()->getQuestionSetType() === self::QUESTION_SET_TYPE_RANDOM;
6654 return $this->participant_repository->getFirstAndLastVisitForActiveId($active_id);
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Class ResourceIdentification.
updatePassAndTestResults(array $active_ids)
removeAllQuestionResults($question_id)
A class defining mark schemas for assessment test objects.
A class defining marks for assessment test objects.
withGeneralSettings(SettingsGeneral $settings)
getQuestionBehaviourSettings()
getTestBehaviourSettings()
getParticipantFunctionalitySettings()
getIntroductionSettings()
getResultDetailsSettings()
withGamificationSettings(SettingsGamification $settings)
getGamificationSettings()
getResultSummarySettings()
raiseError(string $a_msg, int $a_err_obj)
wrapper for downward compability
static _getSolutionMaxPass(int $question_id, int $active_id)
Returns the maximum pass a users question solution.
static _getSuggestedSolutionOutput(int $question_id)
static instantiateQuestion(int $question_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static completeMissingPluginName(array $question_type_data)
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false, ?ilObjUser $user=null,)
@classDescription Date and time handling
static getASCIIFilename(string $a_filename)
static makeDir(string $a_dir)
creates a new directory and inherits all filesystem permissions of the parent directory You may pass ...
static ilTempnam(?string $a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
static getDataDir()
get data directory (outside webspace)
static createDirectory(string $a_dir, int $a_mod=0755)
create directory
static removeTrailingPathSeparators(string $path)
static getInstanceByType(string $type)
static _refreshStatus(int $a_obj_id, ?array $a_users=null)
A news item can be created by different sources.
static getFirstNewsIdForContext(int $a_context_obj_id, string $a_context_obj_type, int $a_context_sub_obj_id=0, string $a_context_sub_obj_type="")
Get first new id of news set related to a certain context.
static deleteNewsOfContext(int $a_context_obj_id, string $a_context_obj_type, int $a_context_sub_obj_id=0, string $a_context_sub_obj_type="")
Delete all news of a context.
static _getAvailableQuestionpools(bool $use_object_id=false, bool $equal_points=false, bool $could_be_offline=false, bool $showPath=false, bool $with_questioncount=false, string $permission='read', int $usr_id=0)
Returns the available question pools for the active user.
isPreviousSolutionReuseEnabled()
isNextPassAllowed(ilTestPassesSelector $testPassesSelector, int &$next_pass_allowed_timestamp)
setQuestionOrder(array $order)
static _getResultPass($active_id)
Retrieves the pass number that should be counted for a given user.
static _getObjectIDFromActiveID($active_id)
Returns the ILIAS test object id for a given active id.
static _instanciateQuestion($question_id)
Creates an instance of a question with a given question id.
getUserData($ids)
Returns a data of all users specified by id list.
buildIso8601PeriodForExportCompatibility(DateTimeImmutable $date_time)
getTestId()
Gets the database id of the additional test data.
endingTimeReached()
Returns true if the ending time of a test is reached An ending time is not available for self assessm...
setTestId($a_id)
Sets the test ID.
getTotalPointsPassedArray()
Returns an array with the total points of all users who passed the test This array could be used for ...
getShowKioskModeParticipant()
getHighscoreWTime()
Gets if the column with the workingtime should be shown.
getHighscoreOwnTable()
Gets if the own rankings table should be shown.
getAnsweredQuestionCount($active_id, $pass=null)
Retrieves the number of answered questions for a given user in a given test.
hasAnyTestResult(ilTestSession $test_session)
getHighscoreTopTable()
Gets, if the top-rankings table should be shown.
isShowExamIdInTestPassEnabled()
RequestDataCollector $testrequest
removeQuestionsWithResults(array $question_ids)
canShowSolutionPrintview($user_id=null)
getEnableProcessingTime()
getListOfQuestionsStart()
& getExistingQuestions($pass=null)
Get the id's of the questions which are already part of the test.
saveToDb(bool $properties_only=false)
getActiveParticipantList()
static _getTestIDFromObjectID($object_id)
Returns the ILIAS test id for a given object id.
pcArrayShuffle($array)
Shuffles the values of a given array.
getInstantFeedbackSolution()
getStartingTimeOfUser($active_id, $pass=null)
Returns the unix timestamp of the time a user started a test.
checkQuestionParent(int $question_id)
& getInvitedUsers(int $user_id=0, $order="login, lastname, firstname")
Returns a list of all invited users in a test.
getDetailedTestResults($participants)
returns all test results for all participants
isNrOfTriesReached($tries)
returns if number of tries are reached
static _lookupRandomTest(int $obj_id)
reindexFixedQuestionOrdering()
static getTestObjIdsWithActiveForUserId($userId)
getAllQuestions($pass=null)
Returns all questions of a test in test order.
MediaObjectRepository $media_object_repository
getPotentialRandomTestQuestions()
getScoreSettingsRepository()
inviteUser($user_id, $client_ip="")
Invites a user to a test.
removeTestResultsFromSoapLpAdministration(array $user_ids)
replaceFilesInPageImports(string $text, array $mappings)
getQuestiontext($question_id)
Returns the question text for a given question.
loadQuestions(int $active_id=0, ?int $pass=null)
Load the test question id's from the database.
const QUESTION_SET_TYPE_RANDOM
getQuestionTitlesAndIndexes()
Returns the titles of the test questions in question sequence.
static _getScoreCutting(int $active_id)
processPrintoutput2FO($print_output)
Convert a print output to XSL-FO.
getXMLZip()
Get zipped xml file for test.
getShowSolutionListComparison()
static lookupExamId($active_id, $pass)
createExportDirectory()
creates data directory for export files (data_dir/tst_data/tst_<id>/export, depending on data directo...
ExportImportFactory $export_factory
getAnonOnlyParticipantIds()
return int[]
static _getActiveIdOfUser($user_id="", $test_id="")
removeQuestionFromSequences(int $question_id, array $active_ids, ilTestReindexedSequencePositionMap $reindexed_sequence_position_map)
exportXMLPageObjects(&$a_xml_writer, $inst, &$expLog)
export page objects to xml (see ilias_co.dtd)
storeMarkSchema(MarkSchema $mark_schema)
& createTestSequence($active_id, $pass, $shuffle)
createQuestionGUI($question_type, $question_id=-1)
Creates a question GUI instance of a given question type.
questionMoveDown($question_id)
Moves a question down in order.
isMaxProcessingTimeReached(int $starting_time, int $active_id)
Returns whether the maximum processing time for a test is reached or not.
$metadata
A reference to an IMS compatible matadata set.
addToNewsOnOnline(bool $old_online_status, bool $new_online_status)
getExtraTime(int $active_id)
getWorkingTimeOfParticipantForPass(int $active_id, int $pass)
@depracated 11, will be removed in 12, use TestResultManager::fetchWorkingTime instead
ilTestParticipantAccessFilterFactory $participant_access_filter
hasQuestionsWithoutQuestionpool()
exportFileItems($target_dir, &$expLog)
export files of file itmes
getQuestionsOfTest(int $active_id)
getImagePath()
Returns the image path for web accessable images of a test The image path is under the CLIENT_WEB_DIR...
getShowSolutionSignature()
Returns if the signature field should be shown in the test results.
getHighscoreAnon()
Gets if the highscores should be anonymized per setting.
getPassScoring()
Gets the pass scoring type.
isShowGradingStatusEnabled()
isHighscoreAnon()
Gets if the highscores should be displayed anonymized.
getShowSolutionAnswersOnly()
Returns if the full solution (including ILIAS content) should be presented to the solution or not.
MarksRepository $marks_repository
startingTimeReached()
Returns true if the starting time of a test is reached A starting time is not available for self asse...
getProcessingTimeInSeconds(int $active_id=0)
getProcessingTimeForXML()
getShowPassDetails()
Returns if the pass details should be shown when a test is not finished.
getQuestionSetTypeTranslation(ilLanguage $lng, $questionSetType)
Filesystem $filesystem_web
getQuestionsOfPass(int $active_id, int $pass)
static _lookupAuthor($obj_id)
Gets the authors name of the ilObjTest object.
getShowSolutionFeedback()
Returns if the feedback should be presented to the solution or not.
$test_sequence
contains the test sequence data
getTimeExtensionsOfParticipants()
const QUESTION_SET_TYPE_FIXED
duplicateQuestionForTest($question_id)
Takes a question and creates a copy of the question for use in the test.
_getLastAccess(int $active_id)
isActiveTestSubmitted($user_id=null)
returns if the active for user_id has been submitted
getTestResult(int $active_id, ?int $attempt=null, bool $ordered_sequence=false, bool $consider_hidden_questions=true, bool $consider_optional_questions=true)
Calculates the results of a test for a given user and returns an array with all test results.
bool $activation_visibility
evalStatistical($active_id)
Returns the statistical evaluation of the test for a specified user.
cloneObject(int $target_id, int $copy_id=0, bool $omit_tree=false)
Clone object.
isExecutable($test_session, $user_id, $allow_pass_increase=false)
Checks if the test is executable by the given user.
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
getAuthor()
Gets the authors name of the ilObjTest object.
getAnswerFeedbackPoints()
getFixedQuestionSetTotalPoints()
isTestQuestion(int $question_id)
getScoreCutting()
Determines if the score of a question should be cut at 0 points or the score of the whole test.
getShowSolutionSuggested()
getHighscorePercentage()
Gets if the percentage column should be shown.
getQuestionCountWithoutReloading()
prepareTextareaOutput($txt_output, $prepare_for_latex_output=false, $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output in tests.
getTestParticipantsForManualScoring($filter=null)
getHtmlQuestionContentPurifier()
getAvailableQuestions($arr_filter, $completeonly=0)
Calculates the available questions for a test.
static _getObjectIDFromTestID($test_id)
Returns the ILIAS test object id for a given test id.
fromXML(ilQTIAssessment $assessment, array $mappings)
Receives parameters from a QTI parser and creates a valid ILIAS test object.
isSkillServiceToBeConsidered()
Returns whether this test must consider skills, usually by providing appropriate extensions in the us...
modifyExportIdentifier($a_tag, $a_param, $a_value)
Returns the installation id for a given identifier.
lookupQuestionSetTypeByActiveId(int $active_id)
buildDateTimeImmutableFromPeriod(?string $period)
updateWorkingTime($times_id)
Update the working time of a test when a question is answered.
canShowTestResults(ilTestSession $test_session)
removeQuestion(int $question_id)
static isSkillManagementGloballyActivated()
evalTotalStartedAverageTime(?array $active_ids_to_filter=null)
addQTIMaterial(ilXmlWriter &$xml_writer, ?int $page_id, string $material='')
buildStatisticsAccessFilteredParticipantList()
$evaluation_data
Contains the evaluation data settings the tutor defines for the user.
getGenericAnswerFeedback()
static _getUserIdFromActiveId(int $active_id)
doCreateMetaData()
@inheritDoc
static _getMaxPass($active_id)
Retrieves the maximum pass of a given user for a given test in which the user answered at least one q...
isRandomTest()
Returns the fact wether this test is a random questions test or not.
getQuestionType($question_id)
Returns the question type of a question with a given id.
getShowSolutionPrintview()
Returns if the solution printview should be presented to the user or not.
setAccessFilteredParticipantList(ilTestParticipantList $access_filtered_participant_list)
MainSettings $main_settings
removeTestResultsByUserIds(array $user_ids)
setQuestionSetSolved($value, $question_id, $user_id)
sets question solved state to value for given user_id
isFixedTest()
Returns the fact wether this test is a fixed question set test or not.
isComplete(ilTestQuestionSetConfig $test_question_set_config)
replaceMobsInPageImports(string $text, array $mappings)
& _getCompleteWorkingTimeOfParticipants($test_id)
Returns the complete working time in seconds for all test participants.
convertTimeToDateTimeImmutableIfNecessary(DateTimeImmutable|int|null $date_time)
isTestFinished($active_id)
returns if the active for user_id has been submitted
static _getBestPass($active_id)
Retrieves the best pass of a given user for a given test.
static getSingleManualFeedback(int $active_id, int $question_id, int $pass)
getQuestionDataset($question_id)
Returns the dataset for a given question id.
insertQuestion(int $question_id, bool $link_only=false)
getAggregatedResultsData()
MainSettingsRepository $main_settings_repository
int $activation_starting_time
addConcludingRemarksToSettingsFromImport(SettingsFinishing $settings, array $material, array $mappings)
getHighscoreAchievedTS()
Returns if date and time of the scores achievement should be displayed.
SettingsFactory $settings_factory
getHighscoreTopNum(int $a_retval=10)
Gets the number of entries which are to be shown in the top-rankings table.
Repository $test_result_repository
static _getCountSystem(int $active_id)
isHTML($a_text)
Checks if a given string contains HTML or not.
& getWorkedQuestions($active_id, $pass=null)
Gets the id's of all questions a user already worked through.
getListOfQuestionsSettings()
Returns the settings for the list of questions options in the test properties This could contain one ...
static _getScoreSettingsByActiveId(int $active_id)
startWorkingTime($active_id, $pass)
Write the initial entry for the tests working time to the database.
isFollowupQuestionAnswerFixationEnabled()
getCompleteWorkingTime($user_id)
Returns the complete working time in seconds a user worked on the test.
getQuestionTitle($title, $nr=null, $points=null)
Returns the title of a test question and checks if the title output is allowed.
static buildExamId($active_id, $pass, $test_obj_id=null)
setTemplate(int $template_id)
getAllTestResults($participants)
returns all test results for all participants
GlobalSettingsRepository $global_settings_repo
static _getPassScoring(int $active_id)
deliverPDFfromHTML($content, $title=null)
Delivers a PDF file from XHTML.
buildName(?int $user_id, ?string $firstname, ?string $lastname)
Builds a user name for the output depending on test type and existence of the user.
getImagePathWeb()
Returns the web image path for web accessable images of a test The image path is under the web access...
isShowExamIdInTestResultsEnabled()
static _createImportDirectory()
creates data directory for import files (data_dir/tst_data/tst_<id>/import, depending on data directo...
create()
note: title, description and type should be set when this function is called
ScoreSettingsRepository $score_settings_repository
static _getAvailableTests($use_object_id=false)
Returns the available tests for the active user.
ilTestQuestionSetConfigFactory $question_set_config_factory
getCompleteWorkingTimeOfParticipant($active_id)
Returns the complete working time in seconds for a test participant.
getCompleteEvaluationData($filterby='', $filtertext='')
evalTotalPersons()
Returns the number of persons who started the test.
getUnfilteredEvaluationData()
removeTestActives(array $active_ids)
removeTestResults(ilTestParticipantData $participant_data)
isInstantFeedbackAnswerFixationEnabled()
evalTotalParticipantsArray(string $name_sort_order='asc')
saveManualFeedback(int $active_id, int $question_id, int $pass, ?string $feedback, bool $finalized=false)
copyQuestions(array $question_ids)
getSpecificAnswerFeedback()
saveCompleteStatus(ilTestQuestionSetConfig $test_question_set_config)
bool $print_best_solution_with_result
saveAuthorToMetadata($author="")
Saves an authors name into the lifecycle metadata if no lifecycle metadata exists This will only be c...
isPluginActive($a_pname)
Checks wheather or not a question plugin with a given name is active.
evalTotalPersonsArray(string $name_sort_order='asc')
deliverPDFfromFO($fo, $title=null)
Delivers a PDF file from a XSL-FO string.
static getManualFeedback(int $active_id, int $question_id, ?int $pass)
Retrieves the feedback comment for a question in a test if it is finalized.
GeneralQuestionPropertiesRepository $questionrepository
toXML()
Returns a QTI xml representation of the test.
static lookupPassResultsUpdateTimestamp($active_id, $pass)
ilComponentFactory $component_factory
setTmpCopyWizardCopyId(int $tmpCopyWizardCopyId)
$participantDataExist
holds the fact wether participant data exists or not DO NOT USE TIS PROPERTY DRIRECTLY ALWAYS USE ilO...
getHighscoreScore()
Gets if the score column should be shown.
static isParticipantsLastPassActive(int $test_ref_id, int $user_id)
isBlockPassesAfterPassedEnabled()
getConcludingRemarksPageId()
ilComponentRepository $component_repository
static _lookupFinishedUserTests($a_user_id)
Gather all finished tests for user.
getAvailableQuestionpools(bool $use_object_id=false, ?bool $equal_points=false, bool $could_be_offline=false, bool $show_path=false, bool $with_questioncount=false, string $permission='read')
Returns the available question pools for the active user.
getImportMapping()
get array of (two) new created questions for import id
insertManualFeedback(int $active_id, int $question_id, int $pass, ?string $feedback, bool $finalized, array $feedback_old)
ScoreSettings $score_settings
secondsToHoursMinutesSecondsString(int $seconds)
getVisitingTimeOfParticipant(int $active_id)
getCompleteManualFeedback(int $question_id)
Retrieves the manual feedback for a question in a test.
getListOfQuestionsDescription()
ilTestParticipantList $access_filtered_participant_list
getActiveIdOfUser($user_id="", $anonymous_id="")
Gets the active id of a given user.
removeTestResultsByActiveIds(array $active_ids)
recalculateScores($preserve_manscoring=false)
TestManScoringDoneHelper $test_man_scoring_done_helper
qtiMaterialToArray($a_material)
Reads an QTI material tag and creates a text string.
moveQuestions(array $move_questions, int $target_index, int $insert_mode)
getGeneralQuestionPropertiesRepository()
static _getSolvedQuestions($active_id, $question_fi=null)
get solved questions
ParticipantRepository $participant_repository
getUsrPassOverviewEnabled()
getJavaScriptOutput()
Returns if Javascript should be chosen for drag & drop actions for the active user.
setQuestionSetType(string $question_set_type)
getTextAnswer($active_id, $question_id, $pass=null)
Returns the text answer of a given user for a given question.
userLookupFullName($user_id, $overwrite_anonymity=false, $sorted_order=false, $suffix="")
Returns the full name of a test user according to the anonymity status.
getParticipants()
Returns all persons who started the test.
getNrOfResultsForPass($active_id, $pass)
Calculates the number of user results for a specific test pass.
isShowGradingMarkEnabled()
addIntroductionToSettingsFromImport(SettingsIntroduction $settings, array $material, array $mappings)
int $activation_ending_time
getStartingTimeOfParticipants()
Note, this function should only be used if absolutely necessary, since it perform joins on tables tha...
getShowSolutionListOwnAnswers()
getAccessFilteredParticipantList()
removeQuestions(array $question_ids)
getQuestionCountAndPointsForPassOfParticipant(int $active_id, int $pass)
isTestFinishedToViewResults($active_id, $currentpass)
Returns true if an active user completed a test pass and did not start a new pass.
getMainSettingsRepository()
removeQuestionWithResults(int $question_id, TestScoring $scoring)
hasNrOfTriesRestriction()
returns if the numbers of tries have to be checked
clonePage(int $source_page_id)
TestLogViewer $log_viewer
getParticipantsForTestAndQuestion($test_id, $question_id)
Creates an associated array with all active id's for a given test and original question id.
getTitleFilenameCompliant()
returns the object title prepared to be used as a filename
questionMoveUp($question_id)
Moves a question up in order.
isForceInstantFeedbackEnabled()
& getCompleteWorkingTimeOfParticipants()
Returns the complete working time in seconds for all test participants.
static _lookupTestObjIdForQuestionId(int $q_id)
Get test Object ID for question ID.
exportPagesXML(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
export pages of test to xml (see ilias_co.dtd)
isScoreReportingEnabled()
static _lookupName(int $a_user_id)
static getInstanceByRefId(int $ref_id, bool $stop_on_error=true)
get an instance of an Ilias object by reference id
static getInstance(int $obj_id)
Class ilObject Basic functions for all objects.
static _prepareCloneSelection(array $ref_ids, string $new_type, bool $show_path=true)
Prepare copy wizard object selection.
Properties $object_properties
static _lookupObjId(int $ref_id)
static _lookupTitle(int $obj_id)
static _lookupDescription(int $obj_id)
static collectFileItems(ilPageObject $a_page, DOMDocument $a_domdoc)
Get all file items that are used within the page.
getPresentationMaterial()
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
static factory(string $package, int $timeout=0)
static get(string $a_var)
Skill management settings.
special template class to simplify handling of ITX/PEAR
Base Exception for all Exceptions relating to Modules/Test.
@ilCtrl_Calls ilTestPageGUI: ilPageEditorGUI, ilEditClipboardGUI, ilMDEditorGUI @ilCtrl_Calls ilTestP...
@ilCtrl_Calls ilTestEditPageGUI: ilPageEditorGUI, ilEditClipboardGUI, ilMDEditorGUI @ilCtrl_Calls ilT...
getUserIdByActiveId($activeId)
getLastFinishedPassTimestamp()
isQuestionSetConfigured()
static isManScoringDone(int $active_id)
static getStyleSheetLocation(string $mode="output", string $a_css_name="")
get full style sheet file name (path inclusive) of current user
static insertInstIntoID(string $a_value)
inserts installation id into ILIAS id
static _getObjectsByOperations( $a_obj_type, string $a_operation, int $a_usr_id=0, int $limit=0)
Get all objects of a specific type and check access This function is not recursive,...
static deliverData(string $a_data, string $a_filename, string $mime="application/octet-stream")
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
xmlElement(string $tag, $attrs=null, $data=null, $encode=true, $escape=true)
Writes a basic element (no children, just textual content)
xmlEndTag(string $tag)
Writes an endtag.
xmlStartTag(string $tag, ?array $attrs=null, bool $empty=false, bool $encode=true, bool $escape=true)
Writes a starttag.
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
The filesystem interface provides the public interface for the Filesystem service API consumer.
Readable part of repository interface to ilComponentDataDB.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Interface for html sanitizing functionality.
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
trait TestQuestionsImportTrait
TestAdministrationInteractionTypes
TestScoringInteractionTypes
Class ilObjForumAdministration.
if(!file_exists('../ilias.ini.php'))