19declare(strict_types=1);
155 public function __construct(
int $id = 0,
bool $a_call_by_reference =
true)
161 $this->
ctrl = $DIC[
'ilCtrl'];
163 $this->
settings = $DIC[
'ilSetting'];
164 $this->bench =
$DIC[
'ilBench'];
165 $this->component_repository =
$DIC[
'component.repository'];
166 $this->component_factory =
$DIC[
'component.factory'];
167 $this->filesystem_web =
$DIC->filesystem()->web();
168 $this->lo_metadata =
$DIC->learningObjectMetadata();
171 $this->participant_access_filter = $local_dic[
'participant.access_filter.factory'];
172 $this->test_man_scoring_done_helper = $local_dic[
'scoring.manual.done_helper'];
173 $this->
logger = $local_dic[
'logging.logger'];
174 $this->log_viewer = $local_dic[
'logging.viewer'];
175 $this->global_settings_repo = $local_dic[
'settings.global.repository'];
176 $this->marks_repository = $local_dic[
'marks.repository'];
177 $this->settings_factory = $local_dic[
'settings.factory'];
178 $this->questionrepository = $local_dic[
'question.general_properties.repository'];
179 $this->testrequest = $local_dic[
'request_data_collector'];
180 $this->participant_repository = $local_dic[
'participant.repository'];
181 $this->export_factory = $local_dic[
'exportimport.factory'];
182 $this->test_result_repository = $local_dic[
'results.data.repository'];
183 $this->main_settings_repository = $local_dic[
'settings.main.repository'];
184 $this->score_settings_repository = $local_dic[
'settings.scoring.repository'];
188 $this->
lng->loadLanguageModule(
"assessment");
189 $this->score_settings =
null;
196 $this->component_repository,
198 $this->questionrepository
204 return TestDIC::dic();
219 return $this->question_set_config_factory->getQuestionSetConfig();
242 $id = parent::create();
249 if (!parent::update()) {
261 $this->main_settings =
null;
262 $this->score_settings =
null;
263 $this->mark_schema =
null;
267 public function delete():
bool
270 if (!parent::delete()) {
281 $qsaImportFails->deleteRegisteredImportFails();
283 $sltImportFails->deleteRegisteredImportFails();
285 if ($this->
logger->isLoggingEnabled()) {
286 $this->
logger->logTestAdministrationInteraction(
287 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
289 $this->user->getId(),
292 AdditionalInformationGenerator::KEY_TEST_TITLE => $test_title = $this->title
304 $participantData->load($this->
getTestId());
307 $this->db->manipulateF(
308 "DELETE FROM tst_mark WHERE test_fi = %s",
313 $this->db->manipulateF(
314 "DELETE FROM tst_tests WHERE test_id = %s",
319 $this->db->manipulateF(
320 "DELETE FROM tst_test_settings WHERE id = %s",
326 $directory = $tst_data_dir .
"/tst_" . $this->
getId();
327 if (is_dir($directory)) {
335 foreach ($mobs as $mob) {
353 if (!is_writable($tst_data_dir)) {
354 $this->
ilias->raiseError(
"Test Data Directory (" . $tst_data_dir
355 .
") not writeable.", $this->
ilias->error_obj->MESSAGE);
359 $tst_dir = $tst_data_dir .
"/tst_" . $this->
getId();
361 if (!@is_dir($tst_dir)) {
362 $this->
ilias->raiseError(
"Creation of Test Directory failed.", $this->
ilias->error_obj->MESSAGE);
365 $export_dir = $tst_dir .
"/export";
367 if (!@is_dir($export_dir)) {
368 $this->
ilias->raiseError(
"Creation of Export Directory failed.", $this->
ilias->error_obj->MESSAGE);
378 public function getExportFiles(
string $dir =
''): array
381 if (!@is_dir($dir) || !is_writable($dir)) {
386 foreach (
new DirectoryIterator($dir) as $file) {
390 if ($file->isDir()) {
394 $files[] = $file->getBasename();
414 if (!is_writable($tst_data_dir)) {
416 .
") not writeable.",
$ilias->error_obj->FATAL);
420 $tst_dir = $tst_data_dir .
"/tst_import";
422 if (!@is_dir($tst_dir)) {
449 if ($this->
isComplete($test_question_set_config)) {
453 $this->db->manipulateF(
454 'UPDATE tst_tests SET complete = %s WHERE test_id = %s',
456 [$complete, $this->test_id]
461 public function saveToDb(
bool $properties_only =
false): void
463 if ($this->test_id === -1) {
465 $next_id = $this->db->nextId(
'tst_tests');
470 'test_id' => [
'integer', $next_id],
471 'obj_fi' => [
'integer', $this->
getId()],
472 'created' => [
'integer', time()],
473 'tstamp' => [
'integer', time()],
478 $this->test_id = $next_id;
481 $this->settings_factory->createDefaultMainSettings(),
489 $aresult = $this->db->queryF(
490 "SELECT active_id FROM tst_active WHERE test_fi = %s AND tries >= %s AND submitted = %s",
491 [
'integer',
'integer',
'integer'],
494 while ($row = $this->db->fetchAssoc($aresult)) {
495 $this->db->manipulateF(
496 "UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
497 [
'integer',
'timestamp',
'integer'],
498 [1, date(
'Y-m-d H:i:s'), $row[
"active_id"]]
503 $aresult = $this->db->queryF(
504 "SELECT active_id FROM tst_active WHERE test_fi = %s AND tries < %s AND submitted = %s",
505 [
'integer',
'integer',
'integer'],
508 while ($row = $this->db->fetchAssoc($aresult)) {
509 $this->db->manipulateF(
510 "UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
511 [
'integer',
'timestamp',
'integer'],
512 [0,
null, $row[
"active_id"]]
517 $aresult = $this->db->queryF(
518 "SELECT active_id FROM tst_active WHERE test_fi = %s AND submitted = %s",
519 [
'integer',
'integer'],
522 while ($row = $this->db->fetchAssoc($aresult)) {
523 $this->db->manipulateF(
524 "UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
525 [
'integer',
'timestamp',
'integer'],
526 [0,
null, $row[
"active_id"]]
533 if ($properties_only) {
541 $this->marks_repository->storeMarkSchema($this->
getMarkSchema());
546 $this->db->manipulateF(
547 'DELETE FROM tst_test_question WHERE test_fi = %s',
551 foreach ($this->questions as $key => $value) {
552 $next_id = $this->db->nextId(
'tst_test_question');
553 $this->db->insert(
'tst_test_question', [
554 'test_question_id' => [
'integer', $next_id],
555 'test_fi' => [
'integer', $this->
getTestId()],
556 'question_fi' => [
'integer', $value],
557 'sequence' => [
'integer', $key],
558 'tstamp' => [
'integer', time()]
571 foreach ($question_ids as
$id) {
572 $question = assQuestion::instantiateQuestionGUI(
$id);
574 $title = $question->getObject()->getTitle();
576 while (in_array(
$title .
' (' . $i .
')', $question_titles)) {
580 $title .=
' (' . $i .
')';
582 $question_titles[] =
$title;
584 $new_id = $question->getObject()->duplicate(
false,
$title);
586 $clone = assQuestion::instantiateQuestionGUI($new_id);
587 $question = $clone->getObject();
588 $question->setObjId($this->
getId());
589 $clone->setObject($question);
590 $clone->getObject()->saveToDb();
604 $result = $this->db->queryF(
605 "SELECT test_result_id FROM tst_test_result WHERE active_fi = %s AND pass = %s",
606 [
'integer',
'integer'],
609 return $result->numRows();
614 $result = $this->db->queryF(
615 "SELECT test_id FROM tst_tests WHERE obj_fi = %s",
619 if ($result->numRows() === 1) {
620 $data = $this->db->fetchObject($result);
632 $this->questions = [];
634 if ($active_id === 0) {
637 if (is_null($pass)) {
640 $result = $this->db->queryF(
641 'SELECT tst_test_rnd_qst.* '
642 .
'FROM tst_test_rnd_qst, qpl_questions '
643 .
'WHERE tst_test_rnd_qst.active_fi = %s '
644 .
'AND qpl_questions.question_id = tst_test_rnd_qst.question_fi '
645 .
'AND tst_test_rnd_qst.pass = %s '
646 .
'ORDER BY sequence',
647 [
'integer',
'integer'],
651 $result = $this->db->queryF(
652 'SELECT tst_test_question.* '
653 .
'FROM tst_test_question, qpl_questions '
654 .
'WHERE tst_test_question.test_fi = %s '
655 .
'AND qpl_questions.question_id = tst_test_question.question_fi '
656 .
'ORDER BY sequence',
662 if ($this->test_id !== -1) {
664 while (
$data = $this->db->fetchAssoc($result)) {
665 $this->questions[$index++] =
$data[
"question_fi"];
672 $page_id = $this->
getMainSettings()->getIntroductionSettings()->getIntroductionPageId();
673 return $page_id !==
null
680 $page_id = $this->
getMainSettings()->getIntroductionSettings()->getIntroductionPageId();
681 if ($page_id ===
null) {
689 $page_id = $this->
getMainSettings()->getFinishingSettings()->getConcludingRemarksPageId();
690 return $page_id !==
null
697 $page_id = $this->
getMainSettings()->getFinishingSettings()->getConcludingRemarksPageId();
698 if ($page_id ===
null) {
707 $page_object->setParentId($this->
getId());
708 $new_page_id = $page_object->createPageWithNextId();
709 (
new ilTestPage($source_page_id))->copy($new_page_id);
723 return $this->
getMainSettings()->getParticipantFunctionalitySettings()->getPostponedQuestionsMoveToEnd();
728 return $this->
getScoreSettings()->getResultSummarySettings()->getScoreReporting()->isReportingEnabled();
733 return $this->
getMainSettings()->getQuestionBehaviourSettings()->getInstantFeedbackPointsEnabled();
738 return $this->
getMainSettings()->getQuestionBehaviourSettings()->getInstantFeedbackGenericEnabled();
743 return $this->
getMainSettings()->getQuestionBehaviourSettings()->getInstantFeedbackSolutionEnabled();
756 return TestDIC::dic()[
'settings.scoring.repository']->getFor(
803 if ($this->mark_schema ===
null) {
804 $this->mark_schema = $this->marks_repository->getMarkSchemaFor($this->
getTestId());
813 $this->mark_schema =
null;
818 return $this->
getMainSettings()->getTestBehaviourSettings()->getNumberOfTries();
823 return $this->
getMainSettings()->getTestBehaviourSettings()->getBlockAfterPassedEnabled();
828 return $this->
getMainSettings()->getTestBehaviourSettings()->getKioskModeEnabled();
833 return $this->
getMainSettings()->getTestBehaviourSettings()->getShowTitleInKioskMode();
837 return $this->
getMainSettings()->getTestBehaviourSettings()->getShowParticipantNameInKioskMode();
842 return $this->
getMainSettings()->getParticipantFunctionalitySettings()->getUsePreviousAnswerAllowed();
847 return $this->
getMainSettings()->getQuestionBehaviourSettings()->getQuestionTitleOutputMode();
857 return $this->
getMainSettings()->getTestBehaviourSettings()->getProcessingTime();
862 $processing_time = $this->
getMainSettings()->getTestBehaviourSettings()->getProcessingTime();
863 if ($processing_time ===
null
864 || $processing_time ===
''
865 || !preg_match(
'/(\d{2}):(\d{2}):(\d{2})/is', $processing_time, $matches)
880 $processing_time = $this->
getMainSettings()->getTestBehaviourSettings()->getProcessingTime() ??
'';
881 if (preg_match(
"/(\d{2}):(\d{2}):(\d{2})/", (
string) $processing_time, $matches)) {
883 return ($matches[1] * 3600) + ($matches[2] * 60) + $matches[3] + $extratime;
891 return $this->
getMainSettings()->getTestBehaviourSettings()->getProcessingTimeEnabled();
896 return $this->
getMainSettings()->getTestBehaviourSettings()->getResetProcessingTime();
901 return $this->
getMainSettings()->getAccessSettings()->getStartTimeEnabled();
906 $start_time = $this->
getMainSettings()->getAccessSettings()->getStartTime();
907 return $start_time !==
null ? $start_time->getTimestamp() : 0;
912 return $this->
getMainSettings()->getAccessSettings()->getEndTimeEnabled();
917 $end_time = $this->
getMainSettings()->getAccessSettings()->getEndTime();
918 return $end_time !==
null ? $end_time->getTimestamp() : 0;
923 return $this->
getMainSettings()->getAccessSettings()->getPasswordEnabled();
937 $this->test_result_repository
951 $participant_data->load($this->test_id);
953 $question->removeAllExistingSolutions();
960 $participant_data->getActiveIds(),
961 $this->reindexFixedQuestionOrdering()
967 $question->delete($question_id);
976 if ($this->
logger->isLoggingEnabled()) {
977 $this->
logger->logTestAdministrationInteraction(
978 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
980 $this->user->getId(),
981 TestAdministrationInteractionTypes::QUESTION_REMOVED_IN_CORRECTIONS,
983 AdditionalInformationGenerator::KEY_QUESTION_TITLE => $question->getTitleForHTMLOutput(),
984 AdditionalInformationGenerator::KEY_QUESTION_TEXT => $question->getQuestion(),
985 AdditionalInformationGenerator::KEY_QUESTION_ID => $question->getId(),
986 AdditionalInformationGenerator::KEY_QUESTION_TYPE => $question->getQuestionType()
1004 $this->questionrepository
1007 foreach ($active_ids as $active_id) {
1009 $passSelector->setActiveId($active_id);
1011 foreach ($passSelector->getExistingPasses() as $pass) {
1012 $test_sequence = $test_sequence_factory->getSequenceByActiveIdAndPass($active_id, $pass);
1015 $test_sequence->removeQuestion($question_id, $reindexed_sequence_position_map);
1026 foreach ($question_ids as $question_id) {
1027 $this->removeQuestion((
int) $question_id);
1030 $this->reindexFixedQuestionOrdering();
1036 $question = self::_instanciateQuestion($question_id);
1037 $question_title = $question->getTitleForHTMLOutput();
1038 $question->delete($question_id);
1039 if ($this->
logger->isLoggingEnabled()) {
1040 $this->
logger->logTestAdministrationInteraction(
1041 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
1043 $this->user->getId(),
1044 TestAdministrationInteractionTypes::QUESTION_REMOVED,
1046 AdditionalInformationGenerator::KEY_QUESTION_TITLE => $question_title
1051 }
catch (InvalidArgumentException
$e) {
1052 $this->
logger->error($e->getMessage());
1053 $this->
logger->error($e->getTraceAsString());
1067 $this->removeTestResultsByUserIds($user_ids);
1070 $participantData->setUserIdsFilter($user_ids);
1071 $participantData->load($this->getTestId());
1073 $this->removeTestActives($participantData->getActiveIds());
1076 if ($this->
logger->isLoggingEnabled()) {
1077 $this->
logger->logTestAdministrationInteraction(
1078 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
1080 $this->user->getId(),
1081 TestAdministrationInteractionTypes::PARTICIPANT_DATA_REMOVED,
1083 AdditionalInformationGenerator::KEY_USERS => $participantData->getUserIds()
1095 $user_ids = array_map(
1099 $this->participant_repository->removeExtraTimeByUserId($this->getTestId(), $user_ids);
1102 if ($participant_data->
getUserIds() !== []) {
1105 if ($test_lp instanceof
ilTestLP) {
1106 $test_lp->setTestObject($this);
1107 $test_lp->resetLPDataForUserIds($participant_data->
getUserIds(),
false);
1110 $this->participant_repository->removeExtraTimeByUserId($this->getTestId(), $participant_data->
getUserIds());
1114 $this->removeTestActives($participant_data->
getActiveIds());
1116 $user_ids = array_map(
1120 $this->participant_repository->removeExtraTimeByUserId($this->getTestId(), $user_ids);
1123 if ($this->
logger->isLoggingEnabled()) {
1124 $this->
logger->logTestAdministrationInteraction(
1125 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
1127 $this->user->getId(),
1128 TestAdministrationInteractionTypes::PARTICIPANT_DATA_REMOVED,
1130 AdditionalInformationGenerator::KEY_USERS => $participant_data->
getUserIds(),
1141 $participantData->setUserIdsFilter($user_ids);
1142 $participantData->load($this->getTestId());
1144 $in_user_ids = $this->db->in(
'usr_id', $participantData->getUserIds(),
false,
'integer');
1145 $this->db->manipulateF(
1146 "DELETE FROM usr_pref WHERE {$in_user_ids} AND keyword = %s",
1148 [
'tst_password_' . $this->getTestId()]
1151 if ($participantData->getActiveIds() !== []) {
1152 $this->removeTestResultsByActiveIds($participantData->getActiveIds());
1158 $in_active_ids = $this->db->in(
'active_fi', $active_ids,
false,
'integer');
1160 $this->db->manipulate(
"DELETE FROM tst_solutions WHERE {$in_active_ids}");
1161 $this->db->manipulate(
"DELETE FROM tst_qst_solved WHERE {$in_active_ids}");
1162 $this->db->manipulate(
"DELETE FROM tst_test_result WHERE {$in_active_ids}");
1163 $this->db->manipulate(
"DELETE FROM tst_pass_result WHERE {$in_active_ids}");
1164 $this->db->manipulate(
"DELETE FROM tst_result_cache WHERE {$in_active_ids}");
1165 $this->db->manipulate(
"DELETE FROM tst_sequence WHERE {$in_active_ids}");
1166 $this->db->manipulate(
"DELETE FROM tst_times WHERE {$in_active_ids}");
1167 $this->db->manipulate(
1169 .
' WHERE ' . $this->db->in(
'active_id', $active_ids,
false,
'integer')
1172 if ($this->isRandomTest()) {
1173 $this->db->manipulate(
"DELETE FROM tst_test_rnd_qst WHERE {$in_active_ids}");
1176 $this->test_result_repository->removeTestResults($active_ids, $this->
getId());
1178 foreach ($active_ids as $active_id) {
1180 if (is_dir(
CLIENT_WEB_DIR .
"/assessment/tst_" . $this->getTestId() .
"/$active_id")) {
1191 $IN_activeIds = $this->db->in(
'active_id', $active_ids,
false,
'integer');
1192 $this->db->manipulate(
"DELETE FROM tst_active WHERE $IN_activeIds");
1205 $result = $this->db->queryF(
1206 "SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
1207 [
'integer',
'integer'],
1208 [$this->getTestId(), $question_id]
1210 $data = $this->db->fetchObject($result);
1211 if (
$data->sequence > 1) {
1213 $result = $this->db->queryF(
1214 "SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
1215 [
'integer',
'integer'],
1216 [$this->getTestId(),
$data->sequence - 1]
1218 $data_previous = $this->db->fetchObject($result);
1220 $this->db->manipulateF(
1221 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
1222 [
'integer',
'integer'],
1223 [
$data->sequence, $data_previous->test_question_id]
1226 $this->db->manipulateF(
1227 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
1228 [
'integer',
'integer'],
1229 [
$data->sequence - 1,
$data->test_question_id]
1232 $this->loadQuestions();
1244 $current_question_result = $this->db->queryF(
1245 "SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
1246 [
'integer',
'integer'],
1247 [$this->getTestId(), $question_id]
1249 $current_question_data = $this->db->fetchObject($current_question_result);
1250 $next_question_result = $this->db->queryF(
1251 "SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
1252 [
'integer',
'integer'],
1253 [$this->getTestId(), $current_question_data->sequence + 1]
1255 if ($this->db->numRows($next_question_result) === 1) {
1257 $next_question_data = $this->db->fetchObject($next_question_result);
1259 $this->db->manipulateF(
1260 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
1261 [
'integer',
'integer'],
1262 [$current_question_data->sequence, $next_question_data->test_question_id]
1265 $this->db->manipulateF(
1266 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
1267 [
'integer',
'integer'],
1268 [$current_question_data->sequence + 1, $current_question_data->test_question_id]
1271 $this->loadQuestions();
1283 $duplicate_id = $question->duplicate(
true,
'',
'', -1, $this->
getId());
1284 return $duplicate_id;
1290 $duplicate_id = $question_id;
1292 $duplicate_id = $this->duplicateQuestionForTest($question_id);
1296 $result = $this->db->queryF(
1297 "SELECT MAX(sequence) seq FROM tst_test_question WHERE test_fi=%s",
1299 [$this->getTestId()]
1303 if ($result->numRows() == 1) {
1304 $data = $this->db->fetchObject($result);
1305 $sequence =
$data->seq + 1;
1308 $next_id = $this->db->nextId(
'tst_test_question');
1309 $this->db->manipulateF(
1310 "INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
1311 [
'integer',
'integer',
'integer',
'integer',
'integer'],
1312 [$next_id, $this->getTestId(), $duplicate_id, $sequence, time()]
1315 $this->db->manipulateF(
1316 "DELETE FROM tst_active WHERE test_fi = %s",
1318 [$this->getTestId()]
1320 $this->loadQuestions();
1321 $this->saveCompleteStatus($this->question_set_config_factory->getQuestionSetConfig());
1323 if ($this->
logger->isLoggingEnabled()) {
1324 $this->
logger->logTestAdministrationInteraction(
1325 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
1327 $this->user->getId(),
1328 TestAdministrationInteractionTypes::QUESTION_ADDED,
1330 AdditionalInformationGenerator::KEY_QUESTION_ID => $question_id
1332 ->toLog($this->logger->getAdditionalInformationGenerator())
1337 return $duplicate_id;
1343 if ($this->getQuestionSetType() === self::QUESTION_SET_TYPE_FIXED) {
1344 $result = $this->db->queryF(
1345 'SELECT qpl_questions.title FROM tst_test_question, qpl_questions '
1346 .
'WHERE tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id '
1347 .
'ORDER BY tst_test_question.sequence',
1349 [$this->getTestId()]
1351 while ($row = $this->db->fetchAssoc($result)) {
1352 array_push($titles, $row[
'title']);
1368 if ($this->getQuestionSetType() == self::QUESTION_SET_TYPE_FIXED) {
1369 $result = $this->db->queryF(
1370 'SELECT qpl_questions.title, qpl_questions.question_id '
1371 .
'FROM tst_test_question, qpl_questions '
1372 .
'WHERE tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id '
1373 .
'ORDER BY tst_test_question.sequence',
1375 [$this->getTestId()]
1377 while ($row = $this->db->fetchAssoc($result)) {
1378 $titles[$row[
'question_id']] = $row[
"title"];
1396 switch ($this->getTitleOutput()) {
1403 return $this->
lng->txt(
"ass_question") .
' ' . $nr;
1405 return $this->
lng->txt(
"ass_question");
1409 $txt = $this->
lng->txt(
"ass_question") .
' ' . $nr;
1411 $txt = $this->
lng->txt(
"ass_question");
1413 if ($points !=
'') {
1414 $lngv = $this->
lng->txt(
'points');
1416 $lngv = $this->
lng->txt(
'point');
1418 $txt .=
' - ' . $points .
' ' . $lngv;
1424 return $this->
lng->txt(
"ass_question");
1438 $result = $this->db->queryF(
1439 "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",
1443 $row = $this->db->fetchObject($result);
1455 $existing_questions = [];
1456 $active_id = $this->getActiveIdOfUser($this->
user->getId());
1457 if ($this->isRandomTest()) {
1458 if (is_null($pass)) {
1461 $result = $this->db->queryF(
1462 "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",
1463 [
'integer',
'integer'],
1467 $result = $this->db->queryF(
1468 "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",
1470 [$this->getTestId()]
1473 while (
$data = $this->db->fetchObject($result)) {
1474 if (
$data->original_id ===
null) {
1478 array_push($existing_questions,
$data->original_id);
1480 return $existing_questions;
1492 if ($question_id < 1) {
1495 $result = $this->db->queryF(
1496 "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",
1500 if ($result->numRows() == 1) {
1501 $data = $this->db->fetchObject($result);
1502 return $data->type_tag;
1516 $next_id = $this->db->nextId(
'tst_times');
1517 $affectedRows = $this->db->manipulateF(
1518 "INSERT INTO tst_times (times_id, active_fi, started, finished, pass, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
1519 [
'integer',
'integer',
'timestamp',
'timestamp',
'integer',
'integer'],
1520 [$next_id, $active_id, date(
"Y-m-d H:i:s"), date(
"Y-m-d H:i:s"), $pass, time()]
1533 $affectedRows = $this->db->manipulateF(
1534 "UPDATE tst_times SET finished = %s, tstamp = %s WHERE times_id = %s",
1535 [
'timestamp',
'integer',
'integer'],
1536 [date(
'Y-m-d H:i:s'), time(), $times_id]
1548 if (is_null($pass)) {
1549 $result = $this->db->queryF(
1550 "SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
1551 [
'integer',
'integer'],
1555 $result = $this->db->queryF(
1556 "SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
1557 [
'integer',
'integer'],
1562 while ($row = $this->db->fetchAssoc($result)) {
1563 array_push($result_array, $row[
"question_fi"]);
1565 return $result_array;
1579 return ((($currentpass > 0) && ($num == 0)) || $this->isTestFinished($active_id)) ?
true :
false;
1590 if ($this->isRandomTest()) {
1591 $active_id = $this->getActiveIdOfUser($this->
user->getId());
1592 if ($active_id ===
null) {
1595 $this->loadQuestions($active_id, $pass);
1596 if (count($this->questions) === 0) {
1599 if (is_null($pass)) {
1600 $pass = self::_getPass($active_id);
1602 $result = $this->db->queryF(
1603 "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'),
1604 [
'integer',
'integer'],
1608 if (count($this->questions) === 0) {
1611 $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'));
1614 while ($row = $this->db->fetchAssoc($result)) {
1615 $result_array[$row[
"question_id"]] = $row;
1617 return $result_array;
1635 if (is_array($tst_access_code) &&
1637 isset($tst_access_code[$this->getTestId()]) &&
1638 $tst_access_code[$this->getTestId()] !==
'') {
1639 $result = $this->db->queryF(
1640 'SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s',
1641 [
'integer',
'integer',
'text'],
1642 [
$user_id, $this->test_id, $tst_access_code[$this->getTestId()]]
1644 } elseif ((
string) $anonymous_id !==
'') {
1645 $result = $this->db->queryF(
1646 'SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s',
1647 [
'integer',
'integer',
'text'],
1648 [
$user_id, $this->test_id, $anonymous_id]
1654 $result = $this->db->queryF(
1655 'SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s',
1656 [
'integer',
'integer'],
1661 if ($result->numRows()) {
1662 $row = $this->db->fetchAssoc($result);
1663 return (
int) $row[
'active_id'];
1673 $ilUser =
$DIC[
'ilUser'];
1681 $result =
$ilDB->queryF(
1682 "SELECT tst_active.active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
1683 [
'integer',
'integer'],
1686 if ($result->numRows()) {
1687 $row =
$ilDB->fetchAssoc($result);
1688 return $row[
"active_id"];
1702 $keys = array_keys($array);
1705 foreach ($keys as $key) {
1706 $result[$key] = $array[$key];
1719 ?
int $attempt =
null,
1720 bool $ordered_sequence =
false,
1721 bool $consider_hidden_questions =
true,
1722 bool $consider_optional_questions =
true
1724 $test_result = $this->test_result_repository->getTestResult($active_id);
1726 if ($test_result ===
null) {
1727 $test_result = $this->test_result_repository->updateTestResultCache($active_id);
1730 if ($attempt ===
null) {
1731 $attempt = $test_result->getAttempt();
1735 $test_sequence = $test_sequence_factory->getSequenceByActiveIdAndPass($active_id, $attempt);
1737 $test_sequence->setConsiderHiddenQuestionsEnabled($consider_hidden_questions);
1738 $test_sequence->setConsiderOptionalQuestionsEnabled($consider_optional_questions);
1740 $test_sequence->loadFromDb();
1741 $test_sequence->loadQuestions();
1743 if ($ordered_sequence) {
1744 $sequence = $test_sequence->getOrderedSequenceQuestions();
1746 $sequence = $test_sequence->getUserSequenceQuestions();
1753 tst_test_result.question_fi,
1754 tst_test_result.points reached,
1755 tst_test_result.answered answered,
1756 tst_manual_fb.finalized_evaluation finalized_evaluation
1758 FROM tst_test_result
1760 LEFT JOIN tst_solutions
1761 ON tst_solutions.active_fi = tst_test_result.active_fi
1762 AND tst_solutions.question_fi = tst_test_result.question_fi
1764 LEFT JOIN tst_manual_fb
1765 ON tst_test_result.active_fi = tst_manual_fb.active_fi
1766 AND tst_test_result.question_fi = tst_manual_fb.question_fi
1768 WHERE tst_test_result.active_fi = %s
1769 AND tst_test_result.pass = %s
1772 $solutionresult = $this->db->queryF(
1774 [
'integer',
'integer'],
1775 [$active_id, $attempt]
1778 while ($row = $this->db->fetchAssoc($solutionresult)) {
1779 $arr_results[ $row[
'question_fi'] ] = $row;
1782 $result = $this->db->query(
1783 'SELECT qpl_questions.*, qpl_qst_type.type_tag, qpl_sol_sug.question_fi has_sug_sol' . PHP_EOL
1784 .
'FROM qpl_qst_type, qpl_questions' . PHP_EOL
1785 .
'LEFT JOIN qpl_sol_sug' . PHP_EOL
1786 .
'ON qpl_sol_sug.question_fi = qpl_questions.question_id' . PHP_EOL
1787 .
'WHERE qpl_qst_type.question_type_id = qpl_questions.question_type_fi' . PHP_EOL
1788 .
'AND ' . $this->db->in(
'qpl_questions.question_id', $sequence,
false,
'integer')
1793 while ($row = $this->db->fetchAssoc($result)) {
1794 if (!isset($arr_results[ $row[
'question_id'] ])) {
1795 $percentvalue = 0.0;
1798 $row[
'points'] ? $arr_results[$row[
'question_id']][
'reached'] / $row[
'points'] : 0
1801 if ($percentvalue < 0) {
1802 $percentvalue = 0.0;
1808 'max' => round($row[
'points'], 2),
1809 'reached' => round($arr_results[$row[
'question_id']][
'reached'] ?? 0, 2),
1810 'percent' => sprintf(
'%2.2f ', ($percentvalue) * 100) .
'%',
1812 'type' => $row[
'type_tag'],
1813 'qid' => $row[
'question_id'],
1814 'original_id' => $row[
'original_id'],
1815 'workedthrough' => isset($arr_results[$row[
'question_id']]) ? 1 : 0,
1816 'answered' => $arr_results[$row[
'question_id']][
'answered'] ?? 0,
1817 'finalized_evaluation' => $arr_results[$row[
'question_id']][
'finalized_evaluation'] ?? 0,
1820 $unordered[ $row[
'question_id'] ] =
$data;
1828 foreach ($sequence as $qid) {
1831 $pass_max += round($unordered[$qid][
'max'], 2);
1832 $pass_reached += round($unordered[$qid][
'reached'], 2);
1833 $found[] = $unordered[$qid];
1836 if ($this->getScoreCutting() == 1) {
1837 if ($pass_reached < 0) {
1842 $found[
'pass'][
'total_max_points'] = $pass_max;
1843 $found[
'pass'][
'total_reached_points'] = $pass_reached;
1844 $found[
'pass'][
'percent'] = ($pass_max > 0) ? $pass_reached / $pass_max : 0;
1845 $found[
'pass'][
'num_workedthrough'] = count($arr_results);
1846 $found[
'pass'][
'num_questions_total'] = count($unordered);
1848 $found[
'test'][
'total_max_points'] = $test_result->getMaxPoints();
1849 $found[
'test'][
'total_reached_points'] = $test_result->getReachedPoints();
1850 $found[
'test'][
'result_pass'] = $attempt;
1851 $found[
'test'][
'result_tstamp'] = $test_result->getTimestamp();
1852 $found[
'test'][
'passed'] = $test_result->isPassed();
1865 $result = $this->db->queryF(
1866 'SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s',
1868 [$this->getTestId()]
1870 $row = $this->db->fetchAssoc($result);
1871 return $row[
'total'];
1882 $result = $this->db->queryF(
1883 "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",
1884 [
'integer',
'integer'],
1888 while ($row = $this->db->fetchAssoc($result)) {
1889 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"started"], $matches);
1898 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"finished"], $matches);
1907 $time += ($epoch_2 - $epoch_1);
1920 return $this->_getCompleteWorkingTimeOfParticipants($this->getTestId());
1932 $result = $this->db->queryF(
1933 "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",
1939 while ($row = $this->db->fetchAssoc($result)) {
1940 if (!array_key_exists($row[
"active_fi"], $times)) {
1941 $times[$row[
"active_fi"]] = 0;
1943 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"started"], $matches);
1952 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"finished"], $matches);
1961 $times[$row[
"active_fi"]] += ($epoch_2 - $epoch_1);
1974 $result = $this->db->queryF(
1975 "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",
1976 [
'integer',
'integer'],
1977 [$this->getTestId(), $active_id]
1980 while ($row = $this->db->fetchAssoc($result)) {
1981 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"started"], $matches);
1990 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"finished"], $matches);
1999 $time += ($epoch_2 - $epoch_1);
2009 return $this->test_result_repository->fetchWorkingTime($active_id, $pass);
2018 $test_result = &$this->getTestResult($active_id, $pass);
2019 $result = $this->db->queryF(
2020 "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.active_id = %s AND tst_active.active_id = tst_times.active_fi",
2027 while ($row = $this->db->fetchObject($result)) {
2028 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
2037 if (!$first_visit) {
2038 $first_visit = $epoch_1;
2040 if ($epoch_1 < $first_visit) {
2041 $first_visit = $epoch_1;
2043 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
2053 $last_visit = $epoch_2;
2055 if ($epoch_2 > $last_visit) {
2056 $last_visit = $epoch_2;
2058 $times[$row->active_fi] += ($epoch_2 - $epoch_1);
2061 foreach ($times as $key => $value) {
2062 $max_time += $value;
2064 if ((!$test_result[
"test"][
"total_reached_points"]) or (!$test_result[
"test"][
"total_max_points"])) {
2067 $percentage = ($test_result[
"test"][
"total_reached_points"] / $test_result[
"test"][
"total_max_points"]) * 100.0;
2068 if ($percentage < 0) {
2072 $mark_obj = $this->getMarkSchema()->getMatchingMark($percentage);
2073 $first_date = getdate($first_visit);
2074 $last_date = getdate($last_visit);
2075 $qworkedthrough = 0;
2076 foreach ($test_result as $key => $value) {
2077 if (preg_match(
"/\d+/", $key)) {
2078 $qworkedthrough += $value[
"workedthrough"];
2081 if (!$qworkedthrough) {
2084 $atimeofwork = $max_time / $qworkedthrough;
2090 if ($mark_obj !==
null) {
2091 $result_mark = $mark_obj->getShortName();
2093 if ($mark_obj->getPassed()) {
2099 $percent_worked_through = 0;
2100 if (count($this->questions)) {
2101 $percent_worked_through = $qworkedthrough / count($this->questions);
2104 "qworkedthrough" => $qworkedthrough,
2105 "qmax" => count($this->questions),
2106 "pworkedthrough" => $percent_worked_through,
2107 "timeofwork" => $max_time,
2108 "atimeofwork" => $atimeofwork,
2109 "firstvisit" => $first_date,
2110 "lastvisit" => $last_date,
2111 "resultspoints" => $test_result[
"test"][
"total_reached_points"],
2112 "maxpoints" => $test_result[
"test"][
"total_max_points"],
2113 "resultsmarks" => $result_mark,
2114 "passed" => $passed,
2115 "distancemedian" =>
"0"
2117 foreach ($test_result as $key => $value) {
2118 if (preg_match(
"/\d+/", $key)) {
2119 $result_array[$key] = $value;
2122 return $result_array;
2134 $totalpoints_array = [];
2135 $all_users = $this->evalTotalParticipantsArray();
2136 foreach ($all_users as $active_id => $user_name) {
2137 $test_result = &$this->getTestResult($active_id);
2138 $reached = $test_result[
"test"][
"total_reached_points"];
2139 $total = $test_result[
"test"][
"total_max_points"];
2140 $percentage = $total != 0 ? $reached / $total : 0;
2141 $mark = $this->getMarkSchema()->getMatchingMark($percentage * 100.0);
2143 if ($mark !==
null && $mark->getPassed()) {
2144 array_push($totalpoints_array, $test_result[
"test"][
"total_reached_points"]);
2147 return $totalpoints_array;
2157 $result = $this->db->queryF(
2158 "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",
2160 [$this->getTestId()]
2162 $persons_array = [];
2163 while ($row = $this->db->fetchAssoc($result)) {
2164 $name = $this->
lng->txt(
"anonymous");
2165 $fullname = $this->
lng->txt(
"anonymous");
2167 if (!$this->getAnonymity()) {
2168 if (strlen($row[
"firstname"] . $row[
"lastname"] . $row[
"title"]) == 0) {
2169 $name = $this->
lng->txt(
"deleted_user");
2170 $fullname = $this->
lng->txt(
"deleted_user");
2171 $login = $this->
lng->txt(
"unknown");
2173 $login = $row[
"login"];
2175 $name = $this->
lng->txt(
"anonymous");
2176 $fullname = $this->
lng->txt(
"anonymous");
2178 $name = trim($row[
"lastname"] .
", " . $row[
"firstname"] .
" " . $row[
"title"]);
2179 $fullname = trim($row[
"title"] .
" " . $row[
"firstname"] .
" " . $row[
"lastname"]);
2183 $persons_array[$row[
"active_id"]] = [
2185 "fullname" => $fullname,
2189 return $persons_array;
2194 $result = $this->db->queryF(
2195 "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),
2197 [$this->getTestId()]
2199 $persons_array = [];
2200 while ($row = $this->db->fetchAssoc($result)) {
2201 if ($this->getAccessFilteredParticipantList() && !$this->getAccessFilteredParticipantList()->isActiveIdInList($row[
"active_id"])) {
2205 if ($this->getAnonymity()) {
2206 $persons_array[$row[
"active_id"]] = $this->
lng->txt(
"anonymous");
2208 if (strlen($row[
"firstname"] . $row[
"lastname"] . $row[
"title"]) == 0) {
2209 $persons_array[$row[
"active_id"]] = $this->
lng->txt(
"deleted_user");
2212 $persons_array[$row[
"active_id"]] = $row[
"lastname"];
2214 $persons_array[$row[
"active_id"]] = trim($row[
"lastname"] .
", " . $row[
"firstname"] .
" " . $row[
"title"]);
2219 return $persons_array;
2224 $result = $this->db->queryF(
2225 'SELECT tst_active.user_fi, tst_active.active_id, usr_data.login, '
2226 .
'usr_data.firstname, usr_data.lastname, usr_data.title FROM tst_active '
2227 .
'LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id '
2228 .
'WHERE tst_active.test_fi = %s '
2229 .
'ORDER BY usr_data.lastname ' . strtoupper($name_sort_order),
2231 [$this->getTestId()]
2233 $persons_array = [];
2234 while ($row = $this->db->fetchAssoc($result)) {
2235 if ($this->getAnonymity()) {
2236 $persons_array[$row[
'active_id']] = [
'name' => $this->
lng->txt(
"anonymous")];
2238 if (strlen($row[
'firstname'] . $row[
'lastname'] . $row[
"title"]) == 0) {
2239 $persons_array[$row[
'active_id']] = [
'name' => $this->
lng->txt(
'deleted_user')];
2242 $persons_array[$row[
'active_id']] = [
'name' => $row[
'lastname']];
2244 $persons_array[$row[
'active_id']] = [
2245 'name' => trim($row[
'lastname'] .
', ' . $row[
'firstname']
2246 .
' ' . $row[
'title']),
2247 'login' => $row[
'login']
2253 return $persons_array;
2258 if ($this->isRandomTest()) {
2259 $this->db->setLimit($this->getQuestionCount(), 0);
2260 $result = $this->db->queryF(
2261 'SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, '
2262 .
'tst_test_rnd_qst.pass, qpl_questions.points '
2263 .
'FROM tst_test_rnd_qst, qpl_questions '
2264 .
'WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id '
2265 .
'AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence',
2270 $result = $this->db->queryF(
2271 'SELECT tst_test_question.sequence, tst_test_question.question_fi, '
2272 .
'qpl_questions.points '
2273 .
'FROM tst_test_question, tst_active, qpl_questions '
2274 .
'WHERE tst_test_question.question_fi = qpl_questions.question_id '
2275 .
'AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi',
2281 if ($result->numRows()) {
2282 while ($row = $this->db->fetchAssoc($result)) {
2283 array_push($qtest, $row);
2291 if ($this->isRandomTest()) {
2292 $this->db->setLimit($this->getQuestionCount(), 0);
2293 $result = $this->db->queryF(
2294 'SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, '
2295 .
'qpl_questions.points '
2296 .
'FROM tst_test_rnd_qst, qpl_questions '
2297 .
'WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id '
2298 .
'AND tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.pass = %s '
2299 .
'ORDER BY tst_test_rnd_qst.sequence',
2300 [
'integer',
'integer'],
2304 $result = $this->db->queryF(
2305 'SELECT tst_test_question.sequence, tst_test_question.question_fi, '
2306 .
'qpl_questions.points '
2307 .
'FROM tst_test_question, tst_active, qpl_questions '
2308 .
'WHERE tst_test_question.question_fi = qpl_questions.question_id '
2309 .
'AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi',
2315 if ($result->numRows()) {
2316 while ($row = $this->db->fetchAssoc($result)) {
2317 array_push($qpass, $row);
2325 return $this->access_filtered_participant_list;
2330 $this->access_filtered_participant_list = $access_filtered_participant_list;
2336 $list->initializeFromDbRows($this->getTestParticipants());
2338 return $list->getAccessFilteredList(
2339 $this->participant_access_filter->getAccessStatisticsUserFilter($this->getRefId())
2349 $list->initializeFromDbRows($this->getTestParticipants());
2350 if ($this->getAnonymity()) {
2351 return $list->getAllUserIds();
2353 return $list->getAccessFilteredList(
2354 $this->participant_access_filter->getAnonOnlyParticipantsUserFilter($this->getRefId())
2361 ->getEvaluationData();
2366 $question_set_type = $this->lookupQuestionSetTypeByActiveId($active_id);
2368 switch ($question_set_type) {
2370 $res = $this->db->queryF(
2372 SELECT tst_test_rnd_qst.pass,
2373 COUNT(tst_test_rnd_qst.question_fi) qcount,
2374 SUM(qpl_questions.points) qsum
2376 FROM tst_test_rnd_qst,
2379 WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id
2380 AND tst_test_rnd_qst.active_fi = %s
2383 GROUP BY tst_test_rnd_qst.active_fi,
2384 tst_test_rnd_qst.pass
2386 [
'integer',
'integer'],
2392 $res = $this->db->queryF(
2394 SELECT COUNT(tst_test_question.question_fi) qcount,
2395 SUM(qpl_questions.points) qsum
2397 FROM tst_test_question,
2401 WHERE tst_test_question.question_fi = qpl_questions.question_id
2402 AND tst_test_question.test_fi = tst_active.test_fi
2403 AND tst_active.active_id = %s
2405 GROUP BY tst_test_question.test_fi
2413 throw new ilTestException(
"not supported question set type: $question_set_type");
2416 $row = $this->db->fetchAssoc(
$res);
2418 if (is_array($row)) {
2419 return [
"count" => $row[
"qcount"],
"points" => $row[
"qsum"]];
2422 return [
"count" => 0,
"points" => 0];
2427 $data = $this->getUnfilteredEvaluationData();
2428 $data->setFilter($filterby, $filtertext);
2449 || $firstname . $lastname ===
'') {
2450 return $this->
lng->txt(
'deleted_user');
2453 if ($this->getAnonymity()) {
2454 return $this->
lng->txt(
'anonymous');
2461 return trim($lastname .
', ' . $firstname);
2466 $query =
"SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi";
2468 if ($active_ids_to_filter !==
null && $active_ids_to_filter !== []) {
2469 $query .=
" AND " . $this->db->in(
'active_id', $active_ids_to_filter,
false,
'integer');
2472 $result = $this->db->queryF($query, [
'integer'], [$this->getTestId()]);
2474 while ($row = $this->db->fetchObject($result)) {
2475 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
2484 preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
2493 if (isset($times[$row->active_fi])) {
2494 $times[$row->active_fi] += ($epoch_2 - $epoch_1);
2496 $times[$row->active_fi] = ($epoch_2 - $epoch_1);
2501 foreach ($times as $value) {
2502 $max_time += $value;
2508 return (
int) round($max_time /
$counter);
2518 bool $use_object_id =
false,
2519 ?
bool $equal_points =
false,
2520 bool $could_be_offline =
false,
2521 bool $show_path =
false,
2522 bool $with_questioncount =
false,
2523 string $permission =
'read'
2527 $equal_points ?? false,
2530 $with_questioncount,
2572 if ((!$question_type) and ($question_id > 0)) {
2573 $question_type = $this->getQuestionType($question_id);
2576 if (!strlen($question_type)) {
2580 if ($question_id > 0) {
2581 $question_gui = assQuestion::instantiateQuestionGUI($question_id);
2583 $question_type_gui = $question_type .
'GUI';
2584 $question_gui =
new $question_type_gui();
2587 return $question_gui;
2598 if (strcmp((
string) $question_id,
"") !== 0) {
2609 public function moveQuestions(array $move_questions,
int $target_index,
int $insert_mode): void
2611 $this->questions = array_values($this->questions);
2612 $array_pos = array_search($target_index, $this->questions);
2613 if ($insert_mode == 0) {
2614 $part1 = array_slice($this->questions, 0, $array_pos);
2615 $part2 = array_slice($this->questions, $array_pos);
2616 } elseif ($insert_mode == 1) {
2617 $part1 = array_slice($this->questions, 0, $array_pos + 1);
2618 $part2 = array_slice($this->questions, $array_pos + 1);
2620 foreach ($move_questions as $question_id) {
2621 if (!(array_search($question_id, $part1) ===
false)) {
2622 unset($part1[array_search($question_id, $part1)]);
2624 if (!(array_search($question_id, $part2) ===
false)) {
2625 unset($part2[array_search($question_id, $part2)]);
2628 $part1 = array_values($part1);
2629 $part2 = array_values($part2);
2630 $new_array = array_values(array_merge($part1, $move_questions, $part2));
2631 $this->questions = [];
2633 foreach ($new_array as $question_id) {
2634 $this->questions[
$counter] = $question_id;
2637 $this->saveQuestionsToDb();
2639 if ($this->
logger->isLoggingEnabled()) {
2640 $this->
logger->logTestAdministrationInteraction(
2641 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
2643 $this->user->getId(),
2644 TestAdministrationInteractionTypes::QUESTION_MOVED,
2646 AdditionalInformationGenerator::KEY_QUESTION_ORDER => $this->questions
2663 if ($this->isStartingTimeEnabled() && $this->getStartingTime() != 0) {
2665 if ($now < $this->getStartingTime()) {
2681 if ($this->isEndingTimeEnabled() && $this->getEndingTime() != 0) {
2683 if ($now > $this->getEndingTime()) {
2699 if (count($available_pools)) {
2700 $available =
" AND " . $this->db->in(
'qpl_questions.obj_fi', $available_pools,
false,
'integer');
2704 if ($completeonly) {
2705 $available .=
" AND qpl_questions.complete = " . $this->db->quote(
"1",
'text');
2709 if (is_array($arr_filter)) {
2710 if (array_key_exists(
'title', $arr_filter) && strlen($arr_filter[
'title'])) {
2711 $where .=
" AND " . $this->db->like(
'qpl_questions.title',
'text',
"%%" . $arr_filter[
'title'] .
"%%");
2713 if (array_key_exists(
'description', $arr_filter) && strlen($arr_filter[
'description'])) {
2714 $where .=
" AND " . $this->db->like(
'qpl_questions.description',
'text',
"%%" . $arr_filter[
'description'] .
"%%");
2716 if (array_key_exists(
'author', $arr_filter) && strlen($arr_filter[
'author'])) {
2717 $where .=
" AND " . $this->db->like(
'qpl_questions.author',
'text',
"%%" . $arr_filter[
'author'] .
"%%");
2719 if (array_key_exists(
'type', $arr_filter) && strlen($arr_filter[
'type'])) {
2720 $where .=
" AND qpl_qst_type.type_tag = " . $this->db->quote($arr_filter[
'type'],
'text');
2722 if (array_key_exists(
'qpl', $arr_filter) && strlen($arr_filter[
'qpl'])) {
2723 $where .=
" AND " . $this->db->like(
'object_data.title',
'text',
"%%" . $arr_filter[
'qpl'] .
"%%");
2727 $original_ids = &$this->getExistingQuestions();
2728 $original_clause =
" qpl_questions.original_id IS NULL";
2729 if (count($original_ids)) {
2730 $original_clause =
" qpl_questions.original_id IS NULL AND " . $this->db->in(
'qpl_questions.question_id', $original_ids,
true,
'integer');
2733 $query_result = $this->db->query(
"
2734 SELECT qpl_questions.*, qpl_questions.tstamp,
2735 qpl_qst_type.type_tag, qpl_qst_type.plugin, qpl_qst_type.plugin_name,
2736 object_data.title parent_title
2737 FROM qpl_questions, qpl_qst_type, object_data
2738 WHERE $original_clause $available
2739 AND object_data.obj_id = qpl_questions.obj_fi
2740 AND qpl_questions.tstamp > 0
2741 AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id
2746 if ($query_result->numRows()) {
2747 while ($row = $this->db->fetchAssoc($query_result)) {
2750 if (!$row[
'plugin']) {
2751 $row[
'ttype' ] = $this->
lng->txt($row[
"type_tag" ]);
2757 $plugin = $this->component_repository->getPluginByName($row[
'plugin_name']);
2762 $pl = $this->component_factory->getPlugin(
$plugin->getId());
2763 $row[
'ttype' ] = $pl->getQuestionTypeTranslation();
2777 $this->saveToDb(
true);
2779 $main_settings = $this->getMainSettings();
2789 $introduction_settings = $introduction_settings->withIntroductionEnabled(
false);
2790 foreach ($assessment->objectives as
$objectives) {
2792 $introduction_settings = $this->addIntroductionToSettingsFromImport(
2793 $introduction_settings,
2794 $this->qtiMaterialToArray($material),
2803 $finishing_settings = $this->addConcludingRemarksToSettingsFromImport(
2804 $finishing_settings,
2805 $this->qtiMaterialToArray(
2812 $score_settings = $this->getScoreSettings();
2819 foreach ($assessment->qtimetadata as $metadata) {
2820 switch ($metadata[
"label"]) {
2821 case "solution_details":
2822 $result_details_settings = $result_details_settings->withShowPassDetails((
bool) $metadata[
"entry"]);
2824 case "show_solution_list_comparison":
2825 $result_details_settings = $result_details_settings->withShowSolutionListComparison((
bool) $metadata[
"entry"]);
2827 case "print_bs_with_res":
2828 $result_details_settings = $result_details_settings->withShowSolutionListComparison((
bool) $metadata[
"entry"]);
2831 $this->saveAuthorToMetadata($metadata[
"entry"]);
2834 $test_behaviour_settings = $test_behaviour_settings->withNumberOfTries((
int) $metadata[
"entry"]);
2836 case 'block_after_passed':
2837 $test_behaviour_settings = $test_behaviour_settings->withBlockAfterPassedEnabled((
bool) $metadata[
'entry']);
2839 case "pass_waiting":
2840 $test_behaviour_settings = $test_behaviour_settings->withPassWaiting($metadata[
"entry"]);
2843 $test_behaviour_settings = $test_behaviour_settings->withKioskMode((
int) $metadata[
"entry"]);
2845 case 'show_introduction':
2846 $introduction_settings = $introduction_settings->withIntroductionEnabled((
bool) $metadata[
'entry']);
2848 case "showfinalstatement":
2849 case 'show_concluding_remarks':
2850 $finishing_settings = $finishing_settings->withConcludingRemarksEnabled((
bool) $metadata[
"entry"]);
2852 case 'exam_conditions':
2853 $introduction_settings = $introduction_settings->withExamConditionsCheckboxEnabled($metadata[
'entry'] ===
'1');
2855 case "highscore_enabled":
2856 $gamification_settings = $gamification_settings->withHighscoreEnabled((
bool) $metadata[
"entry"]);
2858 case "highscore_anon":
2859 $gamification_settings = $gamification_settings->withHighscoreAnon((
bool) $metadata[
"entry"]);
2861 case "highscore_achieved_ts":
2862 $gamification_settings = $gamification_settings->withHighscoreAchievedTS((
bool) $metadata[
"entry"]);
2864 case "highscore_score":
2865 $gamification_settings = $gamification_settings->withHighscoreScore((
bool) $metadata[
"entry"]);
2867 case "highscore_percentage":
2868 $gamification_settings = $gamification_settings->withHighscorePercentage((
bool) $metadata[
"entry"]);
2870 case "highscore_wtime":
2871 $gamification_settings = $gamification_settings->withHighscoreWTime((
bool) $metadata[
"entry"]);
2873 case "highscore_own_table":
2874 $gamification_settings = $gamification_settings->withHighscoreOwnTable((
bool) $metadata[
"entry"]);
2876 case "highscore_top_table":
2877 $gamification_settings = $gamification_settings->withHighscoreTopTable((
bool) $metadata[
"entry"]);
2879 case "highscore_top_num":
2880 $gamification_settings = $gamification_settings->withHighscoreTopNum((
int) $metadata[
"entry"]);
2882 case "use_previous_answers":
2883 $participant_functionality_settings = $participant_functionality_settings->withUsePreviousAnswerAllowed((
bool) $metadata[
"entry"]);
2885 case 'question_list_enabled':
2886 $participant_functionality_settings = $participant_functionality_settings->withQuestionListEnabled((
bool) $metadata[
'entry']);
2888 case "title_output":
2889 $question_behaviour_settings = $question_behaviour_settings->withQuestionTitleOutputMode((
int) $metadata[
"entry"]);
2891 case "question_set_type":
2892 if ($metadata[
'entry'] === self::QUESTION_SET_TYPE_RANDOM) {
2893 $this->questions = [];
2895 $general_settings = $general_settings->withQuestionSetType($metadata[
"entry"]);
2898 $general_settings = $general_settings->withAnonymity((
bool) $metadata[
"entry"]);
2900 case "results_presentation":
2901 $result_details_settings = $result_details_settings->withResultsPresentation((
int) $metadata[
"entry"]);
2903 case "reset_processing_time":
2904 $test_behaviour_settings = $test_behaviour_settings->withResetProcessingTime($metadata[
"entry"] ===
'1');
2906 case "answer_feedback_points":
2907 $question_behaviour_settings = $question_behaviour_settings->withInstantFeedbackPointsEnabled((
bool) $metadata[
"entry"]);
2909 case "answer_feedback":
2910 $question_behaviour_settings = $question_behaviour_settings->withInstantFeedbackGenericEnabled((
bool) $metadata[
"entry"]);
2912 case 'instant_feedback_specific':
2913 $question_behaviour_settings = $question_behaviour_settings->withInstantFeedbackSpecificEnabled((
bool) $metadata[
'entry']);
2915 case "instant_verification":
2916 $question_behaviour_settings = $question_behaviour_settings->withInstantFeedbackSolutionEnabled((
bool) $metadata[
"entry"]);
2918 case "force_instant_feedback":
2919 $question_behaviour_settings = $question_behaviour_settings->withForceInstantFeedbackOnNextQuestion((
bool) $metadata[
"entry"]);
2921 case "follow_qst_answer_fixation":
2922 $question_behaviour_settings = $question_behaviour_settings->withLockAnswerOnNextQuestionEnabled((
bool) $metadata[
"entry"]);
2924 case "instant_feedback_answer_fixation":
2925 $question_behaviour_settings = $question_behaviour_settings->withLockAnswerOnInstantFeedbackEnabled((
bool) $metadata[
"entry"]);
2928 case "suspend_test_allowed":
2929 $participant_functionality_settings = $participant_functionality_settings->withSuspendTestAllowed((
bool) $metadata[
"entry"]);
2931 case "sequence_settings":
2932 $participant_functionality_settings = $participant_functionality_settings->withPostponedQuestionsMoveToEnd((
bool) $metadata[
"entry"]);
2935 $participant_functionality_settings = $participant_functionality_settings->withQuestionMarkingEnabled((
bool) $metadata[
"entry"]);
2937 case "fixed_participants":
2938 $access_settings = $access_settings->withFixedParticipants((
bool) $metadata[
"entry"]);
2940 case "score_reporting":
2941 if ($metadata[
'entry'] !==
null) {
2942 $result_summary_settings = $result_summary_settings->withScoreReporting(
2943 ScoreReportingTypes::tryFrom((
int) $metadata[
'entry']) ?? ScoreReportingTypes::SCORE_REPORTING_DISABLED
2947 case "shuffle_questions":
2948 $question_behaviour_settings = $question_behaviour_settings->withShuffleQuestions((
bool) $metadata[
"entry"]);
2950 case "count_system":
2951 $scoring_settings = $scoring_settings->withCountSystem((
int) $metadata[
"entry"]);
2953 case "exportsettings":
2954 $result_details_settings = $result_details_settings->withExportSettings((
int) $metadata[
"entry"]);
2956 case "score_cutting":
2957 $scoring_settings = $scoring_settings->withScoreCutting((
int) $metadata[
"entry"]);
2960 $access_settings = $access_settings->withPasswordEnabled(
2961 $metadata[
"entry"] !==
null && $metadata[
"entry"] !==
''
2962 )->withPassword($metadata[
"entry"]);
2964 case 'ip_range_from':
2965 if ($metadata[
'entry'] !==
'') {
2966 $access_settings = $access_settings->withIpRangeFrom($metadata[
'entry']);
2970 if ($metadata[
'entry'] !==
'') {
2971 $access_settings = $access_settings->withIpRangeTo($metadata[
'entry']);
2974 case "pass_scoring":
2975 $scoring_settings = $scoring_settings->withPassScoring((
int) $metadata[
"entry"]);
2977 case 'pass_deletion_allowed':
2978 $result_summary_settings = $result_summary_settings->withPassDeletionAllowed((
bool) $metadata[
"entry"]);
2980 case "usr_pass_overview_mode":
2981 $participant_functionality_settings = $participant_functionality_settings->withUsrPassOverviewMode((
int) $metadata[
"entry"]);
2983 case "question_list":
2984 $participant_functionality_settings = $participant_functionality_settings->withQuestionListEnabled((
bool) $metadata[
"entry"]);
2987 case "reporting_date":
2988 $reporting_date = $this->buildDateTimeImmutableFromPeriod($metadata[
'entry']);
2989 if ($reporting_date !==
null) {
2990 $result_summary_settings = $result_summary_settings->withReportingDate($reporting_date);
2993 case 'enable_processing_time':
2994 $test_behaviour_settings = $test_behaviour_settings->withProcessingTimeEnabled((
bool) $metadata[
'entry']);
2996 case "processing_time":
2997 $test_behaviour_settings = $test_behaviour_settings->withProcessingTime($metadata[
'entry']);
2999 case "starting_time":
3000 $starting_time = $this->buildDateTimeImmutableFromPeriod($metadata[
'entry']);
3001 if ($starting_time !==
null) {
3002 $access_settings = $access_settings->withStartTime($starting_time)
3003 ->withStartTimeEnabled(
true);
3007 $ending_time = $this->buildDateTimeImmutableFromPeriod($metadata[
'entry']);
3008 if ($ending_time !==
null) {
3009 $access_settings = $access_settings->withEndTime($ending_time)
3010 ->withStartTimeEnabled(
true);
3013 case "enable_examview":
3014 $finishing_settings = $finishing_settings->withShowAnswerOverview((
bool) $metadata[
"entry"]);
3016 case 'redirection_mode':
3017 $finishing_settings = $finishing_settings->withRedirectionMode(
3018 RedirectionModes::tryFrom((
int) ($metadata[
'entry'] ?? 0)) ?? RedirectionModes::NONE
3021 case 'redirection_url':
3022 $finishing_settings = $finishing_settings->withRedirectionUrl($metadata[
'entry']);
3024 case 'examid_in_test_pass':
3025 $test_behaviour_settings = $test_behaviour_settings->withExamIdInTestAttemptEnabled((
bool) $metadata[
'entry']);
3027 case 'examid_in_test_res':
3028 $result_details_settings = $result_details_settings->withShowExamIdInTestResults((
bool) $metadata[
"entry"]);
3030 case 'skill_service':
3031 $additional_settings = $additional_settings->withSkillsServiceEnabled((
bool) $metadata[
'entry']);
3033 case 'show_grading_status':
3034 $result_summary_settings = $result_summary_settings->withShowGradingStatusEnabled((
bool) $metadata[
"entry"]);
3036 case 'show_grading_mark':
3037 $result_summary_settings = $result_summary_settings->withShowGradingMarkEnabled((
bool) $metadata[
"entry"]);
3040 $question_behaviour_settings = $question_behaviour_settings->withAutosaveEnabled((
bool) $metadata[
'entry']);
3042 case 'autosave_ival':
3043 $question_behaviour_settings = $question_behaviour_settings->withAutosaveInterval((
int) $metadata[
'entry']);
3045 case 'show_summary':
3046 $participant_functionality_settings = $participant_functionality_settings->withQuestionListEnabled(($metadata[
'entry'] & 1) > 0)
3047 ->withUsrPassOverviewMode((
int) $metadata[
'entry']);
3050 case 'hide_info_tab':
3051 $additional_settings = $additional_settings->withHideInfoTab($metadata[
'entry'] ===
'1');
3053 if (preg_match(
"/mark_step_\d+/", $metadata[
"label"])) {
3054 $xmlmark = $metadata[
"entry"];
3055 preg_match(
"/<short>(.*?)<\/short>/", $xmlmark, $matches);
3056 $mark_short = $matches[1];
3057 preg_match(
"/<official>(.*?)<\/official>/", $xmlmark, $matches);
3058 $mark_official = $matches[1];
3059 preg_match(
"/<percentage>(.*?)<\/percentage>/", $xmlmark, $matches);
3060 $mark_percentage = (float) $matches[1];
3061 preg_match(
"/<passed>(.*?)<\/passed>/", $xmlmark, $matches);
3062 $mark_passed = (bool) $matches[1];
3063 $mark_steps[] =
new Mark($mark_short, $mark_official, $mark_percentage, $mark_passed);
3066 $this->mark_schema = $this->getMarkSchema()->withMarkSteps($mark_steps);
3068 $this->getObjectProperties()->storePropertyTitleAndDescription(
3069 $this->getObjectProperties()->getPropertyTitleAndDescription()
3073 $this->addToNewsOnOnline(
false, $this->getObjectProperties()->getPropertyIsOnline()->getIsOnline());
3074 $main_settings = $main_settings
3076 ->withIntroductionSettings($introduction_settings)
3077 ->withAccessSettings($access_settings)
3078 ->withParticipantFunctionalitySettings($participant_functionality_settings)
3079 ->withTestBehaviourSettings($test_behaviour_settings)
3080 ->withQuestionBehaviourSettings($question_behaviour_settings)
3081 ->withFinishingSettings($finishing_settings)
3082 ->withAdditionalSettings($additional_settings);
3083 $this->getMainSettingsRepository()->store($main_settings);
3084 $this->main_settings = $main_settings;
3086 $score_settings = $score_settings
3088 ->withScoringSettings($scoring_settings)
3089 ->withResultDetailsSettings($result_details_settings)
3090 ->withResultSummarySettings($result_summary_settings);
3091 $this->getScoreSettingsRepository()->store($score_settings);
3092 $this->score_settings = $score_settings;
3093 $this->loadFromDb();
3101 if (!str_starts_with($material[
'text'],
'<PageObject>')) {
3105 $text = $this->replaceFilesInPageImports(
3106 $this->replaceMobsInPageImports(
3108 $mappings[
'components/ILIAS/MediaObjects'][
'mob'] ?? []
3113 $page_object->setParentId($this->
getId());
3114 $page_object->setXMLContent(
$text);
3115 $new_page_id = $page_object->createPageWithNextId();
3116 return $settings->withIntroductionPageId($new_page_id);
3124 if (!str_starts_with($material[
'text'],
'<PageObject>')) {
3128 $text = $this->replaceFilesInPageImports(
3129 $this->replaceMobsInPageImports(
3131 $mappings[
'components/ILIAS/MediaObjects'][
'mob'] ?? []
3136 $page_object->setParentId($this->
getId());
3137 $page_object->setXMLContent(
$text);
3138 $new_page_id = $page_object->createPageWithNextId();
3139 return $settings->withConcludingRemarksPageId($new_page_id);
3144 preg_match_all(
'/il_(\d+)_mob_(\d+)/',
$text, $matches);
3145 foreach ($matches[0] as $index => $match) {
3146 if (empty($mappings[$matches[2][$index]])) {
3149 $text = str_replace($match,
"il__mob_{$mappings[$matches[2][$index]]}",
$text);
3157 preg_match_all(
'/il_(\d+)_file_(\d+)/',
$text, $matches);
3158 foreach ($matches[0] as $index => $match) {
3159 if (empty($mappings[$matches[2][$index]])) {
3162 $text = str_replace($match,
"il__file_{$mappings[$matches[2][$index]]}",
$text);
3174 $main_settings = $this->getMainSettings();
3177 $a_xml_writer->xmlHeader();
3178 $a_xml_writer->xmlSetDtdDef(
"<!DOCTYPE questestinterop SYSTEM \"ims_qtiasiv1p2p1.dtd\">");
3179 $a_xml_writer->xmlStartTag(
"questestinterop");
3182 "ident" =>
"il_" .
IL_INST_ID .
"_tst_" . $this->getTestId(),
3185 $a_xml_writer->xmlStartTag(
"assessment", $attrs);
3186 $a_xml_writer->xmlElement(
"qticomment",
null, $this->getDescription());
3189 $a_xml_writer->xmlElement(
3192 $this->getProcessingTimeForXML()
3196 $a_xml_writer->xmlStartTag(
"qtimetadata");
3197 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3198 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"ILIAS_VERSION");
3199 $a_xml_writer->xmlElement(
"fieldentry",
null,
ILIAS_VERSION);
3200 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3202 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3203 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"anonymity");
3204 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getGeneralSettings()->getAnonymity() ? 1 : 0);
3205 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3207 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3208 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"question_set_type");
3209 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getGeneralSettings()->getQuestionSetType());
3210 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3212 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3213 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"sequence_settings");
3215 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3217 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3218 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"author");
3219 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getAuthor());
3220 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3222 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3223 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"reset_processing_time");
3224 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getTestBehaviourSettings()->getResetProcessingTime() ? 1 : 0);
3225 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3227 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3228 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"count_system");
3229 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getCountSystem());
3230 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3232 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3233 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"score_cutting");
3234 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getScoreCutting());
3235 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3237 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3238 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"password");
3239 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getAccessSettings()->getPassword() ??
'');
3240 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3242 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3243 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"ip_range_from");
3244 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getAccessSettings()->getIpRangeFrom());
3245 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3247 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3248 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"ip_range_to");
3249 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getAccessSettings()->getIpRangeTo());
3250 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3252 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3253 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"pass_scoring");
3254 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getPassScoring());
3255 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3257 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3258 $a_xml_writer->xmlElement(
'fieldlabel',
null,
'pass_deletion_allowed');
3259 $a_xml_writer->xmlElement(
'fieldentry',
null, $this->isPassDeletionAllowed() ? 1 : 0);
3260 $a_xml_writer->xmlEndTag(
'qtimetadatafield');
3262 if ($this->getScoreSettings()->getResultSummarySettings()->getReportingDate() !==
null) {
3263 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3264 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"reporting_date");
3265 $a_xml_writer->xmlElement(
3268 $this->buildIso8601PeriodForExportCompatibility(
3269 $this->getScoreSettings()->getResultSummarySettings()->getReportingDate(),
3272 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3275 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3276 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"nr_of_tries");
3278 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3280 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3281 $a_xml_writer->xmlElement(
'fieldlabel',
null,
'block_after_passed');
3282 $a_xml_writer->xmlElement(
'fieldentry',
null, $main_settings->
getTestBehaviourSettings()->getBlockAfterPassedEnabled() ? 1 : 0);
3283 $a_xml_writer->xmlEndTag(
'qtimetadatafield');
3285 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3286 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"pass_waiting");
3288 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3290 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3291 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"kiosk");
3293 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3295 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3296 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"redirection_mode");
3297 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getFinishingSettings()->getRedirectionMode()->value);
3298 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3300 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3301 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"redirection_url");
3302 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getFinishingSettings()->getRedirectionUrl());
3303 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3305 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3306 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"use_previous_answers");
3308 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3310 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3311 $a_xml_writer->xmlElement(
'fieldlabel',
null,
'question_list_enabled');
3313 $a_xml_writer->xmlEndTag(
'qtimetadatafield');
3315 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3316 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"title_output");
3318 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3320 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3321 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"results_presentation");
3322 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getScoreSettings()->getResultDetailsSettings()->getResultsPresentation());
3323 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3325 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3326 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"examid_in_test_pass");
3327 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getTestBehaviourSettings()->getExamIdInTestAttemptEnabled() ? 1 : 0);
3328 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3330 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3331 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"examid_in_test_res");
3332 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getScoreSettings()->getResultDetailsSettings()->getShowExamIdInTestResults() ? 1 : 0);
3333 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3335 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3336 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"usr_pass_overview_mode");
3338 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3340 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3341 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"score_reporting");
3342 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getScoreSettings()->getResultSummarySettings()->getScoreReporting()->value);
3343 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3345 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3346 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_solution_list_comparison");
3347 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->score_settings->getResultDetailsSettings()->getShowSolutionListComparison() ? 1 : 0);
3348 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3350 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3351 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"instant_verification");
3352 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getInstantFeedbackSolutionEnabled() ? 1 : 0);
3353 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3355 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3356 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"answer_feedback");
3357 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getInstantFeedbackGenericEnabled() ? 1 : 0);
3358 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3360 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3361 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"instant_feedback_specific");
3362 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getInstantFeedbackSpecificEnabled() ? 1 : 0);
3363 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3365 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3366 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"answer_feedback_points");
3367 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getInstantFeedbackPointsEnabled() ? 1 : 0);
3368 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3370 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3371 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"follow_qst_answer_fixation");
3372 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getLockAnswerOnNextQuestionEnabled() ? 1 : 0);
3373 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3375 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3376 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"instant_feedback_answer_fixation");
3377 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getLockAnswerOnInstantFeedbackEnabled() ? 1 : 0);
3378 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3380 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3381 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"force_instant_feedback");
3382 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getForceInstantFeedbackOnNextQuestion() ? 1 : 0);
3383 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3385 $highscore_metadata = [
3386 'highscore_enabled' => $this->getHighscoreEnabled(),
3387 'highscore_anon' => $this->getHighscoreAnon(),
3388 'highscore_achieved_ts' => $this->getHighscoreAchievedTS(),
3389 'highscore_score' => $this->getHighscoreScore(),
3390 'highscore_percentage' => $this->getHighscorePercentage(),
3391 'highscore_wtime' => $this->getHighscoreWTime(),
3392 'highscore_own_table' => $this->getHighscoreOwnTable(),
3393 'highscore_top_table' => $this->getHighscoreTopTable(),
3395 foreach ($highscore_metadata as $label => $value) {
3396 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3397 $a_xml_writer->xmlElement(
"fieldlabel",
null, $label);
3398 $a_xml_writer->xmlElement(
"fieldentry",
null, $value ? 1 : 0);
3399 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3401 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3402 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"highscore_top_num");
3403 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getHighscoreTopNum());
3404 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3406 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3407 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"suspend_test_allowed");
3409 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3411 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3412 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_marker");
3414 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3416 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3417 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"fixed_participants");
3418 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getAccessSettings()->getFixedParticipants() ? 1 : 0);
3419 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3421 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3422 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_introduction");
3423 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getIntroductionSettings()->getIntroductionEnabled() ? 1 : 0);
3424 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3426 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3427 $a_xml_writer->xmlElement(
"fieldlabel",
null,
'exam_conditions');
3428 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getIntroductionSettings()->getExamConditionsCheckboxEnabled() ? 1 : 0);
3429 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3431 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3432 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_concluding_remarks");
3433 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getFinishingSettings()->getConcludingRemarksEnabled() ? 1 : 0);
3434 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3436 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3437 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"exportsettings");
3438 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->getExportSettings());
3439 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3441 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3442 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"shuffle_questions");
3444 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3446 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3447 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"processing_time");
3449 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3451 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3452 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"enable_examview");
3453 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getFinishingSettings()->getShowAnswerOverview() ? 1 : 0);
3454 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3456 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3457 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"skill_service");
3458 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getAdditionalSettings()->getSkillsServiceEnabled() ? 1 : 0);
3459 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3461 if ($this->getInstantFeedbackSolution() == 1) {
3463 "solutionswitch" =>
"Yes"
3468 $a_xml_writer->xmlElement(
"assessmentcontrol", $attrs,
null);
3470 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3471 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_grading_status");
3472 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->isShowGradingStatusEnabled() ? 1 : 0);
3473 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3475 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3476 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"show_grading_mark");
3477 $a_xml_writer->xmlElement(
"fieldentry",
null, $this->isShowGradingMarkEnabled() ? 1 : 0);
3478 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3480 $a_xml_writer->xmlStartTag(
'qtimetadatafield');
3481 $a_xml_writer->xmlElement(
'fieldlabel',
null,
'hide_info_tab');
3482 $a_xml_writer->xmlElement(
'fieldentry',
null, $this->getMainSettings()->getAdditionalSettings()->getHideInfoTab() ? 1 : 0);
3483 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3485 if ($this->getStartingTime() > 0) {
3486 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3487 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"starting_time");
3488 $a_xml_writer->xmlElement(
3491 $this->buildIso8601PeriodForExportCompatibility(
3492 (
new DateTimeImmutable())->setTimestamp($this->getStartingTime()),
3495 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3498 if ($this->getEndingTime() > 0) {
3499 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3500 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"ending_time");
3501 $a_xml_writer->xmlElement(
3504 $this->buildIso8601PeriodForExportCompatibility(
3505 (
new DateTimeImmutable())->setTimestamp($this->getEndingTime()),
3508 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3511 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3512 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"autosave");
3514 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3516 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3517 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"autosave_ival");
3519 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3521 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3522 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"instant_feedback_specific");
3523 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getInstantFeedbackSpecificEnabled() ? 1 : 0);
3524 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3526 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3527 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"instant_feedback_answer_fixation");
3528 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getQuestionBehaviourSettings()->getLockAnswerOnInstantFeedbackEnabled() ? 1 : 0);
3529 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3531 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3532 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"enable_processing_time");
3533 $a_xml_writer->xmlElement(
"fieldentry",
null, $main_settings->
getTestBehaviourSettings()->getProcessingTimeEnabled() ? 1 : 0);
3534 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3536 foreach ($this->getMarkSchema()->getMarkSteps() as $index => $mark) {
3537 $a_xml_writer->xmlStartTag(
"qtimetadatafield");
3538 $a_xml_writer->xmlElement(
"fieldlabel",
null,
"mark_step_$index");
3539 $a_xml_writer->xmlElement(
"fieldentry",
null, sprintf(
3540 "<short>%s</short><official>%s</official><percentage>%.2f</percentage><passed>%d</passed>",
3541 $mark->getShortName(),
3542 $mark->getOfficialName(),
3543 $mark->getMinimumLevel(),
3546 $a_xml_writer->xmlEndTag(
"qtimetadatafield");
3548 $a_xml_writer->xmlEndTag(
"qtimetadata");
3551 $introduction = $page_id !==
null
3552 ? (
new ilTestPage($page_id))->getXMLContent()
3555 $a_xml_writer->xmlStartTag(
"objectives");
3556 $this->addQTIMaterial($a_xml_writer, $page_id, $introduction);
3557 $a_xml_writer->xmlEndTag(
"objectives");
3559 if ($this->getInstantFeedbackSolution() == 1) {
3561 "solutionswitch" =>
"Yes"
3566 $a_xml_writer->xmlElement(
"assessmentcontrol", $attrs,
null);
3568 if (strlen($this->getFinalStatement())) {
3570 $concluding_remarks = $page_id !==
null
3571 ? (
new ilTestPage($page_id))->getXMLContent()
3574 $a_xml_writer->xmlStartTag(
"presentation_material");
3575 $a_xml_writer->xmlStartTag(
"flow_mat");
3576 $this->addQTIMaterial($a_xml_writer, $page_id, $concluding_remarks);
3577 $a_xml_writer->xmlEndTag(
"flow_mat");
3578 $a_xml_writer->xmlEndTag(
"presentation_material");
3584 $a_xml_writer->xmlElement(
"section", $attrs,
null);
3585 $a_xml_writer->xmlEndTag(
"assessment");
3586 $a_xml_writer->xmlEndTag(
"questestinterop");
3588 $xml = $a_xml_writer->xmlDumpMem(
false);
3594 return $date_time->setTimezone(
new DateTimeZone(
'UTC'))->format(
'\PY\Yn\Mj\D\TG\Hi\Ms\S');
3599 if ($period ===
null) {
3602 if (preg_match(
"/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $period, $matches)) {
3603 return new DateTimeImmutable(
3605 "%02d-%02d-%02d %02d:%02d:%02d",
3613 new \DateTimeZone(
'UTC')
3625 public function exportPagesXML(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog): void
3627 $this->mob_ids = [];
3630 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Start Export Page Objects");
3631 $this->bench->start(
"ContentObjectExport",
"exportPageObjects");
3632 $this->exportXMLPageObjects($a_xml_writer, $a_inst, $expLog);
3633 $this->bench->stop(
"ContentObjectExport",
"exportPageObjects");
3634 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Finished Export Page Objects");
3637 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Start Export Media Objects");
3638 $this->bench->start(
"ContentObjectExport",
"exportMediaObjects");
3639 $this->exportXMLMediaObjects($a_xml_writer, $a_inst, $a_target_dir, $expLog);
3640 $this->bench->stop(
"ContentObjectExport",
"exportMediaObjects");
3641 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Finished Export Media Objects");
3644 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Start Export File Items");
3645 $this->bench->start(
"ContentObjectExport",
"exportFileItems");
3646 $this->exportFileItems($a_target_dir, $expLog);
3647 $this->bench->stop(
"ContentObjectExport",
"exportFileItems");
3648 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Finished Export File Items");
3658 if ($a_tag ==
"Identifier" && $a_param ==
"Entry") {
3674 foreach ($this->questions as $question_id) {
3675 $this->bench->start(
"ContentObjectExport",
"exportPageObject");
3676 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Page Object " . $question_id);
3679 $a_xml_writer->xmlStartTag(
"PageObject", $attrs);
3683 $this->bench->start(
"ContentObjectExport",
"exportPageObject_XML");
3685 $page_object->buildDom();
3686 $page_object->insertInstIntoIDs((
string) $inst);
3687 $mob_ids = $page_object->collectMediaObjects(
false);
3689 $xml = $page_object->getXMLFromDom(
false,
false,
false,
"",
true);
3690 $xml = str_replace(
"&",
"&", $xml);
3691 $a_xml_writer->appendXML($xml);
3692 $page_object->freeDom();
3693 unset($page_object);
3695 $this->bench->stop(
"ContentObjectExport",
"exportPageObject_XML");
3698 $this->bench->start(
"ContentObjectExport",
"exportPageObject_CollectMedia");
3700 foreach ($mob_ids as $mob_id) {
3701 $this->mob_ids[$mob_id] = $mob_id;
3703 $this->bench->stop(
"ContentObjectExport",
"exportPageObject_CollectMedia");
3706 $this->bench->start(
"ContentObjectExport",
"exportPageObject_CollectFileItems");
3708 foreach ($file_ids as $file_id) {
3709 $this->file_ids[$file_id] = $file_id;
3711 $this->bench->stop(
"ContentObjectExport",
"exportPageObject_CollectFileItems");
3713 $a_xml_writer->xmlEndTag(
"PageObject");
3716 $this->bench->stop(
"ContentObjectExport",
"exportPageObject");
3725 foreach ($this->mob_ids as $mob_id) {
3726 $expLog->write(date(
"[y-m-d H:i:s] ") .
"Media Object " . $mob_id);
3728 $target_dir = $a_target_dir . DIRECTORY_SEPARATOR .
'objects'
3729 . DIRECTORY_SEPARATOR .
'il_' .
IL_INST_ID .
'_mob_' . $mob_id;
3732 $media_obj->exportXML($a_xml_writer, (
int) $a_inst);
3733 foreach ($media_obj->getMediaItems() as $item) {
3734 $stream = $item->getLocationStream();
3735 file_put_contents($target_dir . DIRECTORY_SEPARATOR . $item->getLocation(), $stream);
3749 foreach ($this->file_ids as $file_id) {
3750 $expLog->write(date(
"[y-m-d H:i:s] ") .
"File Item " . $file_id);
3751 $file_dir = $target_dir .
'/objects/il_' .
IL_INST_ID .
'_file_' . $file_id;
3753 $file_obj =
new ilObjFile((
int) $file_id,
false);
3754 $source_file = $file_obj->getFile($file_obj->getVersion());
3755 if (!is_file($source_file)) {
3756 $source_file = $file_obj->getFile();
3758 if (is_file($source_file)) {
3759 copy($source_file, $file_dir .
'/' . $file_obj->getFileName());
3778 $this->saveCompleteStatus($this->question_set_config_factory->getQuestionSetConfig());
3780 if ($this->participantDataExist()) {
3781 $this->recalculateScores(
true);
3790 $total = $this->evalTotalPersons();
3791 $results_summary_settings = $this->getScoreSettings()->getResultSummarySettings();
3793 || $results_summary_settings->getScoreReporting()->isReportingEnabled() ===
false) {
3797 if ($results_summary_settings->getScoreReporting() === ScoreReportingTypes::SCORE_REPORTING_DATE) {
3798 return $results_summary_settings->getReportingDate()
3799 >=
new DateTimeImmutable(
'now',
new DateTimeZone(
'UTC'));
3816 $path_to_lifecycle = $this->lo_metadata->paths()->custom()->withNextStep(
'lifeCycle')->get();
3817 $path_to_authors = $this->lo_metadata->paths()->authors();
3819 $reader = $this->lo_metadata->read($this->
getId(), 0, $this->getType(), $path_to_lifecycle);
3820 if (!is_null($reader->allData($path_to_lifecycle)->current())) {
3824 if ($author ===
'') {
3825 $author = $this->
user->getFullname();
3827 $this->lo_metadata->manipulate($this->
getId(), 0, $this->getType())
3828 ->prepareCreateOrUpdate($path_to_authors, $author)
3837 $this->saveAuthorToMetadata();
3849 $path_to_authors = $this->lo_metadata->paths()->authors();
3850 $author_data = $this->lo_metadata->read($this->
getId(), 0, $this->getType(), $path_to_authors)
3851 ->allData($path_to_authors);
3853 return $this->lo_metadata->dataHelper()->makePresentableAsList(
', ', ...$author_data);
3867 $lo_metadata =
$DIC->learningObjectMetadata();
3869 $path_to_authors = $lo_metadata->paths()->authors();
3870 $author_data = $lo_metadata->read($obj_id, 0,
"tst", $path_to_authors)
3871 ->allData($path_to_authors);
3873 return $lo_metadata->dataHelper()->makePresentableAsList(
',', ...$author_data);
3885 $ilUser =
$DIC[
'ilUser'];
3888 $tests = array_slice(
3896 if (count($tests)) {
3899 if ($use_object_id) {
3901 $result_array[$obj_id] = $titles[
$ref_id];
3907 return $result_array;
3920 $this->loadFromDb();
3922 $new_obj = parent::cloneObject($target_id, $copy_id, $omit_tree);
3923 $new_obj->setTmpCopyWizardCopyId($copy_id);
3924 $this->cloneMetaData($new_obj);
3926 $new_obj->saveToDb();
3927 $new_obj->addToNewsOnOnline(
false, $new_obj->getObjectProperties()->getPropertyIsOnline()->getIsOnline());
3929 $new_main_settings = $this->getMainSettings()
3930 ->withIntroductionSettings(
3931 $this->getMainSettings()->getIntroductionSettings()->withIntroductionPageId(
3932 $this->cloneIntroduction()
3934 )->withFinishingSettings(
3935 $this->getMainSettings()->getFinishingSettings()->withConcludingRemarksPageId(
3936 $this->cloneConcludingRemarks()
3940 $new_main_settings = $this->getMainSettingsRepository()->store($new_main_settings, $new_obj->getTestId());
3941 $this->getScoreSettingsRepository()->store(
3942 $this->getScoreSettings()->withId($new_main_settings->getId())
3944 $this->marks_repository->storeMarkSchema(
3945 $this->getMarkSchema()->withTestId($new_obj->getTestId())
3948 $new_obj->setTemplate($this->getTemplate());
3957 $templateRepository,
3961 $cloneAction->cloneCertificate($this, $new_obj);
3963 $this->question_set_config_factory->getQuestionSetConfig()->cloneQuestionSetRelatedData($new_obj);
3964 $new_obj->saveQuestionsToDb();
3967 $skillLevelThresholdList->setTestId($this->getTestId());
3968 $skillLevelThresholdList->loadFromDb();
3969 $skillLevelThresholdList->cloneListForTest($new_obj->getTestId());
3972 $obj_settings->cloneSettings($new_obj->getId());
3974 if ($new_obj->getTestLogger()->isLoggingEnabled()) {
3975 $new_obj->getTestLogger()->logTestAdministrationInteraction(
3976 $new_obj->getTestLogger()->getInteractionFactory()->buildTestAdministrationInteraction(
3977 $new_obj->getRefId(),
3978 $this->user->getId(),
3979 TestAdministrationInteractionTypes::NEW_TEST_CREATED,
3992 if ($this->isRandomTest()) {
3998 $this->component_repository,
4000 $this->questionrepository
4003 $questionSetConfig->loadFromDb();
4005 if ($questionSetConfig->isQuestionAmountConfigurationModePerPool()) {
4012 $sourcePoolDefinitionList->loadDefinitions();
4014 if (is_int($sourcePoolDefinitionList->getQuestionAmount())) {
4015 $num = $sourcePoolDefinitionList->getQuestionAmount();
4017 } elseif (is_int($questionSetConfig->getQuestionAmountPerTest())) {
4018 $num = $questionSetConfig->getQuestionAmountPerTest();
4021 $this->loadQuestions();
4022 $num = count($this->questions);
4030 if ($this->isRandomTest()) {
4031 return $this->getQuestionCount();
4033 return count($this->questions);
4048 $result =
$ilDB->queryF(
4049 "SELECT obj_fi FROM tst_tests WHERE test_id = %s",
4053 if ($result->numRows()) {
4054 $row =
$ilDB->fetchAssoc($result);
4055 $object_id = $row[
"obj_fi"];
4072 $result =
$ilDB->queryF(
4073 "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",
4077 if ($result->numRows()) {
4078 $row =
$ilDB->fetchAssoc($result);
4079 $object_id = $row[
"obj_fi"];
4096 $result =
$ilDB->queryF(
4097 "SELECT test_id FROM tst_tests WHERE obj_fi = %s",
4101 if ($result->numRows()) {
4102 $row =
$ilDB->fetchAssoc($result);
4103 $test_id = $row[
"test_id"];
4118 if (($active_id) && ($question_id)) {
4119 if ($pass ===
null) {
4122 if ($pass ===
null) {
4125 $query = $this->db->queryF(
4126 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
4127 [
'integer',
'integer',
'integer'],
4128 [$active_id, $question_id, $pass]
4130 $result = $this->db->fetchAll($query);
4131 if (count($result) == 1) {
4132 return $result[0][
"value1"];
4149 $result = $this->db->queryF(
4150 "SELECT question_text FROM qpl_questions WHERE question_id = %s",
4154 if ($result->numRows() == 1) {
4155 $row = $this->db->fetchAssoc($result);
4156 $res = $row[
"question_text"];
4165 $participant_list->initializeFromDbRows($this->getTestParticipants());
4167 return $participant_list;
4180 if ($this->getAnonymity()) {
4182 $result = $this->db->queryF(
4183 "SELECT tst_active.active_id, tst_active.tries, usr_id, %s login, %s lastname, %s firstname, " .
4184 "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 " .
4185 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
4186 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
4188 [
'text',
'text',
'text',
'integer',
'integer'],
4189 [
'', $this->
lng->txt(
'anonymous'),
'', $this->getTestId(),
$user_id]
4192 $result = $this->db->queryF(
4193 "SELECT tst_active.active_id, tst_active.tries, usr_id, %s login, %s lastname, %s firstname, " .
4194 "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 " .
4195 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
4196 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
4198 [
'text',
'text',
'text',
'integer'],
4199 [
'', $this->
lng->txt(
'anonymous'),
'', $this->getTestId()]
4204 $result = $this->db->queryF(
4205 "SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, " .
4206 "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 " .
4207 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
4208 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
4210 [
'integer',
'integer'],
4214 $result = $this->db->queryF(
4215 "SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, " .
4216 "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 " .
4217 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
4218 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
4221 [$this->getTestId()]
4226 while ($row = $this->db->fetchAssoc($result)) {
4227 $result_array[$row[
'usr_id']] = $row;
4229 return $result_array;
4236 SELECT tst_active.active_id,
4238 tst_active.user_fi usr_id,
4242 tst_active.submitted test_finished,
4243 usr_data.matriculation,
4245 tst_active.lastindex,
4246 COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes
4249 ON tst_active.user_fi = usr_data.usr_id
4250 WHERE tst_active.test_fi = %s
4251 ORDER BY usr_data.lastname
4253 $result = $this->db->queryF(
4255 [
'text',
'text',
'text',
'integer'],
4256 [
'', $this->
lng->txt(
"anonymous"),
"", $this->getTestId()]
4260 SELECT tst_active.active_id,
4262 tst_active.user_fi usr_id,
4266 tst_active.submitted test_finished,
4267 usr_data.matriculation,
4269 tst_active.lastindex,
4270 COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes
4273 ON tst_active.user_fi = usr_data.usr_id
4274 WHERE tst_active.test_fi = %s
4275 ORDER BY usr_data.lastname
4277 $result = $this->db->queryF(
4280 [$this->getTestId()]
4284 while ($row = $this->db->fetchAssoc($result)) {
4285 $data[$row[
'active_id']] = $row;
4287 foreach (
$data as $index => $participant) {
4288 if (strlen(trim($participant[
"firstname"] . $participant[
"lastname"])) == 0) {
4289 $data[$index][
"lastname"] = $this->
lng->txt(
"deleted_user");
4297 if (!$this->getGlobalSettings()->isManualScoringEnabled()) {
4301 $filtered_participants = [];
4302 foreach ($this->getTestParticipants() as $active_id => $participant) {
4303 if ($participant[
'tries'] > 0) {
4306 if ($this->test_man_scoring_done_helper->isDone((
int) $active_id)) {
4307 $filtered_participants[$active_id] = $participant;
4311 if (!$this->test_man_scoring_done_helper->isDone((
int) $active_id)) {
4312 $filtered_participants[$active_id] = $participant;
4316 $filtered_participants[$active_id] = $participant;
4320 return $filtered_participants;
4331 if (!is_array($ids) || count($ids) == 0) {
4335 if ($this->getAnonymity()) {
4336 $result = $this->db->queryF(
4337 "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",
4338 [
'text',
'text',
'text'],
4339 [
"", $this->lng->txt(
"anonymous"),
""]
4342 $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");
4346 while ($row = $this->db->fetchAssoc($result)) {
4347 $result_array[$row[
"usr_id"]] = $row;
4349 return $result_array;
4354 if (!is_array($ids) || count($ids) == 0) {
4367 if (!is_array($ids) || count($ids) == 0) {
4371 foreach ($ids as $obj_id) {
4385 $this->db->manipulateF(
4386 "DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
4387 [
'integer',
'integer'],
4390 $this->db->manipulateF(
4391 "INSERT INTO tst_invited_user (test_fi, user_fi, ip_range_from, ip_range_to, tstamp) VALUES (%s, %s, %s, %s, %s)",
4392 [
'integer',
'integer',
'text',
'text',
'integer'],
4393 [$this->getTestId(),
$user_id, (strlen($client_ip)) ? $client_ip :
null, (strlen($client_ip)) ? $client_ip :
null,time()]
4406 if (is_numeric($question_fi)) {
4407 $result =
$ilDB->queryF(
4408 "SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s AND question_fi=%s",
4409 [
'integer',
'integer'],
4410 [$active_id, $question_fi]
4413 $result =
$ilDB->queryF(
4414 "SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s",
4420 while ($row =
$ilDB->fetchAssoc($result)) {
4421 $result_array[$row[
"question_fi"]] = $row;
4423 return $result_array;
4432 $active_id = $this->getActiveIdOfUser(
$user_id);
4433 $this->db->manipulateF(
4434 "DELETE FROM tst_qst_solved WHERE active_fi = %s AND question_fi = %s",
4435 [
'integer',
'integer'],
4436 [$active_id, $question_id]
4438 $this->db->manipulateF(
4439 "INSERT INTO tst_qst_solved (solved, question_fi, active_fi) VALUES (%s, %s, %s)",
4440 [
'integer',
'integer',
'integer'],
4441 [$value, $question_id, $active_id]
4450 $result = $this->db->queryF(
4451 "SELECT submitted FROM tst_active WHERE active_id=%s AND submitted=%s",
4452 [
'integer',
'integer'],
4455 return $result->numRows() == 1;
4467 $result = $this->db->queryF(
4468 "SELECT submitted FROM tst_active WHERE test_fi=%s AND user_fi=%s AND submitted=%s",
4469 [
'integer',
'integer',
'integer'],
4472 return $result->numRows() == 1;
4480 return $this->getNrOfTries() != 0;
4491 return $tries >= $this->getNrOfTries();
4506 "user_id" => $this->
lng->txt(
"user_id"),
4507 "matriculation" => $this->
lng->txt(
"matriculation"),
4508 "lastname" => $this->
lng->txt(
"lastname"),
4509 "firstname" => $this->
lng->txt(
"firstname"),
4510 "login" => $this->
lng->txt(
"login"),
4511 "reached_points" => $this->
lng->txt(
"tst_reached_points"),
4512 "max_points" => $this->
lng->txt(
"tst_maximum_points"),
4513 "percent_value" => $this->
lng->txt(
"tst_percent_solved"),
4514 "mark" => $this->
lng->txt(
"tst_mark"),
4515 "passed" => $this->
lng->txt(
"tst_mark_passed"),
4518 if (count($participants)) {
4519 foreach ($participants as $active_id => $user_rec) {
4522 $reached_points = 0;
4526 if (!is_int($pass)) {
4529 foreach ($this->questions as $value) {
4531 if (is_object($question)) {
4532 $max_points += $question->getMaximumPoints();
4533 $reached_points += $question->getReachedPoints($active_id, $pass);
4536 if ($max_points > 0) {
4537 $percentvalue = $reached_points / $max_points;
4538 if ($percentvalue < 0) {
4539 $percentvalue = 0.0;
4544 $mark_obj = $this->getMarkSchema()->getMatchingMark($percentvalue * 100);
4546 if ($mark_obj !==
null) {
4547 $mark = $mark_obj->getOfficialName();
4549 if ($this->getAnonymity()) {
4550 $user_rec[
'firstname'] =
"";
4551 $user_rec[
'lastname'] = $this->
lng->txt(
"anonymous");
4554 "user_id" => $user_rec[
'usr_id'],
4555 "matriculation" => $user_rec[
'matriculation'],
4556 "lastname" => $user_rec[
'lastname'],
4557 "firstname" => $user_rec[
'firstname'],
4558 "login" => $user_rec[
'login'],
4559 "reached_points" => $reached_points,
4560 "max_points" => $max_points,
4561 "percent_value" => $percentvalue,
4563 "passed" => $user_rec[
'passed'] ?
'1' :
'0',
4582 $result =
$ilDB->queryF(
4583 "SELECT tries FROM tst_active WHERE active_id = %s",
4587 if ($result->numRows()) {
4588 $row =
$ilDB->fetchAssoc($result);
4589 return $row[
"tries"];
4608 $result =
$ilDB->queryF(
4609 "SELECT MAX(pass) maxpass FROM tst_pass_result WHERE active_fi = %s",
4614 if ($result->numRows()) {
4615 $row =
$ilDB->fetchAssoc($result);
4616 return $row[
"maxpass"];
4632 $result =
$ilDB->queryF(
4633 "SELECT * FROM tst_pass_result WHERE active_fi = %s",
4638 if (!$result->numRows()) {
4644 while ($row =
$ilDB->fetchAssoc($result)) {
4645 if ($row[
"maxpoints"] > 0.0) {
4646 $factor = (float) ($row[
"points"] / $row[
"maxpoints"]);
4650 if ($factor === 0.0 && $bestfactor === 0.0
4651 || $factor > $bestfactor) {
4653 $bestfactor = $factor;
4657 if (is_array($bestrow)) {
4658 return $bestrow[
"pass"];
4674 $counted_pass =
null;
4680 return $counted_pass;
4694 if ($this->isRandomTest()) {
4695 $this->loadQuestions($active_id, $pass);
4698 foreach ($this->questions as $value) {
4699 if ($this->questionrepository->lookupResultRecordExist($active_id, $value, $pass)) {
4700 $workedthrough += 1;
4703 return $workedthrough;
4717 if (is_null($pass)) {
4722 SELECT tst_pass_result.tstamp pass_res_tstamp,
4723 tst_test_result.tstamp quest_res_tstamp
4725 FROM tst_pass_result
4727 LEFT JOIN tst_test_result
4728 ON tst_test_result.active_fi = tst_pass_result.active_fi
4729 AND tst_test_result.pass = tst_pass_result.pass
4731 WHERE tst_pass_result.active_fi = %s
4732 AND tst_pass_result.pass = %s
4734 ORDER BY tst_test_result.tstamp DESC
4737 $result =
$ilDB->queryF(
4739 [
'integer',
'integer'],
4743 while ($row =
$ilDB->fetchAssoc($result)) {
4744 if ($row[
'quest_res_tstamp']) {
4745 return $row[
'quest_res_tstamp'];
4748 return $row[
'pass_res_tstamp'];
4765 "executable" =>
true,
4766 "errormessage" =>
""
4769 if (!$this->getObjectProperties()->getPropertyIsOnline()->getIsOnline()) {
4770 $result[
"executable"] =
false;
4771 $result[
"errormessage"] = $this->
lng->txt(
'autosave_failed') .
': ' . $this->
lng->txt(
'offline');
4775 if (!$this->startingTimeReached()) {
4776 $result[
"executable"] =
false;
4780 if ($this->endingTimeReached()) {
4781 $result[
"executable"] =
false;
4786 $active_id = $this->getActiveIdOfUser(
$user_id);
4788 if ($this->getEnableProcessingTime()
4790 && ($starting_time = $this->getStartingTimeOfUser($active_id)) !==
false
4791 && $this->isMaxProcessingTimeReached($starting_time, $active_id)) {
4792 $result[
"executable"] =
false;
4793 $result[
"errormessage"] = $this->
lng->txt(
"detail_max_processing_time_reached");
4798 $testPassesSelector->setActiveId($active_id);
4799 $testPassesSelector->setLastFinishedPass($test_session->getLastFinishedPass());
4801 if ($this->hasNrOfTriesRestriction() && ($active_id > 0)) {
4802 $closedPasses = $testPassesSelector->getClosedPasses();
4804 if (count($closedPasses) >= $this->getNrOfTries()) {
4805 $result[
"executable"] =
false;
4806 $result[
"errormessage"] = $this->
lng->txt(
"maximum_nr_of_tries_reached");
4810 if ($this->isBlockPassesAfterPassedEnabled() && !$testPassesSelector->openPassExists()) {
4811 if ($this->test_result_repository->isPassed(
$user_id, $this->getId())) {
4812 $result[
'executable'] =
false;
4813 $result[
'errormessage'] = $this->
lng->txt(
"tst_addit_passes_blocked_after_passed_msg");
4819 $next_pass_allowed_timestamp = 0;
4820 if (!$this->isNextPassAllowed($testPassesSelector, $next_pass_allowed_timestamp)) {
4823 $result[
'executable'] =
false;
4824 $result[
'errormessage'] = sprintf($this->
lng->txt(
'wait_for_next_pass_hint_msg'), $date);
4832 $waiting_between_passes = $this->getMainSettings()->getTestBehaviourSettings()->getPassWaiting();
4836 $this->getMainSettings()->getTestBehaviourSettings()->getPassWaitingEnabled()
4837 && ($waiting_between_passes !==
'')
4839 && ($last_finished_pass_timestamp !==
null)
4841 $time_values = explode(
':', $waiting_between_passes);
4842 $next_pass_allowed_timestamp = strtotime(
'+ ' . $time_values[0] .
' Days + ' . $time_values[1] .
' Hours' . $time_values[2] .
' Minutes', $last_finished_pass_timestamp);
4843 return (time() > $next_pass_allowed_timestamp);
4853 $pass_selector->setActiveId($test_session->
getActiveId());
4856 return $pass_selector->hasReportablePasses();
4863 $pass_selector->setActiveId($test_session->
getActiveId());
4866 return $pass_selector->hasExistingPasses();
4878 if ($active_id < 1) {
4881 if ($pass ===
null) {
4882 $pass = ($this->getResetProcessingTime()) ? self::_getPass($active_id) : 0;
4884 $result = $this->db->queryF(
4885 "SELECT tst_times.started FROM tst_times WHERE tst_times.active_fi = %s AND tst_times.pass = %s ORDER BY tst_times.started",
4886 [
'integer',
'integer'],
4889 if ($result->numRows()) {
4890 $row = $this->db->fetchAssoc($result);
4891 if (preg_match(
"/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row[
"started"], $matches)) {
4916 if (!$this->getEnableProcessingTime()) {
4920 $processing_time = $this->getProcessingTimeInSeconds($active_id);
4922 if ($now > ($starting_time + $processing_time)) {
4931 $tags_trafo = $this->
refinery->string()->stripTags();
4935 questtypes.type_tag,
4937 origquest.obj_fi orig_obj_fi
4939 FROM qpl_questions questions
4941 INNER JOIN qpl_qst_type questtypes
4942 ON questtypes.question_type_id = questions.question_type_fi
4944 INNER JOIN tst_test_question tstquest
4945 ON tstquest.question_fi = questions.question_id
4947 LEFT JOIN qpl_questions origquest
4948 ON origquest.question_id = questions.original_id
4950 WHERE tstquest.test_fi = %s
4952 ORDER BY tstquest.sequence
4955 $query_result = $this->db->queryF(
4958 [$this->getTestId()]
4963 while ($row = $this->db->fetchAssoc($query_result)) {
4964 $row[
'title'] = $tags_trafo->transform($row[
'title']);
4965 $row[
'description'] = $tags_trafo->transform($row[
'description'] !==
'' && $row[
'description'] !==
null ? $row[
'description'] :
' ');
4966 $row[
'author'] = $tags_trafo->transform($row[
'author']);
4968 $questions[] = $row;
4976 foreach ($this->getTestQuestions() as $questionData) {
4977 if ($questionData[
'question_id'] != $question_id) {
4989 $row = $this->db->fetchAssoc($this->db->queryF(
4990 "SELECT COUNT(question_id) cnt FROM qpl_questions WHERE question_id = %s AND obj_fi = %s",
4991 [
'integer',
'integer'],
4992 [$question_id, $this->getId()]
4995 return (
bool) $row[
'cnt'];
5002 foreach ($this->getTestQuestions() as $question_data) {
5003 $points += $question_data[
'points'];
5016 questtypes.type_tag,
5017 origquest.obj_fi orig_obj_fi
5019 FROM qpl_questions questions
5021 INNER JOIN qpl_qst_type questtypes
5022 ON questtypes.question_type_id = questions.question_type_fi
5024 INNER JOIN tst_rnd_cpy tstquest
5025 ON tstquest.qst_fi = questions.question_id
5027 LEFT JOIN qpl_questions origquest
5028 ON origquest.question_id = questions.original_id
5030 WHERE tstquest.tst_fi = %s
5033 $query_result = $this->db->queryF(
5036 [$this->getTestId()]
5039 return $this->db->fetchAll($query_result);
5044 return $this->getMainSettings()->getQuestionBehaviourSettings()->getShuffleQuestions();
5060 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getUsrPassOverviewMode();
5065 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getQuestionListEnabled();
5070 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getUsrPassOverviewEnabled();
5075 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getShownQuestionListAtBeginning();
5080 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getShownQuestionListAtEnd();
5085 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getShowDescriptionInQuestionList();
5093 return $this->getScoreSettings()->getResultDetailsSettings()->getShowPassDetails();
5101 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionPrintview();
5108 return $this->getShowSolutionPrintview();
5116 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionFeedback();
5124 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionAnswersOnly();
5132 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionSignature();
5140 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionSuggested();
5149 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionListComparison();
5154 return $this->getScoreSettings()->getResultDetailsSettings()->getShowSolutionListOwnAnswers();
5164 $result =
$ilDB->queryF(
5165 "SELECT user_fi FROM tst_active WHERE active_id = %s",
5169 if ($result->numRows()) {
5170 $row =
$ilDB->fetchAssoc($result);
5171 return $row[
"user_fi"];
5179 $result = $this->db->queryF(
5180 "SELECT finished FROM tst_times WHERE active_fi = %s ORDER BY finished DESC",
5184 if ($result->numRows()) {
5185 $row = $this->db->fetchAssoc($result);
5186 return $row[
"finished"];
5191 public static function lookupLastTestPassAccess(
int $active_id,
int $pass_index): ?
int
5198 SELECT MAX(tst_times.tstamp) as last_pass_access
5200 WHERE active_fi = %s
5206 [
'integer',
'integer'],
5207 [$active_id, $pass_index]
5210 while ($row =
$ilDB->fetchAssoc(
$res)) {
5211 return $row[
'last_pass_access'];
5226 if (preg_match(
"/<[^>]*?>/", $a_text)) {
5243 for ($i = 0; $i < $a_material->getMaterialCount(); $i++) {
5244 $material = $a_material->getMaterial($i);
5245 if ($material[
'type'] ===
'mattext') {
5246 $result .= $material[
'material']->getContent();
5248 if ($material[
'type'] ===
'matimage') {
5249 $matimage = $material[
'material'];
5250 if (preg_match(
'/(il_([0-9]+)_mob_([0-9]+))/', $matimage->getLabel(), $matches)) {
5252 'mob' => $matimage->getLabel(),
5253 'uri' => $matimage->getUri()
5259 $decoded_result = base64_decode($result);
5260 if (str_starts_with($decoded_result,
'<PageObject>')) {
5261 $result = $decoded_result;
5274 'texttype' =>
'text/plain'
5278 if ($page_id !==
null) {
5279 $attrs[
'texttype'] =
'text/xml';
5282 $page_object->buildDom();
5283 $page_object->insertInstIntoIDs((
string)
IL_INST_ID);
5284 $material = base64_encode($page_object->getXMLFromDom());
5286 foreach ($file_ids as $file_id) {
5287 $this->file_ids[] = (
int) $file_id;
5290 } elseif ($this->isHTML($material)) {
5291 $attrs[
'texttype'] =
'text/xhtml';
5293 $mob_string =
'mm_';
5296 $xml_writer->
xmlElement(
'mattext', $attrs, $material);
5297 foreach ($mobs as $mob) {
5298 $mob_id_string = (string) $mob;
5299 $moblabel =
'il_' .
IL_INST_ID .
'_mob_' . $mob_id_string;
5300 if (strpos($material, $mob_string . $mob_id_string) !==
false) {
5304 'label' => $moblabel,
5305 'uri' =>
'objects/' .
'il_' .
IL_INST_ID .
'_mob_' . $mob_id_string .
'/' . $mob_obj->getTitle()
5308 $xml_writer->
xmlElement(
'matimage', $imgattrs,
null);
5322 if ($txt_output ==
null) {
5327 $prepare_for_latex_output,
5328 $omitNl2BrWhenTextArea
5334 return $this->getMainSettings()->getGeneralSettings()->getAnonymity();
5339 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getSuspendTestAllowed();
5344 return $this->getMainSettings()->getParticipantFunctionalitySettings()->getQuestionMarkingEnabled();
5349 return $this->getMainSettings()->getAccessSettings()->getFixedParticipants();
5354 return $this->main_settings_repository->getFor(
5355 self::_getTestIDFromObjectID(self::_getObjectIDFromActiveID($active_id)),
5356 )->getGeneralSettings()->getQuestionSetType();
5371 if ($this->getAnonymity() && !$overwrite_anonymity) {
5372 return $this->
lng->txt(
"anonymous") . $suffix;
5375 if (strlen($uname[
"firstname"] . $uname[
"lastname"]) == 0) {
5376 $uname[
"firstname"] = $this->
lng->txt(
"deleted_user");
5378 if ($sorted_order) {
5379 return trim($uname[
"lastname"] .
", " . $uname[
"firstname"]) . $suffix;
5381 return trim($uname[
"firstname"] .
" " . $uname[
"lastname"]) . $suffix;
5387 DateTimeImmutable|
int|
null $date_time
5388 ): ?DateTimeImmutable {
5389 if ($date_time === null || $date_time instanceof DateTimeImmutable) {
5393 return DateTimeImmutable::createFromFormat(
'U', (
string) $date_time);
5405 if (extension_loaded(
"tidy")) {
5408 "output-xml" =>
true,
5409 "numeric-entities" =>
true
5412 $tidy->parseString($print_output, $config,
'utf8');
5413 $tidy->cleanRepair();
5414 $print_output = tidy_get_output($tidy);
5415 $print_output = preg_replace(
"/^.*?(<html)/",
"\\1", $print_output);
5417 $print_output = str_replace(
" ",
" ", $print_output);
5418 $print_output = str_replace(
"⊗",
"X", $print_output);
5420 $xsl = file_get_contents(
"./components/ILIAS/Test/xml/question2fo.xsl");
5425 'font-family="Helvetica, unifont"',
5426 'font-family="' . $this->
settings->get(
'rpc_pdf_font',
'Helvetica, unifont') .
'"',
5430 $args = [
'/_xml' => $print_output,
'/_xsl' => $xsl ];
5431 $xh = xslt_create();
5433 $output = xslt_process($xh,
"arg:/_xml",
"arg:/_xsl",
null, $args,
$params);
5447 $content = preg_replace(
"/href=\".*?\"/",
"", $content);
5448 $printbody =
new ilTemplate(
"tpl.il_as_tst_print_body.html",
true,
true,
"components/ILIAS/Test");
5450 $printbody->setVariable(
"ADM_CONTENT", $content);
5451 $printbody->setCurrentBlock(
"css_file");
5453 $printbody->parseCurrentBlock();
5454 $printoutput = $printbody->get();
5455 $html = str_replace(
"href=\"./",
"href=\"" . ILIAS_HTTP_PATH .
"/", $printoutput);
5456 $html = preg_replace(
"/<div id=\"dontprint\">.*?<\\/div>/ims",
"", $html);
5457 if (extension_loaded(
"tidy")) {
5460 "output-xml" =>
true,
5461 "numeric-entities" =>
true
5464 $tidy->parseString($html, $config,
'utf8');
5465 $tidy->cleanRepair();
5466 $html = tidy_get_output($tidy);
5467 $html = preg_replace(
"/^.*?(<html)/",
"\\1", $html);
5469 $html = str_replace(
" ",
" ", $html);
5470 $html = str_replace(
"⊗",
"X", $html);
5472 $html = preg_replace(
"/src=\".\\//ims",
"src=\"" . ILIAS_HTTP_PATH .
"/", $html);
5473 $this->deliverPDFfromFO($this->processPrintoutput2FO($html), $title);
5484 $fp = fopen($fo_file,
"w");
5493 $pdf_base64->scalar,
5498 }
catch (Exception
$e) {
5499 $this->
logger->info(__METHOD__ .
': ' .
$e->getMessage());
5515 if ($pass ===
null) {
5519 $row = self::getSingleManualFeedback((
int) $active_id, (
int) $question_id, (
int) $pass);
5522 $feedback = $row[
'feedback'] ??
'';
5533 $result =
$ilDB->queryF(
5534 "SELECT * FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
5535 [
'integer',
'integer',
'integer'],
5536 [$active_id, $question_id, $pass]
5539 if (
$ilDB->numRows($result) === 1) {
5540 $row =
$ilDB->fetchAssoc($result);
5542 } elseif (
$ilDB->numRows($result) > 1) {
5543 $DIC->logger()->root()->warning(
5544 "WARNING: Multiple feedback entries on tst_manual_fb for " .
5545 "active_fi = $active_id , question_fi = $question_id and pass = $pass"
5565 $result =
$ilDB->queryF(
5566 "SELECT * FROM tst_manual_fb WHERE question_fi = %s",
5571 while ($row =
$ilDB->fetchAssoc($result)) {
5572 $active = $row[
'active_fi'];
5573 $pass = $row[
'pass'];
5574 $question = $row[
'question_fi'];
5578 $feedback[$active][$pass][$question] = $row;
5589 bool $finalized =
false
5591 $feedback_old = self::getSingleManualFeedback($active_id, $question_id, $pass);
5592 $this->db->manipulateF(
5593 'DELETE FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s',
5594 [
'integer',
'integer',
'integer'],
5595 [$active_id, $question_id, $pass]
5598 $this->insertManualFeedback($active_id, $question_id, $pass, $feedback, $finalized, $feedback_old);
5610 $next_id = $this->db->nextId(
'tst_manual_fb');
5611 $user = $this->
user->getId();
5612 $finalized_time = time();
5615 'manual_feedback_id' => [
'integer', $next_id],
5616 'active_fi' => [
'integer', $active_id],
5617 'question_fi' => [
'integer', $question_id],
5618 'pass' => [
'integer', $pass],
5620 'tstamp' => [
'integer', time()]
5623 if ($feedback_old !== [] && (
int) $feedback_old[
'finalized_evaluation'] === 1) {
5624 $user = $feedback_old[
'finalized_by_usr_id'];
5625 $finalized_time = $feedback_old[
'finalized_tstamp'];
5628 if ($finalized ===
false) {
5629 $update_default[
'finalized_evaluation'] = [
'integer', 0];
5630 $update_default[
'finalized_by_usr_id'] = [
'integer', 0];
5631 $update_default[
'finalized_tstamp'] = [
'integer', 0];
5632 } elseif ($finalized ===
true) {
5633 $update_default[
'finalized_evaluation'] = [
'integer', 1];
5634 $update_default[
'finalized_by_usr_id'] = [
'integer', $user];
5635 $update_default[
'finalized_tstamp'] = [
'integer', $finalized_time];
5638 $this->db->insert(
'tst_manual_fb', $update_default);
5640 if ($this->
logger->isLoggingEnabled()) {
5641 $this->
logger->logScoringInteraction(
5642 $this->
logger->getInteractionFactory()->buildScoringInteraction(
5645 $this->user->getId(),
5646 self::_getUserIdFromActiveId($active_id),
5647 TestScoringInteractionTypes::QUESTION_GRADED,
5649 AdditionalInformationGenerator::KEY_EVAL_FINALIZED => $this->logger
5650 ->getAdditionalInformationGenerator()->getTrueFalseTagForBool($finalized),
5672 $this->test_sequence =
new ilTestSequence($active_id, $pass, $this->isRandomTest(), $this->questionrepository);
5682 $this->test_id = $a_id;
5695 if (count($participants)) {
5696 foreach ($participants as $active_id => $user_rec) {
5698 $reached_points = 0;
5701 foreach ($this->questions as $value) {
5703 if (is_object($question)) {
5704 $max_points += $question->getMaximumPoints();
5705 $reached_points += $question->getReachedPoints($active_id, $pass);
5706 if ($max_points > 0) {
5707 $percentvalue = $reached_points / $max_points;
5708 if ($percentvalue < 0) {
5709 $percentvalue = 0.0;
5714 if ($this->getAnonymity()) {
5715 $user_rec[
'firstname'] =
"";
5716 $user_rec[
'lastname'] = $this->
lng->txt(
"anonymous");
5719 "user_id" => $user_rec[
'usr_id'],
5720 "matriculation" => $user_rec[
'matriculation'],
5721 "lastname" => $user_rec[
'lastname'],
5722 "firstname" => $user_rec[
'firstname'],
5723 "login" => $user_rec[
'login'],
5724 "question_id" => $question->getId(),
5725 "question_title" => $question->getTitle(),
5726 "reached_points" => $reached_points,
5727 "max_points" => $max_points,
5728 "passed" => $user_rec[
'passed'] ?
'1' :
'0',
5745 $result =
$ilDB->queryF(
5746 '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',
5750 $rec =
$ilDB->fetchAssoc($result);
5751 return $rec[
'obj_id'] ??
null;
5762 if (!$this->component_repository->getComponentByTypeAndName(
5765 )->getPluginSlotById(
'qst')->hasPluginName($a_pname)) {
5769 return $this->component_repository
5770 ->getComponentByTypeAndName(
5774 ->getPluginSlotById(
5788 SELECT tst_test_result.active_fi, tst_test_result.question_fi, tst_test_result.pass
5789 FROM tst_test_result
5790 INNER JOIN tst_active ON tst_active.active_id = tst_test_result.active_fi AND tst_active.test_fi = %s
5791 INNER JOIN qpl_questions ON qpl_questions.question_id = tst_test_result.question_fi
5792 LEFT JOIN usr_data ON usr_data.usr_id = tst_active.user_fi
5793 WHERE tst_test_result.question_fi = %s
5794 ORDER BY usr_data.lastname ASC, usr_data.firstname ASC
5797 $result = $this->db->queryF(
5799 [
'integer',
'integer'],
5800 [$test_id, $question_id]
5804 while ($row = $this->db->fetchAssoc($result)) {
5805 if ($this->getAccessFilteredParticipantList() && !$this->getAccessFilteredParticipantList()->isActiveIdInList($row[
"active_fi"])) {
5809 if (!array_key_exists($row[
"active_fi"], $foundusers)) {
5810 $foundusers[$row[
"active_fi"]] = [];
5812 array_push($foundusers[$row[
"active_fi"]], [
"pass" => $row[
"pass"],
"qid" => $row[
"question_fi"]]);
5819 $data = $this->getCompleteEvaluationData();
5820 $found_participants =
$data->getParticipants();
5821 $results = [
'overview' => [],
'questions' => []];
5822 if ($found_participants !== []) {
5823 $results[
'overview'][
'tst_stat_result_mark_median'] =
$data->getStatistics()->getEvaluationDataOfMedianUser()?->getMark()?->getShortName() ??
'';
5824 $results[
'overview'][
'tst_stat_result_rank_median'] =
$data->getStatistics()->rankMedian();
5825 $results[
'overview'][
'tst_stat_result_total_participants'] =
$data->getStatistics()->count();
5826 $results[
'overview'][
'tst_stat_result_median'] =
$data->getStatistics()->median();
5827 $results[
'overview'][
'tst_eval_total_persons'] = count($found_participants);
5828 $total_finished =
$data->getTotalFinishedParticipants();
5829 $results[
'overview'][
'tst_eval_total_finished'] = $total_finished;
5830 $results[
'overview'][
'tst_eval_total_finished_average_time'] =
5831 $this->secondsToHoursMinutesSecondsString(
5832 $this->evalTotalStartedAverageTime(
$data->getParticipantIds())
5835 $total_passed_reached = 0;
5836 $total_passed_max = 0;
5837 $total_passed_time = 0;
5838 foreach ($found_participants as $userdata) {
5839 if ($userdata->getMark()?->getPassed()) {
5841 $total_passed_reached += $userdata->getReached();
5842 $total_passed_max += $userdata->getMaxpoints();
5843 $total_passed_time += $userdata->getTimeOnTask();
5846 $average_passed_reached = $total_passed ? $total_passed_reached / $total_passed : 0;
5847 $average_passed_max = $total_passed ? $total_passed_max / $total_passed : 0;
5848 $average_passed_time = $total_passed ? $total_passed_time / $total_passed : 0;
5849 $results[
'overview'][
'tst_eval_total_passed'] = $total_passed;
5850 $results[
'overview'][
'tst_eval_total_passed_average_points'] = sprintf(
'%2.2f', $average_passed_reached)
5851 .
' ' . strtolower(
'of') .
' ' . sprintf(
'%2.2f', $average_passed_max);
5852 $results[
'overview'][
'tst_eval_total_passed_average_time'] =
5853 $this->secondsToHoursMinutesSecondsString((
int) $average_passed_time);
5856 foreach (
$data->getQuestionTitles() as $question_id => $question_title) {
5860 foreach ($found_participants as $userdata) {
5861 for ($i = 0; $i <= $userdata->getLastPass(); $i++) {
5862 if (is_object($userdata->getPass($i))) {
5863 $question = $userdata->getPass($i)->getAnsweredQuestionByQuestionId($question_id);
5864 if (is_array($question)) {
5866 $reached += $question[
'reached'];
5867 $max += $question[
'points'];
5872 $percent = $max ? $reached / $max * 100.0 : 0;
5873 $results[
'questions'][$question_id] = [
5875 sprintf(
'%.2f', $answered ? $reached / $answered : 0) .
' ' . strtolower($this->
lng->txt(
'of')) .
' ' . sprintf(
'%.2f', $answered ? $max / $answered : 0),
5876 sprintf(
'%.2f', $percent) .
'%',
5878 sprintf(
'%.2f', $answered ? $reached / $answered : 0),
5879 sprintf(
'%.2f', $answered ? $max / $answered : 0),
5888 $diff_hours = floor($seconds / 3600);
5889 $seconds -= $diff_hours * 3600;
5890 $diff_minutes = floor($seconds / 60);
5891 $seconds -= $diff_minutes * 60;
5892 return sprintf(
'%02d:%02d:%02d', $diff_hours, $diff_minutes, $seconds);
5900 return $this->export_factory->getExporter($this,
'xml')
5906 return $this->getScoreSettings()->getResultDetailsSettings()->getExportSettings();
5911 $this->template_id = $template_id;
5916 return $this->template_id;
5921 $question_set_config = $this->question_set_config_factory->getQuestionSetConfig();
5922 $reindexed_sequence_position_map = $question_set_config->reindexQuestionOrdering();
5924 $this->loadQuestions();
5926 return $reindexed_sequence_position_map;
5935 foreach (array_keys($order) as
$id) {
5939 UPDATE tst_test_question
5941 WHERE question_fi = %s
5944 $this->db->manipulateF(
5946 [
'integer',
'integer'],
5951 if ($this->
logger->isLoggingEnabled()) {
5952 $this->
logger->logTestAdministrationInteraction(
5953 $this->
logger->getInteractionFactory()->buildTestAdministrationInteraction(
5955 $this->user->getId(),
5956 TestAdministrationInteractionTypes::QUESTION_MOVED,
5958 AdditionalInformationGenerator::KEY_QUESTION_ORDER => $order
5964 $this->loadQuestions();
5969 $questions = $this->getQuestionTitlesAndIndexes();
5971 $IN_questions = $this->db->in(
'q1.question_id', array_keys($questions),
false,
'integer');
5974 SELECT count(q1.question_id) cnt
5976 FROM qpl_questions q1
5978 INNER JOIN qpl_questions q2
5979 ON q2.question_id = q1.original_id
5982 AND q1.obj_fi = q2.obj_fi
5984 $rset = $this->db->query($query);
5985 $row = $this->db->fetchAssoc($rset);
5987 return $row[
'cnt'] > 0;
6001 $result =
$ilDB->queryF(
6002 "SELECT test_fi,MAX(pass) AS pass FROM tst_active" .
6003 " JOIN tst_pass_result ON (tst_pass_result.active_fi = tst_active.active_id)" .
6004 " WHERE user_fi=%s" .
6005 " GROUP BY test_fi",
6006 [
'integer',
'integer'],
6010 while ($row =
$ilDB->fetchAssoc($result)) {
6011 $obj_id = self::_getObjectIDFromTestID($row[
"test_fi"]);
6012 $all[$obj_id] = (bool) $row[
"pass"];
6019 return $this->questions;
6024 return $this->online;
6029 $page_id = $this->getMainSettings()->getIntroductionSettings()->getIntroductionPageId();
6030 if ($page_id !==
null) {
6035 $page_object->setParentId($this->
getId());
6036 $new_page_id = $page_object->createPageWithNextId();
6037 $settings = $this->getMainSettings()->getIntroductionSettings()
6038 ->withIntroductionPageId($new_page_id);
6039 $this->getMainSettingsRepository()->store(
6040 $this->getMainSettings()->withIntroductionSettings($settings)
6042 return $new_page_id;
6047 $page_id = $this->getMainSettings()->getFinishingSettings()->getConcludingRemarksPageId();
6048 if ($page_id !==
null) {
6053 $page_object->setParentId($this->
getId());
6054 $new_page_id = $page_object->createPageWithNextId();
6055 $settings = $this->getMainSettings()->getFinishingSettings()
6056 ->withConcludingRemarksPageId($new_page_id);
6057 $this->getMainSettingsRepository()->store(
6058 $this->getMainSettings()->withFinishingSettings($settings)
6060 return $new_page_id;
6065 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreEnabled();
6079 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreAnon();
6092 return $this->getAnonymity() == 1 || $this->getHighscoreAnon();
6100 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreAchievedTS();
6108 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreScore();
6116 return $this->getScoreSettings()->getGamificationSettings()->getHighscorePercentage();
6124 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreWTime();
6132 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreOwnTable();
6140 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreTopTable();
6149 return $this->getScoreSettings()->getGamificationSettings()->getHighscoreTopNum();
6154 return $this->getScoreSettings()->getGamificationSettings()->getHighScoreMode();
6159 return $this->getMainSettings()->getQuestionBehaviourSettings()->getInstantFeedbackSpecificEnabled();
6164 return $this->getMainSettings()->getQuestionBehaviourSettings()->getAutosaveEnabled();
6169 return $this->getScoreSettings()->getResultSummarySettings()->getPassDeletionAllowed();
6174 return $this->getMainSettings()->getFinishingSettings()->getShowAnswerOverview();
6186 $result = $this->db->queryF(
6187 "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",
6189 [$this->getTestId()]
6191 while ($row = $this->db->fetchAssoc($result)) {
6192 $times[$row[
'active_fi']] = $row[
'started'];
6200 $result = $this->db->queryF(
6201 "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",
6203 [$this->getTestId()]
6205 while ($row = $this->db->fetchAssoc($result)) {
6206 $times[$row[
'active_fi']] = $row[
'additionaltime'];
6213 if ($active_id === 0) {
6216 return $this->participant_repository
6217 ->getParticipantByActiveId($this->getTestId(), $active_id)
6218 ?->getExtraTime() ?? 0;
6224 SELECT MAX(tst_pass_result.pass) + 1 max_res
6225 FROM tst_pass_result
6226 INNER JOIN tst_active ON tst_active.active_id = tst_pass_result.active_fi
6227 WHERE test_fi = ' . $this->db->quote($this->getTestId(),
'integer') .
'
6229 $res = $this->db->query($query);
6231 return (
int)
$data[
'max_res'];
6239 $exam_id_query =
'SELECT exam_id FROM tst_pass_result WHERE active_fi = %s AND pass = %s';
6240 $exam_id_result =
$ilDB->queryF($exam_id_query, [
'integer',
'integer' ], [ $active_id, $pass ]);
6241 if (
$ilDB->numRows($exam_id_result) == 1) {
6242 $exam_id_row =
$ilDB->fetchAssoc($exam_id_result);
6244 if ($exam_id_row[
'exam_id'] !=
null) {
6245 return $exam_id_row[
'exam_id'];
6252 public static function buildExamId($active_id, $pass, $test_obj_id =
null): string
6259 if ($test_obj_id ===
null) {
6260 $obj_id = self::_getObjectIDFromActiveID($active_id);
6262 $obj_id = $test_obj_id;
6265 $examId =
'I' . $inst_id .
'_T' . $obj_id .
'_A' . $active_id .
'_P' . $pass;
6272 return $this->getMainSettings()->getTestBehaviourSettings()->getExamIdInTestAttemptEnabled();
6277 return $this->getScoreSettings()->getResultDetailsSettings()->getShowExamIdInTestResults();
6283 $this->main_settings = $this->getMainSettings()->withGeneralSettings(
6285 ->withQuestionSetType($question_set_type)
6291 return $this->getMainSettings()->getGeneralSettings()->getQuestionSetType();
6301 return $this->getQuestionSetType() == self::QUESTION_SET_TYPE_FIXED;
6311 return $this->getQuestionSetType() == self::QUESTION_SET_TYPE_RANDOM;
6316 switch ($questionSetType) {
6318 return $lng->txt(
'tst_question_set_type_fixed');
6321 return $lng->txt(
'tst_question_set_type_random');
6324 throw new ilTestException(
'invalid question set type value given: ' . $questionSetType);
6329 if ($this->participantDataExist ===
null) {
6330 $this->participantDataExist = (bool) $this->evalTotalPersons();
6333 return $this->participantDataExist;
6342 $this->test_result_repository
6344 $scoring->setPreserveManualScores($preserve_manscoring);
6345 $scoring->recalculateSolutions();
6356 INNER JOIN tst_tests
6357 ON test_id = test_fi
6361 $res =
$ilDB->queryF($query, [
'integer'], [$userId]);
6365 while ($row =
$ilDB->fetchAssoc(
$res)) {
6366 $objIds[] = (
int) $row[
'obj_fi'];
6374 return $this->getMainSettings()->getAdditionalSettings()->getSkillsServiceEnabled();
6390 if (!$this->getMainSettings()->getAdditionalSettings()->getSkillsServiceEnabled()) {
6394 if (!self::isSkillManagementGloballyActivated()) {
6401 private static $isSkillManagementGloballyActivated =
null;
6405 if (self::$isSkillManagementGloballyActivated ===
null) {
6408 self::$isSkillManagementGloballyActivated = $skmgSet->isActivated();
6411 return self::$isSkillManagementGloballyActivated;
6416 return $this->getScoreSettings()->getResultSummarySettings()->getShowGradingStatusEnabled();
6421 return $this->getScoreSettings()->getResultSummarySettings()->getShowGradingMarkEnabled();
6426 return $this->getMainSettings()->getQuestionBehaviourSettings()->getLockAnswerOnNextQuestionEnabled();
6431 return $this->getMainSettings()->getQuestionBehaviourSettings()->getLockAnswerOnInstantFeedbackEnabled();
6436 return $this->getMainSettings()->getQuestionBehaviourSettings()->getForceInstantFeedbackOnNextQuestion();
6444 $ilUser =
$DIC[
'ilUser'];
6448 $active_id = $test_obj->getActiveIdOfUser(
$user_id);
6453 $test_session_factory->reset();
6455 $test_sequence_factory =
new ilTestSequenceFactory($test_obj,
$ilDB, TestDIC::dic()[
'question.general_properties.repository']);
6457 $test_session = $test_session_factory->getSession($active_id);
6458 $test_sequence = $test_sequence_factory->getSequenceByActiveIdAndPass($active_id, $test_session->getPass());
6459 $test_sequence->loadFromDb();
6461 return $test_sequence->hasSequence();
6467 SELECT COUNT(test_question_id) cnt
6468 FROM tst_test_question
6473 $questRes = $this->db->queryF($query, [
'integer'], [$this->getTestId()]);
6475 $row = $this->db->fetchAssoc($questRes);
6476 $questCount = $row[
'cnt'];
6478 if ($this->getShuffleQuestions()) {
6482 INNER JOIN tst_sequence tseq
6483 ON tseq.active_fi = tac.active_id
6484 WHERE tac.test_fi = %s
6487 $partRes = $this->db->queryF(
6490 [$this->getTestId()]
6493 while ($row = $this->db->fetchAssoc($partRes)) {
6494 $sequence = @unserialize($row[
'sequence']);
6500 $sequence = array_filter($sequence,
function ($value) use ($questCount) {
6501 return $value <= $questCount;
6504 $num_seq = count($sequence);
6505 if ($questCount > $num_seq) {
6506 $diff = $questCount - $num_seq;
6507 for ($i = 1; $i <= $diff; $i++) {
6508 $sequence[$num_seq + $i - 1] = $num_seq + $i;
6512 $new_sequence = serialize($sequence);
6514 $this->db->update(
'tst_sequence', [
6515 'sequence' => [
'clob', $new_sequence]
6517 'active_fi' => [
'integer', $row[
'active_fi']],
6518 'pass' => [
'integer', $row[
'pass']]
6522 $new_sequence = serialize($questCount > 0 ? range(1, $questCount) : []);
6527 INNER JOIN tst_sequence tseq
6528 ON tseq.active_fi = tac.active_id
6529 WHERE tac.test_fi = %s
6532 $part_rest = $this->db->queryF(
6535 [$this->getTestId()]
6538 while ($row = $this->db->fetchAssoc($part_rest)) {
6539 $this->db->update(
'tst_sequence', [
6540 'sequence' => [
'clob', $new_sequence]
6542 'active_fi' => [
'integer', $row[
'active_fi']],
6543 'pass' => [
'integer', $row[
'pass']]
6559 return $this->questionrepository;
6564 return $this->global_settings_repo->getGlobalSettings();
6569 if (!$this->main_settings) {
6570 $this->main_settings = $this->getMainSettingsRepository()
6571 ->getFor($this->getTestId());
6573 return $this->main_settings;
6578 return $this->main_settings_repository;
6583 if (!$this->score_settings) {
6584 $this->score_settings = $this->getScoreSettingsRepository()
6585 ->getFor($this->getTestId());
6587 return $this->score_settings;
6592 return $this->score_settings_repository;
6596 bool $old_online_status,
6597 bool $new_online_status
6599 if (!$old_online_status && $new_online_status) {
6601 $newsItem->setContext($this->
getId(),
'tst');
6603 $newsItem->setTitle(
'new_test_online');
6604 $newsItem->setContentIsLangVar(
true);
6605 $newsItem->setContent(
'');
6606 $newsItem->setUserId($this->
user->getId());
6608 $newsItem->create();
6612 if ($old_online_status && !$new_online_status) {
6618 if (!$new_online_status && $newsId > 0) {
6620 $newsItem->setTitle(
'new_test_online');
6621 $newsItem->setContentIsLangVar(
true);
6622 $newsItem->setContent(
'');
6623 $newsItem->update();
6632 return TestDIC::dic()[
'settings.main.repository']->getFor(
6634 )->getGeneralSettings()->getQuestionSetType() === self::QUESTION_SET_TYPE_RANDOM;
6639 return $this->participant_repository->getFirstAndLastVisitForActiveId($active_id);
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
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.
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.
exportXMLMediaObjects(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
export media objects to xml (see ilias_co.dtd)
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 $a_package, int $a_timeout=0)
Creates an ilRpcClient instance to our ilServer.
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'))