ILIAS  trunk Revision v12.0_alpha-1540-g00f839d5fa1
class.ilTestEvaluationGUI.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22use ILIAS\Test\Results\Presentation\TitlesBuilder as ResultsTitlesBuilder;
25use ILIAS\UI\Component\ViewControl\Mode as ViewControlMode;
26use ILIAS\UI\Component\Link\Standard as StandardLink;
27use ILIAS\UI\Component\Panel\Sub as SubPanel;
29
49{
50 private const DEFAULT_CMD = 'outUserListOfAnswerPasses';
54
55 public function __construct(ilObjTest $object)
56 {
57 parent::__construct($object);
58 $this->participant_access_filter = new ilTestParticipantAccessFilterFactory($this->access);
59 $this->participant_repository = TestDIC::dic()['participant.repository'];
60
61 $this->processLockerFactory = new ilTestProcessLockerFactory(
62 new ilSetting('assessment'),
63 $this->db,
64 $this->object->getTestLogger()
65 );
66 }
67
68 public function getTestAccess(): ilTestAccess
69 {
70 return $this->testAccess;
71 }
72
73 public function setTestAccess($testAccess): void
74 {
75 $this->testAccess = $testAccess;
76 }
77
78 public function executeCommand()
79 {
80 $cmd = $this->ctrl->getCmd(self::DEFAULT_CMD);
81 $next_class = $this->ctrl->getNextClass($this);
82 $this->ctrl->saveParameter($this, 'sequence');
83 $this->ctrl->saveParameter($this, 'active_id');
84
85 switch ($next_class) {
86 case 'iltestpassdetailsoverviewtablegui':
87 $tableGUI = new ilTestPassDetailsOverviewTableGUI($this->ctrl, $this, 'outUserPassDetails');
88 $this->ctrl->forwardCommand($tableGUI);
89 break;
90
91 default:
92 if (in_array($cmd, ['excel_scored_test_run', 'excel_all_test_runs'])) {
93 $ret = $this->exportEvaluation($cmd);
94 } elseif (in_array($cmd, ['excel_all_test_runs_a'])) {
95 $ret = $this->exportAggregatedResults($cmd);
96 } else {
97 $ret = $this->$cmd();
98 }
99 break;
100 }
101 return $ret;
102 }
103
110 public function getEvaluationQuestionId($question_id, $original_id = '')
111 {
112 if ($original_id > 0) {
113 return $original_id;
114 } else {
115 return $question_id;
116 }
117 }
118
119 protected function setCss(): void
120 {
121 $this->tpl->addCss(ilUtil::getStyleSheetLocation('output', 'test_print.css'), 'print');
122 if ($this->object->getShowSolutionAnswersOnly()) {
123 $this->tpl->addCss(ilUtil::getStyleSheetLocation('output', 'test_print_hide_content.css'), 'print');
124 }
125 $this->tpl->addCss(ilObjStyleSheet::getContentStylePath(0));
126 }
127
128 public function printResults(): void
129 {
130 $this->ctrl->saveParameterByClass(self::class, 'active_ids');
131 $this->global_screen->tool()->context()->current()->addAdditionalData(
132 PrintLayoutProvider::TEST_CONTEXT_PRINT,
133 true
134 );
135
136 $selected_active_ids = explode(',', $this->testrequest->strVal('active_ids'));
137 $results_panel = $this->ui_factory->panel()->report(
138 $this->lng->txt('tst_results'),
139 array_map(
140 function (string $v): SubPanel {
141 $value = (int) $v;
142 $attempt_id = ilObjTest::_getResultPass($value);
143 $components = $this->buildAttemptComponents($value, $attempt_id, false, true);
144 return $this->ui_factory->panel()->sub(
145 $this->buildResultsTitle($value, $attempt_id),
147 );
148 },
149 $selected_active_ids
150 )
151 );
152
153 $this->tpl->setVariable(
154 'ADM_CONTENT',
155 $this->ui_renderer->render([
156 $results_panel,
157 $this->ui_factory->legacy()->content('')->withAdditionalOnLoadCode(
158 fn(string $id): string => 'setTimeout(() => {window.print();}, 50)'
159 )
160 ])
161 );
162 }
163
164 public function showResults(): void
165 {
166 $this->setCss();
167 $this->ctrl->saveParameterByClass(self::class, 'active_ids');
168 $selected_active_ids = explode(',', $this->testrequest->strVal('active_ids'));
169
172
173 $current_active_id = (int) $selected_active_ids[0];
174 if (count($selected_active_ids) > 1
175 && ($selected_active_id = $this->testrequest->getActiveId()) > 0
176 && array_search($selected_active_id, $selected_active_ids) !== false) {
177 $current_active_id = $selected_active_id;
178 }
179
180 if ($this->testrequest->isset('attempt')) {
181 $attempt_id = min($this->testrequest->int('attempt'), ilObjTest::_getMaxPass($current_active_id));
182 } else {
183 $attempt_id = ilObjTest::_getResultPass($current_active_id);
184 }
185
186 $results_panel = $this->ui_factory->panel()->report(
187 $this->buildResultsTitle($current_active_id, $attempt_id),
188 $this->buildAttemptComponents($current_active_id, $attempt_id, true, false)
189 );
190
191 $attempts_ids_array = $this->results_data_factory->getAttemptIdsArrayFor(
192 $this->object,
193 $current_active_id
194 );
195
196 if (count($attempts_ids_array) > 1) {
197 $results_panel = $results_panel->withViewControls([
199 $attempts_ids_array,
200 ++$attempt_id
201 )
202 ]);
203 }
204
205 if (count($selected_active_ids) > 1) {
206 $this->addParticipantSelectorToToolbar($selected_active_ids, $current_active_id);
207 }
208
209 $this->tpl->setVariable(
210 'ADM_CONTENT',
211 $this->ui_renderer->render($results_panel)
212 );
213
214 $this->tabs->setBackTarget(
215 $this->lng->txt('back'),
216 $this->ctrl->getLinkTargetByClass(['ilTestParticipantsGUI'])
217 );
218 }
219
220 public function outUserPassDetails(): void
221 {
222 $this->tabs->clearSubTabs();
223 $this->tabs->setBackTarget($this->lng->txt('tst_results_back_overview'), $this->ctrl->getLinkTarget($this));
224
225 $test_session = $this->test_session_factory->getSession();
226
227 if (!$this->object->getShowPassDetails()) {
228 $this->ctrl->redirectByClass([ilRepositoryGUI::class, ilObjTestGUI::class, ilInfoScreenGUI::class]);
229 }
230
231 $active_id = $test_session->getActiveId();
232 $user_id = $test_session->getUserId();
233
234 $this->ctrl->saveParameter($this, 'pass');
235 $pass = $this->testrequest->int('pass');
236
237 $test_result_header_label_builder = new ResultsTitlesBuilder($this->lng, $this->obj_cache);
238
239 $objectives_list = null;
240
241 $consider_hidden_questions = true;
242 $consider_optional_questions = true;
243
244 if ($this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()) {
245 $consider_hidden_questions = false;
246 $consider_optional_questions = true;
247
248 $test_sequence = $this->test_sequence_factory->getSequenceByActiveIdAndPass($active_id, $pass);
249 $test_sequence->loadFromDb();
250 $test_sequence->loadQuestions();
251
252 if ($this->object->isRandomTest() && !$test_sequence->isAnsweringOptionalQuestionsConfirmed()) {
253 $consider_optional_questions = false;
254 }
255
256 $objectivesAdapter = ilLOTestQuestionAdapter::getInstance($test_session);
257
258 $objectives_list = $this->buildQuestionRelatedObjectivesList($objectivesAdapter, $test_sequence);
259 $objectives_list->loadObjectivesTitles();
260
261 $test_result_header_label_builder->setObjectiveOrientedContainerId($test_session->getObjectiveOrientedContainerId());
262 $test_result_header_label_builder->setUserId($test_session->getUserId());
263 $test_result_header_label_builder->setTestObjId($this->object->getId());
264 $test_result_header_label_builder->setTestRefId($this->object->getRefId());
265 $test_result_header_label_builder->initObjectiveOrientedMode();
266 }
267
268 $tpl = new ilTemplate('tpl.il_as_tst_pass_details_overview_participants.html', true, true, 'components/ILIAS/Test');
269
271
272 if ((new ilCertificateDownloadValidator())->isCertificateDownloadable($user_id, $this->object->getId())) {
274 }
275
276 $tpl->setCurrentBlock('signature');
277 $tpl->setVariable('SIGNATURE', $this->getResultsSignature());
279
280 if ($this->object->isShowExamIdInTestResultsEnabled()) {
281 if ($this->object->isShowExamIdInTestResultsEnabled()) {
283 $test_session->getActiveId(),
284 $pass
285 ));
286 $tpl->setVariable('EXAM_ID_TXT', $this->lng->txt('exam_id'));
287 }
288 }
289
290 if (!$this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired() &&
291 $this->isGradingMessageRequired() && $this->object->getNrOfTries() == 1) {
292 $grading_message_builder = $this->getGradingMessageBuilder($active_id);
293 $grading_message_builder->buildMessage();
294 $grading_message_builder->sendMessage();
295 }
296
297 $data = $this->object->getCompleteEvaluationData();
298 $reached = $data->getParticipant($active_id)->getPass($pass)->getReachedPoints();
299 $max = $data->getParticipant($active_id)->getPass($pass)->getMaxPoints();
300 $percent = $max ? $reached / $max * 100.0 : 0;
301 $result = $data->getParticipant($active_id)->getPass($pass)->getReachedPoints() . ' ' . strtolower($this->lng->txt('of')) . ' ' . $data->getParticipant($active_id)->getPass($pass)->getMaxPoints() . ' (' . sprintf('%2.2f', $percent) . ' %' . ')';
302 $tpl->setCurrentBlock('total_score');
303 $tpl->setVariable('TOTAL_RESULT_TEXT', $this->lng->txt('tst_stat_result_resultspoints'));
304 $tpl->setVariable('TOTAL_RESULT', $result);
306
307 $tpl->setVariable('TEXT_RESULTS', $test_result_header_label_builder->getPassDetailsHeaderLabel($pass + 1));
308 $tpl->setVariable('FORMACTION', $this->ctrl->getFormAction($this));
309
310 $this->populateExamId($tpl, $active_id, (int) $pass);
311 $this->populatePassFinishDate($tpl, ilObjTest::lookupLastTestPassAccess($active_id, $pass));
312
313 $this->setCss();
314
315 $settings = $this->results_presentation_factory->getAttemptResultsSettings(
316 $this->object,
317 true
318 );
319 $table = $this->results_presentation_factory->getAttemptResultsPresentationTable(
320 $this->results_data_factory->getAttemptResultsFor(
321 $settings,
322 $this->object,
323 $active_id,
324 $pass,
325 true
326 ),
327 $settings,
328 $this->buildResultsTitle($active_id, $pass),
329 false
330 );
331
332 $tpl->setVariable('LIST_OF_ANSWERS', $table->render());
333
334 $this->tpl->addCss(ilObjStyleSheet::getContentStylePath(0));
335
336 $this->tpl->setContent(
337 $tpl->get()
338 );
339 }
340
341 public function outUserResultsOverview()
342 {
343 $test_session = $this->test_session_factory->getSession();
344 $active_id = $test_session->getActiveId();
345 $user_id = $this->user->getId();
346
347 if (!$this->object->canShowTestResults($test_session)) {
348 $this->ctrl->redirectByClass([ilRepositoryGUI::class, ilObjTestGUI::class, ilInfoScreenGUI::class]);
349 }
350
351 $templatehead = new ilTemplate('tpl.il_as_tst_results_participants.html', true, true, 'components/ILIAS/Test');
352 $template = new ilTemplate('tpl.il_as_tst_results_participant.html', true, true, 'components/ILIAS/Test');
353
355
356 if ((new ilCertificateDownloadValidator())->isCertificateDownloadable($user_id, $this->object->getId())) {
358 }
359
360 $test_result_header_label_builder = new ResultsTitlesBuilder($this->lng, $this->obj_cache);
361 if ($this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()) {
362 $test_result_header_label_builder->setObjectiveOrientedContainerId($test_session->getObjectiveOrientedContainerId());
363 $test_result_header_label_builder->setUserId($test_session->getUserId());
364 $test_result_header_label_builder->setTestObjId($this->object->getId());
365 $test_result_header_label_builder->setTestRefId($this->object->getRefId());
366 $test_result_header_label_builder->initObjectiveOrientedMode();
367 }
368
369 $template->setCurrentBlock('pass_overview');
370
371 $test_passes_selector = new ilTestPassesSelector($this->db, $this->object);
372 $test_passes_selector->setActiveId($test_session->getActiveId());
373 $test_passes_selector->setLastFinishedPass($test_session->getLastFinishedPass());
374
375 $pass_overview_table_gui = $this->buildPassOverviewTableGUI($this);
376 $pass_overview_table_gui->setActiveId($test_session->getActiveId());
377 $pass_overview_table_gui->setResultPresentationEnabled(true);
378 if ($this->object->getShowPassDetails()) {
379 $pass_overview_table_gui->setPassDetailsCommand('outUserPassDetails');
380 }
381 if ($this->object->isPassDeletionAllowed()) {
382 $pass_overview_table_gui->setPassDeletionCommand('confirmDeletePass');
383 }
384 $pass_overview_table_gui->init();
385 $pass_overview_table_gui->setData($this->getPassOverviewTableData($test_session, $test_passes_selector->getReportablePasses(), true));
386 $pass_overview_table_gui->setTitle($test_result_header_label_builder->getPassOverviewHeaderLabel());
387 $overview = $pass_overview_table_gui->getHTML();
388 if ($this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()) {
389 $lo_status = new ilTestLearningObjectivesStatusGUI(
390 $this->lng,
391 $this->ctrl,
392 $this->ui_factory,
393 $this->ui_renderer,
394 $this->testrequest
395 );
396 $lo_status->setCrsObjId($this->getObjectiveOrientedContainer()->getObjId());
397 $lo_status->setUsrId($test_session->getUserId());
398 $overview .= '<br />' . $lo_status->getHTML();
399 }
400 $template->setVariable('PASS_OVERVIEW', $overview);
401 $template->parseCurrentBlock();
402
403 if ($this->isGradingMessageRequired()) {
404 $grading_message_builder = $this->getGradingMessageBuilder($active_id);
405 $grading_message_builder->buildMessage();
406 $grading_message_builder->sendMessage();
407 }
408
409 if (!$this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()) {
410 $overwrite_anonymity = $test_session->getUserId() === $user_id;
411 if (!$overwrite_anonymity && $this->object->getAnonymity()) {
412 $template->setVariable('TEXT_HEADING', $this->lng->txt('tst_result'));
413 } else {
414 $template->setVariable(
415 'TEXT_HEADING',
416 sprintf(
417 $this->lng->txt('tst_result_user_name'),
418 $this->object->userLookupFullName($user_id, $overwrite_anonymity)
419 )
420 );
421 $template->setVariable(
422 'USER_DATA',
423 $this->getAdditionalUsrDataHtmlAndPopulateWindowTitle($active_id, $overwrite_anonymity)
424 );
425 }
426 }
427
428 $this->setCss();
429 $templatehead->setVariable('RESULTS_PARTICIPANT', $template->get());
430 $this->tpl->setContent($templatehead->get());
431 }
432
434 {
435 if (!$this->object->getShowSolutionPrintview()) {
436 $this->tpl->setOnScreenMessage('info', $this->lng->txt('no_permission'), true);
437 $this->ctrl->redirectByClass([ilRepositoryGUI::class, ilObjTestGUI::class, ilInfoScreenGUI::class]);
438 }
439
440 $template = new ilTemplate('tpl.il_as_tst_info_list_of_answers.html', true, true, 'components/ILIAS/Test');
441
442 $user_id = $this->user->getId();
443
444 $test_session = $this->test_session_factory->getSession();
445 $active_id = $test_session->getActiveId();
446
447 if ($this->testrequest->isset('attempt')) {
448 $attempt = $this->testrequest->int('attempt');
449 } else {
450 $attempt = \ilObjTest::_getResultPass($test_session->getActiveId());
451 }
452
453 $test_passes_selector = new ilTestPassesSelector($this->db, $this->object);
454 $test_passes_selector->setActiveId($test_session->getActiveId());
455 $test_passes_selector->setLastFinishedPass($test_session->getLastFinishedPass());
456
457 if (count($test_passes_selector->getClosedPasses()) > 1) {
458 $this->addAttemptSwitchingViewControlToToolbar($test_passes_selector->getClosedPasses(), $attempt);
459 }
460
462
463 $test_result_header_label_builder = new ResultsTitlesBuilder($this->lng, $this->obj_cache);
464 $test_result_header_label_builder->setAttemptLastAccessDate(
465 (new \DateTimeImmutable(
466 '@' . ilObjTest::lookupLastTestPassAccess($test_session->getActiveId(), $attempt)
467 ))->setTimezone(new \DateTimeZone($this->user->getTimeZone()))
468 ->format($this->user->getDateTimeFormat()->toString())
469 );
470
471 $objectives_list = null;
472 if ($this->getObjectiveOrientedContainer()?->isObjectiveOrientedPresentationRequired()) {
473 $test_sequence = $this->test_sequence_factory->getSequenceByActiveIdAndPass($active_id, $attempt);
474 $test_sequence->loadFromDb();
475 $test_sequence->loadQuestions();
476
477 $objectivesAdapter = ilLOTestQuestionAdapter::getInstance($test_session);
478
479 $objectives_list = $this->buildQuestionRelatedObjectivesList($objectivesAdapter, $test_sequence);
480 $objectives_list->loadObjectivesTitles();
481
482 $test_result_header_label_builder->setObjectiveOrientedContainerId($test_session->getObjectiveOrientedContainerId());
483 $test_result_header_label_builder->setUserId($test_session->getUserId());
484 $test_result_header_label_builder->setTestObjId($this->object->getId());
485 $test_result_header_label_builder->setTestRefId($this->object->getRefId());
486 $test_result_header_label_builder->initObjectiveOrientedMode();
487 }
488
489 $result_array = $this->object->getTestResult(
490 $active_id,
491 $attempt,
492 false,
493 !$this->getObjectiveOrientedContainer()?->isObjectiveOrientedPresentationRequired()
494 );
495
496 $signature = $this->getResultsSignature();
497 $show_all_answers = true;
498 if ($this->object->isExecutable($test_session, $user_id)) {
499 $show_all_answers = false;
500 }
501 $this->setContextResultPresentation(false);
502 $answers = $this->getPassListOfAnswers(
503 $result_array,
504 $active_id,
505 $attempt,
506 false,
507 $show_all_answers,
508 false,
509 false,
510 false,
511 $objectives_list,
512 $test_result_header_label_builder
513 );
514 $template->setVariable('PASS_DETAILS', $answers);
515
517 $active_id,
518 $test_session->getUserId() === $user_id
519 );
520 $template->setVariable('USER_DATA', $user_data);
521 if (strlen($signature)) {
522 $template->setVariable('SIGNATURE', $signature);
523 }
524 if (!is_null($attempt) && $this->object->isShowExamIdInTestResultsEnabled()) {
525 $template->setCurrentBlock('exam_id_footer');
526 $template->setVariable('EXAM_ID_VAL', ilObjTest::lookupExamId(
527 $test_session->getActiveId(),
528 $attempt
529 ));
530 $template->setVariable('EXAM_ID_TXT', $this->lng->txt('exam_id'));
531 $template->parseCurrentBlock();
532 }
533
534 $this->setCss();
535 $this->tpl->setVariable('ADM_CONTENT', $template->get());
536 }
537
538 public function outCertificate()
539 {
540 $ilUserCertificateRepository = new ilUserCertificateRepository($this->db, $this->logger->getComponentLogger());
541 $pdfGenerator = new ilPdfGenerator($ilUserCertificateRepository);
542
543 $pdfAction = new ilCertificatePdfAction(
544 $pdfGenerator,
546 $this->lng->txt('error_creating_certificate_pdf')
547 );
548
549 $pdfAction->downloadPdf($this->user->getId(), $this->object->getId());
550 }
551
552 public function confirmDeletePass()
553 {
554 if ($this->testrequest->isset('context') && strlen($this->testrequest->raw('context'))) {
555 $context = $this->testrequest->raw('context');
556 } else {
558 }
559
560 if (!$this->object->isPassDeletionAllowed()) {
561 $this->redirectToPassDeletionContext($context);
562 }
563
564 $confirm = new ilTestPassDeletionConfirmationGUI($this->ctrl, $this->lng, $this);
565 $confirm->build($this->testrequest->getActiveId('active_id'), $this->testrequest->int('pass'), $context);
566
567 $this->tpl->setContent($this->ctrl->getHTML($confirm));
568 }
569
570 public function cancelDeletePass(): void
571 {
572 $this->redirectToPassDeletionContext($this->testrequest->strVal('context'));
573 }
574
575 private function redirectToPassDeletionContext(string $context): void
576 {
577 switch ($context) {
579
580 $this->ctrl->redirect($this);
581
582 // no break
584 $this->ctrl->redirectByClass([ilRepositoryGUI::class, ilObjTestGUI::class, ilInfoScreenGUI::class]);
585 }
586 }
587
588 public function performDeletePass(): void
589 {
590 $context = $this->testrequest->strVal('context') ?? ilTestPassDeletionConfirmationGUI::CONTEXT_PASS_OVERVIEW;
591 $active_fi = $this->testrequest->int('active_id');
592 $pass = $this->testrequest->int('pass');
593
594 if (!$this->object->isPassDeletionAllowed()) {
595 $this->redirectToPassDeletionContext($context);
596 }
597
599
600 if ($active_fi === 0 || !$this->testrequest->isset('pass')) {
601 $this->ctrl->redirect($this, 'outUserResultsOverview');
602 }
603
604 if ($pass === ilObjTest::_getResultPass($active_fi)) {
605 $this->ctrl->redirect($this, 'outUserResultsOverview');
606 }
607
608 // Get information
609 $result = $ilDB->query("
610 SELECT tst_active.tries, tst_active.last_finished_pass, tst_sequence.pass
611 FROM tst_active
612 LEFT JOIN tst_sequence
613 ON tst_sequence.active_fi = tst_active.active_id
614 AND tst_sequence.pass = tst_active.tries
615 WHERE tst_active.active_id = {$ilDB->quote($active_fi, 'integer')}
616 ");
617
618 $row = $ilDB->fetchAssoc($result);
619
620 $tries = $row['tries'];
621 $lastFinishedPass = is_numeric($row['last_finished_pass']) ? $row['last_finished_pass'] : -1;
622
623 if ($pass < $lastFinishedPass) {
624 $isActivePass = false;
625 $must_renumber = true;
626 } elseif ($pass == $lastFinishedPass) {
627 $isActivePass = false;
628
629 if ($tries == $row['pass']) {
630 $must_renumber = true;
631 } else {
632 $must_renumber = false;
633 }
634 } elseif ($pass == $row['pass']) {
635 $isActivePass = true;
636 $must_renumber = false;
637 } else {
638 throw new ilTestException('This should not happen, please contact Bjoern Heyser to clean up this pass salad!');
639 }
640
641 if ($isActivePass) {
642 $this->ctrl->redirect($this, 'outUserResultsOverview');
643 }
644
645 if ($pass == 0 && (
646 ($lastFinishedPass == 0 && $tries == 1 && $tries != $row['pass'])
647 || ($isActivePass == true) // should be equal to || ($lastFinishedPass == -1 && $tries == 0)
648 )) {
649 $last_pass = true;
650 } else {
651 $last_pass = false;
652 }
653
654 // Work on tables:
655 // tst_active
656 if ($last_pass) {
657 $ilDB->manipulate(
658 'DELETE
659 FROM tst_active
660 WHERE active_id = ' . $ilDB->quote($active_fi, 'integer')
661 );
662 } elseif (!$isActivePass) {
663 $ilDB->manipulate(
664 'UPDATE tst_active
665 SET tries = ' . $ilDB->quote($tries - 1, 'integer') . ',
666 last_finished_pass = ' . $ilDB->quote($lastFinishedPass - 1, 'integer') . '
667 WHERE active_id = ' . $ilDB->quote($active_fi, 'integer')
668 );
669 }
670 // tst_manual_fb
671 $ilDB->manipulate(
672 'DELETE
673 FROM tst_manual_fb
674 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
675 AND pass = ' . $ilDB->quote($pass, 'integer')
676 );
677
678 if ($must_renumber) {
679 $ilDB->manipulate(
680 'UPDATE tst_manual_fb
681 SET pass = pass - 1
682 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
683 AND pass > ' . $ilDB->quote($pass, 'integer')
684 );
685 }
686
687 // tst_mark -> nothing to do
688 //
689 // tst_pass_result
690 $ilDB->manipulate(
691 'DELETE
692 FROM tst_pass_result
693 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
694 AND pass = ' . $ilDB->quote($pass, 'integer')
695 );
696
697 if ($must_renumber) {
698 $ilDB->manipulate(
699 'UPDATE tst_pass_result
700 SET pass = pass - 1
701 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
702 AND pass > ' . $ilDB->quote($pass, 'integer')
703 );
704 }
705
706 $ilDB->manipulate(
707 'DELETE
708 FROM tst_sequence
709 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
710 AND pass = ' . $ilDB->quote($pass, 'integer')
711 );
712
713 if ($must_renumber) {
714 $ilDB->manipulate(
715 'UPDATE tst_sequence
716 SET pass = pass - 1
717 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
718 AND pass > ' . $ilDB->quote($pass, 'integer')
719 );
720 }
721
722 // tst_solutions
723 $ilDB->manipulate(
724 'DELETE
725 FROM tst_solutions
726 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
727 AND pass = ' . $ilDB->quote($pass, 'integer')
728 );
729
730 if ($must_renumber) {
731 $ilDB->manipulate(
732 'UPDATE tst_solutions
733 SET pass = pass - 1
734 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
735 AND pass > ' . $ilDB->quote($pass, 'integer')
736 );
737 }
738
739 // tst_test_result
740 $ilDB->manipulate(
741 'DELETE
742 FROM tst_test_result
743 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
744 AND pass = ' . $ilDB->quote($pass, 'integer')
745 );
746
747 if ($must_renumber) {
748 $ilDB->manipulate(
749 'UPDATE tst_test_result
750 SET pass = pass - 1
751 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
752 AND pass > ' . $ilDB->quote($pass, 'integer')
753 );
754 }
755
756 // tst_test_rnd_qst -> nothing to do
757
758 // tst_times
759 $ilDB->manipulate(
760 'DELETE
761 FROM tst_times
762 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
763 AND pass = ' . $ilDB->quote($pass, 'integer')
764 );
765
766 if ($must_renumber) {
767 $ilDB->manipulate(
768 'UPDATE tst_times
769 SET pass = pass - 1
770 WHERE active_fi = ' . $ilDB->quote($active_fi, 'integer') . '
771 AND pass > ' . $ilDB->quote($pass, 'integer')
772 );
773 }
774
775 $this->test_result_repository->updateTestResultCache((int) $active_fi);
776
777 $this->redirectToPassDeletionContext($context);
778 }
779
781 {
782 $this->ctrl->redirectByClass(ilTestParticipantsGUI::class);
783 }
784
785 protected function sendPage(string $page)
786 {
787 $this->http->saveResponse($this->http->response()->withBody(
788 Streams::ofString($page)
789 ));
790 $this->http->sendResponse();
791 $this->http->close();
792 }
793
794 protected function buildResultsTitle(int $active_id, int $pass): string
795 {
796 if ($this->object->getAnonymity()) {
797 return sprintf(
798 $this->lng->txt('tst_eval_results_by_pass_lo'),
799 $pass + 1
800 );
801 }
802 return sprintf(
803 $this->lng->txt('tst_result_user_name_pass'),
804 $pass + 1,
805 $this->participant_repository->getParticipantByActiveId($this->object->getTestId(), $active_id)->getDisplayName($this->lng)
806 );
807 }
808
809 private function buildAttemptComponents(
810 int $active_id,
811 int $attempt_id,
812 bool $with_test_results_overview,
813 bool $for_print
814 ): array {
815 $settings = $this->results_presentation_factory->getAttemptResultsSettings(
816 $this->object,
817 false
818 );
819 $attempt_overview = $this->ui_factory->panel()->sub(
820 $this->lng->txt('question_summary'),
821 $this->results_data_factory->getAttemptOverviewFor(
822 $settings,
823 $this->object,
824 $active_id,
825 $attempt_id
826 )->getAsDescriptiveListing(
827 $this->lng,
828 $this->ui_factory,
829 [
830 'timezone' => new DateTimeZone($this->user->getTimeZone()),
831 'datetimeformat' => $this->user->getDateTimeFormat()->toString()
832 ]
833 )
834 );
835
836 if ($with_test_results_overview) {
837 $attempt_overview = $attempt_overview->withFurtherInformation(
838 $this->ui_factory->card()->standard($this->lng->txt('overview'))->withSections([
839 $this->results_data_factory->getOverviewDataForTest($this->object)
840 ->getAsDescriptiveListing(
841 $this->lng,
842 $this->ui_factory
843 )
844 ])
845 );
846 }
847
848 $results_presentation_table = $this->results_presentation_factory->getAttemptResultsPresentationTable(
849 $this->results_data_factory->getAttemptResultsFor(
850 $settings,
851 $this->object,
852 $active_id,
853 $attempt_id,
854 false
855 ),
856 $settings,
857 '',
858 $for_print
859 )->getTableComponent();
860
861 if ($for_print) {
862 $signal = $results_presentation_table->getExpandAllSignal();
863 $signal_options = json_encode(['options' => $signal->getOptions()]);
864 $results_presentation_table = [
865 $results_presentation_table,
866 $this->ui_factory->legacy()->content('')->withAdditionalOnLoadCode(
867 static fn(string $id): string => "$(document).trigger('{$signal->getId()}', $signal_options);"
868 )
869 ];
870 }
871
872
873
874 $attempt_details = $this->ui_factory->panel()->sub(
875 $this->lng->txt('details'),
876 $results_presentation_table
877 );
878
879 return [$attempt_overview, $attempt_details];
880 }
881
882 private function addPrintResultsButtonToToolbar(): void
883 {
884 $link = $this->ctrl->getLinkTargetByClass(self::class, 'printResults');
885 $this->toolbar->addComponent(
886 $this->ui_factory->button()->standard(
887 $this->lng->txt('print'),
888 ''
889 )->withOnLoadCode(
890 fn($id): string => "document.getElementById('{$id}').addEventListener('click', "
891 . "(e) => {window.open('{$link}');}"
892 . ');'
893 )
894 );
895 }
896
897 private function addPrintButtonToToolbar(): void
898 {
899 $this->toolbar->addComponent(
900 $this->ui_factory->button()->standard(
901 $this->lng->txt('print'),
902 ''
903 )->withOnLoadCode(
904 fn($id): string => "document.getElementById('{$id}').addEventListener('click', "
905 . "()=>{window.print();}"
906 . ');'
907 )
908 );
909 }
910
912 {
913 $this->toolbar->addComponent(
914 $this->ui_factory->button()->standard(
915 $this->lng->txt('certificate'),
916 $this->ctrl->getLinkTargetByClass(self::class, 'outCertificate')
917 )
918 );
919 }
920
922 {
923 if ($this->testrequest->isset('show_best_solutions')) {
925 'tst_results_show_best_solutions',
926 $this->testrequest->int('show_best_solutions') === 1
927 );
928 }
929
930 if (ilSession::get('tst_results_show_best_solutions')) {
931 $this->ctrl->setParameter($this, 'show_best_solutions', '0');
932 $label = $this->lng->txt('tst_btn_hide_best_solutions');
933 } else {
934 $this->ctrl->setParameter($this, 'show_best_solutions', '1');
935 $label = $this->lng->txt('tst_btn_show_best_solutions');
936 }
937
938 $this->toolbar->addSeparator();
939 $this->toolbar->addComponent(
940 $this->ui_factory->button()->standard(
941 $label,
942 $this->ctrl->getLinkTargetByClass(self::class, $this->ctrl->getCmd(self::DEFAULT_CMD))
943 )
944 );
945 $this->ctrl->clearParameters($this, 'show_best_solutions');
946 }
947
949 array $selected_active_ids,
950 int $current_active_id
951 ): void {
952 $this->toolbar->addSeparator();
953 $this->toolbar->addComponent(
954 $this->ui_factory->dropdown()
955 ->standard(
956 $this->buildParticipantSelectorArray($selected_active_ids, $current_active_id)
957 )->withLabel($this->lng->txt('tst_res_jump_to_participant_hint_opt'))
958 );
959 }
960
962 array $selected_active_ids,
963 int $current_active_id
964 ): array {
965 $this->ctrl->setParameterByClass(self::class, 'active_ids', implode(',', $selected_active_ids));
966 unset($selected_active_ids[array_search($current_active_id, $selected_active_ids)]);
967 $available_user_links = array_map(
968 function (int $v): StandardLink {
969 $this->ctrl->setParameterByClass(self::class, 'active_id', $v);
970 return $this->ui_factory->link()->standard(
971 ilObjUser::_lookupFullname($this->object->_getUserIdFromActiveId($v)),
972 $this->ctrl->getLinkTargetByClass(self::class, 'showResults')
973 );
974 },
975 $selected_active_ids
976 );
977 $this->ctrl->clearParameterByClass(self::class, 'active_id');
978 $this->ctrl->clearParameterByClass(self::class, 'active_ids');
979 return $available_user_links;
980 }
981
983 array $available_attempts,
984 int $selected_attempt
985 ): void {
986 $selected_attempt++;
987 $this->toolbar->addComponent(
988 $this->buildAttemptSwitchingViewControl(
989 $available_attempts,
990 $selected_attempt
991 )
992 );
993 $this->ctrl->clearParameterByClass(self::class, 'attempt');
994 }
995
997 array $available_attempts,
998 int $selected_attempt
999 ): ViewControlMode {
1000 return $this->ui_factory->viewControl()->mode(
1001 array_reduce(
1002 $available_attempts,
1003 function (array $c, int $v): array {
1004 $this->ctrl->setParameterByClass(self::class, 'attempt', $v);
1005 $attempt = $v + 1;
1006 $c["{$this->lng->txt('tst_attempt')} {$attempt}"] = $this
1007 ->ctrl->getLinkTargetByClass(self::class, $this->ctrl->getCmd(self::DEFAULT_CMD));
1008 return $c;
1009 },
1010 []
1011 ),
1012 $this->lng->txt('select_attempt')
1013 )->withActive("{$this->lng->txt('tst_attempt')} {$selected_attempt}");
1014 }
1015}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$components
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
Validates if an active certificate is stored in the database and can be downloaded by the user.
Just a wrapper class to create Unit Test for other classes.
static getInstance(ilTestSession $a_test_session)
static getContentStylePath(int $a_style_id, bool $add_random=true, bool $add_token=true)
get content style path static (to avoid full reading)
static _getResultPass($active_id)
Retrieves the pass number that should be counted for a given user.
static lookupExamId($active_id, $pass)
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...
static _lookupFullname(int $a_user_id)
static get(string $a_var)
static set(string $a_var, $a_val)
Set a value.
ILIAS Setting Class.
special template class to simplify handling of ITX/PEAR
Output class for assessment test evaluation.
addParticipantSelectorToToolbar(array $selected_active_ids, int $current_active_id)
readonly ParticipantRepository $participant_repository
__construct(ilObjTest $object)
getEvaluationQuestionId($question_id, $original_id='')
Returns the ID of a question for evaluation purposes.
buildParticipantSelectorArray(array $selected_active_ids, int $current_active_id)
ilTestProcessLockerFactory $processLockerFactory
buildAttemptSwitchingViewControl(array $available_attempts, int $selected_attempt)
addAttemptSwitchingViewControlToToolbar(array $available_attempts, int $selected_attempt)
buildResultsTitle(int $active_id, int $pass)
buildAttemptComponents(int $active_id, int $attempt_id, bool $with_test_results_overview, bool $for_print)
redirectToPassDeletionContext(string $context)
Base Exception for all Exceptions relating to Modules/Test.
Service GUI class for tests.
buildPassOverviewTableGUI(ilTestEvaluationGUI $target_gui)
setContextResultPresentation(bool $contextResultPresentation)
getAdditionalUsrDataHtmlAndPopulateWindowTitle(int $active_id, bool $overwrite_anonymity=false)
Returns the user data for a test results output.
buildQuestionRelatedObjectivesList(ilLOTestQuestionAdapter $objectives_adapter, ilTestQuestionSequence $test_sequence)
getResultsSignature()
Returns HTML code for a signature field.
populatePassFinishDate(ilTemplate $tpl, ?int $pass_finish_date)
getPassListOfAnswers(&$result_array, $active_id, $pass, $show_solutions=false, $only_answered_questions=false, $show_question_only=false, $show_reached_points=false, $anchorNav=false, ?ilTestQuestionRelatedObjectivesList $objectives_list=null, ?ResultsTitlesBuilder $testResultHeaderLabelBuilder=null)
Returns the list of answers of a users test pass.
populateExamId(ilTemplate $tpl, int $active_id, int $pass)
readonly ilDBInterface $db
ilGlobalTemplateInterface ilTemplate $tpl
sk 2023-08-01: We need this union type, even if it is wrong! To change this
readonly ilSetting $settings
getPassOverviewTableData(ilTestSession $test_session, array $passes, bool $with_results)
getGradingMessageBuilder(int $active_id)
static getStyleSheetLocation(string $mode="output", string $a_css_name="")
get full style sheet file name (path inclusive) of current user
$c
Definition: deliver.php:25
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setVariable(string $variable, $value='')
Sets the given variable to the given value.
parseCurrentBlock(string $block_name=self::DEFAULT_BLOCK)
Parses the given block.
setCurrentBlock(string $part=self::DEFAULT_BLOCK)
Sets the template to the given block.
get(string $part=self::DEFAULT_BLOCK)
Renders the given block and returns the html string.
This describes a Sub Panel.
Definition: Sub.php:30
This describes a Mode Control.
Definition: Mode.php:29
static http()
Fetches the global http state from ILIAS.
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc