ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilTestPlayerAbstractGUI.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once './Modules/Test/classes/inc.AssessmentConstants.php';
5require_once './Modules/Test/classes/class.ilTestPlayerCommands.php';
6require_once './Modules/Test/classes/class.ilTestServiceGUI.php';
7require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
8require_once './Services/UIComponent/Button/classes/class.ilSubmitButton.php';
9require_once 'Modules/Test/classes/class.ilTestPlayerNavButton.php';
10
26{
27 const PRESENTATION_MODE_VIEW = 'view';
28 const PRESENTATION_MODE_EDIT = 'edit';
29
31
32 public $ref_id;
34 public $sequence;
35 public $cmdCtrl;
38
43
47 protected $processLocker;
48
52 protected $testSession;
53
57 protected $assSettings;
58
62 protected $testSequence = null;
63
69 public function __construct($a_object)
70 {
71 parent::__construct($a_object);
72 $this->ref_id = $_GET["ref_id"];
73
74 global $DIC;
75 $rbacsystem = $DIC['rbacsystem'];
76 $ilUser = $DIC['ilUser'];
77 $lng = $DIC['lng'];
78 require_once 'Modules/Test/classes/class.ilTestPasswordChecker.php';
79 $this->passwordChecker = new ilTestPasswordChecker($rbacsystem, $ilUser, $this->object, $lng);
80
81 $this->processLocker = null;
82 $this->testSession = null;
83 $this->assSettings = null;
84 }
85
86 protected function checkReadAccess()
87 {
88 global $DIC;
89 $rbacsystem = $DIC['rbacsystem'];
90
91 if (!$rbacsystem->checkAccess("read", $this->object->getRefId())) {
92 // only with read access it is possible to run the test
93 $this->ilias->raiseError($this->lng->txt("cannot_execute_test"), $this->ilias->error_obj->MESSAGE);
94 }
95 }
96
97 protected function checkTestExecutable()
98 {
99 $executable = $this->object->isExecutable($this->testSession, $this->testSession->getUserId());
100
101 if (!$executable['executable']) {
102 ilUtil::sendInfo($executable['errormessage'], true);
103 $this->ctrl->redirectByClass("ilobjtestgui", "infoScreen");
104 }
105 }
106
108 {
109 global $DIC; /* @var ILIAS\DI\Container $DIC */
110
111 if ($testSession->getUserId() != $DIC->user()->getId()) {
112 throw new ilTestException('active id given does not relate to current user!');
113 }
114 }
115
117 {
118 if ($testSession->getActiveId()) {
119 return;
120 }
121
122 global $DIC;
123 $ilUser = $DIC['ilUser'];
124
125 $testSession->setUserId($ilUser->getId());
126
127 if ($testSession->isAnonymousUser()) {
128 if (!$testSession->doesAccessCodeInSessionExists()) {
129 return;
130 }
131
132 $testSession->setAnonymousId($testSession->getAccessCodeFromSession());
133 }
134
135 $testSession->saveToDb();
136 }
137
138 protected function initProcessLocker($activeId)
139 {
140 global $DIC;
141 $ilDB = $DIC['ilDB'];
142
143 require_once 'Modules/Test/classes/class.ilTestProcessLockerFactory.php';
144 $processLockerFactory = new ilTestProcessLockerFactory($this->assSettings, $ilDB);
145
146 $processLockerFactory->setActiveId($activeId);
147
148 $this->processLocker = $processLockerFactory->getLocker();
149 }
150
157 public function saveTagsCmd()
158 {
159 include_once("./Services/Tagging/classes/class.ilTaggingGUI.php");
160 $tagging_gui = new ilTaggingGUI();
161 $tagging_gui->setObject($this->object->getId(), $this->object->getType());
162 $tagging_gui->saveInput();
163 $this->ctrl->redirectByClass("ilobjtestgui", "infoScreen");
164 }
165
169 public function updateWorkingTime()
170 {
171 if ($_SESSION["active_time_id"]) {
172 $this->object->updateWorkingTime($_SESSION["active_time_id"]);
173 }
174
175 $_SESSION["active_time_id"] = $this->object->startWorkingTime(
176 $this->testSession->getActiveId(),
177 $this->testSession->getPass()
178 );
179 }
180
181 // fau: testNav - new function removeIntermediateSolution()
187 {
188 $questionId = $this->getCurrentQuestionId();
189
190 $this->getQuestionInstance($questionId)->removeIntermediateSolution(
191 $this->testSession->getActiveId(),
192 $this->testSession->getPass()
193 );
194 }
195 // fau.
196
200 abstract public function saveQuestionSolution($authorized = true, $force = false);
201
202 abstract protected function canSaveResult();
203
204 public function suspendTestCmd()
205 {
206 $this->ctrl->redirectByClass("ilobjtestgui", "infoScreen");
207 }
208
217 {
218 global $DIC;
219 $ilUser = $DIC['ilUser'];
220 $active_id = $this->testSession->getActiveId();
221 $starting_time = $this->object->getStartingTimeOfUser($active_id);
222 if ($starting_time === false) {
223 return false;
224 } else {
225 return $this->object->isMaxProcessingTimeReached($starting_time, $active_id);
226 }
227 }
228
229 protected function determineInlineScoreDisplay()
230 {
231 $show_question_inline_score = false;
232 if ($this->object->getAnswerFeedbackPoints()) {
233 $show_question_inline_score = true;
234 return $show_question_inline_score;
235 }
236 return $show_question_inline_score;
237 }
238
240 {
241 $this->tpl->setCurrentBlock('test_nav_toolbar');
242 $this->tpl->setVariable('TEST_NAV_TOOLBAR', $toolbarGUI->getHTML());
243 $this->tpl->parseCurrentBlock();
244 }
245
246 protected function populateQuestionNavigation($sequenceElement, $disabled, $primaryNext)
247 {
248 if (!$this->isFirstQuestionInSequence($sequenceElement)) {
249 $this->populatePreviousButtons($disabled);
250 }
251
252 if (!$this->isLastQuestionInSequence($sequenceElement)) {
253 $this->populateNextButtons($disabled, $primaryNext);
254 }
255 }
256
257 protected function populatePreviousButtons($disabled)
258 {
259 $this->populateUpperPreviousButtonBlock($disabled);
260 $this->populateLowerPreviousButtonBlock($disabled);
261 }
262
263 protected function populateNextButtons($disabled, $primaryNext)
264 {
265 $this->populateUpperNextButtonBlock($disabled, $primaryNext);
266 $this->populateLowerNextButtonBlock($disabled, $primaryNext);
267 }
268
269 protected function populateLowerNextButtonBlock($disabled, $primaryNext)
270 {
271 $button = $this->buildNextButtonInstance($disabled, $primaryNext);
272 $button->setId('bottomnextbutton');
273
274 $this->tpl->setCurrentBlock("next_bottom");
275 $this->tpl->setVariable("BTN_NEXT", $button->render());
276 $this->tpl->parseCurrentBlock();
277 }
278
279 protected function populateUpperNextButtonBlock($disabled, $primaryNext)
280 {
281 $button = $this->buildNextButtonInstance($disabled, $primaryNext);
282 $button->setId('nextbutton');
283
284 $this->tpl->setCurrentBlock("next");
285 $this->tpl->setVariable("BTN_NEXT", $button->render());
286 $this->tpl->parseCurrentBlock();
287 }
288
289 protected function populateLowerPreviousButtonBlock($disabled)
290 {
291 $button = $this->buildPreviousButtonInstance($disabled);
292 $button->setId('bottomprevbutton');
293
294 $this->tpl->setCurrentBlock("prev_bottom");
295 $this->tpl->setVariable("BTN_PREV", $button->render());
296 $this->tpl->parseCurrentBlock();
297 }
298
299 protected function populateUpperPreviousButtonBlock($disabled)
300 {
301 $button = $this->buildPreviousButtonInstance($disabled);
302 $button->setId('prevbutton');
303
304 $this->tpl->setCurrentBlock("prev");
305 $this->tpl->setVariable("BTN_PREV", $button->render());
306 $this->tpl->parseCurrentBlock();
307 }
308
314 private function buildNextButtonInstance($disabled, $primaryNext)
315 {
317 // fau: testNav - set glyphicon and primary
318 $button->setPrimary($primaryNext);
319 $button->setRightGlyph('glyphicon glyphicon-arrow-right');
320 // fau.
321 $button->setNextCommand(ilTestPlayerCommands::NEXT_QUESTION);
322 $button->setUrl($this->ctrl->getLinkTarget($this, ilTestPlayerCommands::NEXT_QUESTION));
323 $button->setCaption('next_question');
324 $button->addCSSClass('ilTstNavElem');
325 //$button->setDisabled($disabled);
326 return $button;
327 }
328
333 private function buildPreviousButtonInstance($disabled)
334 {
336 // fau: testNav - set glyphicon and primary
337 $button->setLeftGlyph('glyphicon glyphicon-arrow-left');
338 // fau.
339 $button->setNextCommand(ilTestPlayerCommands::PREVIOUS_QUESTION);
340 $button->setUrl($this->ctrl->getLinkTarget($this, ilTestPlayerCommands::PREVIOUS_QUESTION));
341 $button->setCaption('previous_question');
342 $button->addCSSClass('ilTstNavElem');
343 //$button->setDisabled($disabled);
344 return $button;
345 }
346
347 protected function populateSpecificFeedbackBlock(assQuestionGUI $question_gui)
348 {
349 $solutionValues = $question_gui->object->getSolutionValues(
350 $this->testSession->getActiveId(),
351 null
352 );
353
354 $feedback = $question_gui->getSpecificFeedbackOutput(
355 $question_gui->object->fetchIndexedValuesFromValuePairs($solutionValues)
356 );
357
358 $this->tpl->setCurrentBlock("specific_feedback");
359 $this->tpl->setVariable("SPECIFIC_FEEDBACK", $feedback);
360 $this->tpl->parseCurrentBlock();
361 }
362
363 protected function populateGenericFeedbackBlock(assQuestionGUI $question_gui, $solutionCorrect)
364 {
365 $feedback = $question_gui->getGenericFeedbackOutput($this->testSession->getActiveId(), null);
366
367 if (strlen($feedback)) {
368 $cssClass = (
369 $solutionCorrect ?
371 );
372
373 $this->tpl->setCurrentBlock("answer_feedback");
374 $this->tpl->setVariable("ANSWER_FEEDBACK", $feedback);
375 $this->tpl->setVariable("ILC_FB_CSS_CLASS", $cssClass);
376 $this->tpl->parseCurrentBlock();
377 }
378 }
379
380 protected function populateScoreBlock($reachedPoints, $maxPoints)
381 {
382 $scoreInformation = sprintf(
383 $this->lng->txt("you_received_a_of_b_points"),
384 $reachedPoints,
385 $maxPoints
386 );
387
388 $this->tpl->setCurrentBlock("received_points_information");
389 $this->tpl->setVariable("RECEIVED_POINTS_INFORMATION", $scoreInformation);
390 $this->tpl->parseCurrentBlock();
391 }
392
393 protected function populateSolutionBlock($solutionoutput)
394 {
395 if (strlen($solutionoutput)) {
396 $this->tpl->setCurrentBlock("solution_output");
397 $this->tpl->setVariable("CORRECT_SOLUTION", $this->lng->txt("tst_best_solution_is"));
398 $this->tpl->setVariable("QUESTION_FEEDBACK", $solutionoutput);
399 $this->tpl->parseCurrentBlock();
400 }
401 }
402
403 protected function populateSyntaxStyleBlock()
404 {
405 $this->tpl->setCurrentBlock("SyntaxStyle");
406 $this->tpl->setVariable(
407 "LOCATION_SYNTAX_STYLESHEET",
409 );
410 $this->tpl->parseCurrentBlock();
411 }
412
413 protected function populateContentStyleBlock()
414 {
415 include_once("./Services/Style/Content/classes/class.ilObjStyleSheet.php");
416 $this->tpl->setCurrentBlock("ContentStyle");
417 $this->tpl->setVariable(
418 "LOCATION_CONTENT_STYLESHEET",
420 );
421 $this->tpl->parseCurrentBlock();
422 }
423
429 public function setAnonymousIdCmd()
430 {
431 if ($this->testSession->isAnonymousUser()) {
432 $this->testSession->setAccessCodeToSession($_POST['anonymous_id']);
433 }
434
435 $this->ctrl->redirectByClass("ilobjtestgui", "infoScreen");
436 }
437
444 protected function startPlayerCmd()
445 {
446 $testStartLock = $this->getLockParameter();
447 $isFirstTestStartRequest = false;
448
449 $this->processLocker->executeTestStartLockOperation(function () use ($testStartLock, &$isFirstTestStartRequest) {
450 if ($this->testSession->lookupTestStartLock() != $testStartLock) {
451 $this->testSession->persistTestStartLock($testStartLock);
452 $isFirstTestStartRequest = true;
453 }
454 });
455
456 if ($isFirstTestStartRequest) {
457 $this->handleUserSettings();
458 $this->ctrl->redirect($this, ilTestPlayerCommands::INIT_TEST);
459 }
460
461 $this->ctrl->setParameterByClass('ilObjTestGUI', 'lock', $testStartLock);
462 $this->ctrl->redirectByClass("ilobjtestgui", "redirectToInfoScreen");
463 }
464
465 public function getLockParameter()
466 {
467 if (isset($_POST['lock']) && strlen($_POST['lock'])) {
468 return $_POST['lock'];
469 } elseif (isset($_GET['lock']) && strlen($_GET['lock'])) {
470 return $_GET['lock'];
471 }
472
473 return null;
474 }
475
479 abstract protected function resumePlayerCmd();
480
484 protected function initTestCmd()
485 {
486 if ($this->object->checkMaximumAllowedUsers() == false) {
488 }
489
490 if ($this->testSession->isAnonymousUser() && !$this->testSession->getActiveId()) {
491 $accessCode = $this->testSession->createNewAccessCode();
492
493 $this->testSession->setAccessCodeToSession($accessCode);
494 $this->testSession->setAnonymousId($accessCode);
495 $this->testSession->saveToDb();
496
497 $this->ctrl->redirect($this, ilTestPlayerCommands::DISPLAY_ACCESS_CODE);
498 }
499
500 $this->testSession->unsetAccessCodeInSession();
501 $this->ctrl->redirect($this, ilTestPlayerCommands::START_TEST);
502 }
503
504 public function displayAccessCodeCmd()
505 {
506 $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_anonymous_code_presentation.html", "Modules/Test");
507 $this->tpl->setCurrentBlock("adm_content");
508 $this->tpl->setVariable("TEXT_ANONYMOUS_CODE_CREATED", $this->lng->txt("tst_access_code_created"));
509 $this->tpl->setVariable("TEXT_ANONYMOUS_CODE", $this->testSession->getAccessCodeFromSession());
510 $this->tpl->setVariable("FORMACTION", $this->ctrl->getFormAction($this));
511 $this->tpl->setVariable("CMD_CONFIRM", ilTestPlayerCommands::ACCESS_CODE_CONFIRMED);
512 $this->tpl->setVariable("TXT_CONFIRM", $this->lng->txt("continue_work"));
513 $this->tpl->parseCurrentBlock();
514 }
515
516 public function accessCodeConfirmedCmd()
517 {
518 $this->ctrl->redirect($this, ilTestPlayerCommands::START_TEST);
519 }
520
524 public function handleUserSettings()
525 {
526 global $DIC;
527 $ilUser = $DIC['ilUser'];
528
529 if ($_POST["chb_javascript"]) {
530 $ilUser->writePref("tst_javascript", 1);
531 } else {
532 $ilUser->writePref("tst_javascript", 0);
533 }
534
535 // hide previous results
536 if ($this->object->getNrOfTries() != 1) {
537 if ($this->object->getUsePreviousAnswers() == 1) {
538 if ($_POST["chb_use_previous_answers"]) {
539 $ilUser->writePref("tst_use_previous_answers", 1);
540 } else {
541 $ilUser->writePref("tst_use_previous_answers", 0);
542 }
543 }
544 }
545 }
546
551 public function redirectAfterAutosaveCmd()
552 {
553 $active_id = $this->testSession->getActiveId();
554 $actualpass = ilObjTest::_getPass($active_id);
555
556 $this->performTestPassFinishedTasks($actualpass);
557
558 $this->testSession->setLastFinishedPass($this->testSession->getPass());
559 $this->testSession->increaseTestPass();
560
561 $url = $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::AFTER_TEST_PASS_FINISHED);
562
563 $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_redirect_autosave.html", "Modules/Test");
564 $this->tpl->setVariable("TEXT_REDIRECT", $this->lng->txt("redirectAfterSave"));
565 $this->tpl->setCurrentBlock("HeadContent");
566 $this->tpl->setVariable("CONTENT_BLOCK", "<meta http-equiv=\"refresh\" content=\"5; url=" . $url . "\">");
567 $this->tpl->parseCurrentBlock();
568 }
569
571 {
572 $active_id = $this->testSession->getActiveId();
573 $actualpass = ilObjTest::_getPass($active_id);
574
575 $this->performTestPassFinishedTasks($actualpass);
576
577 $this->testSession->setLastFinishedPass($this->testSession->getPass());
578 $this->testSession->increaseTestPass();
579
580 $url = $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::AFTER_TEST_PASS_FINISHED, '', false, false);
581
582 $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_redirect_autosave.html", "Modules/Test");
583 $this->tpl->setVariable("TEXT_REDIRECT", $this->lng->txt("redirectAfterSave"));
584 $this->tpl->setVariable("URL", $url);
585 }
586
587 abstract protected function getCurrentQuestionId();
588
593 public function autosaveCmd()
594 {
595 $result = "";
596 if (is_array($_POST) && count($_POST) > 0) {
597 if (!$this->canSaveResult() || $this->isParticipantsAnswerFixed($this->getCurrentQuestionId())) {
598 $result = '-IGNORE-';
599 } else {
600 // answer is changed from authorized solution, so save the change as intermediate solution
601 if ($this->getAnswerChangedParameter()) {
602 $res = $this->saveQuestionSolution(false, true);
603 }
604 // answer is not changed from authorized solution, so delete an intermediate solution
605 else {
606 $db_res = $this->removeIntermediateSolution();
607 $res = is_int($db_res);
608 }
609 if ($res) {
610 $result = $this->lng->txt("autosave_success");
611 } else {
612 $result = $this->lng->txt("autosave_failed");
613 }
614 }
615 }
616 echo $result;
617 exit;
618 }
619
624 public function autosaveOnTimeLimitCmd()
625 {
626 if (!$this->isParticipantsAnswerFixed($this->getCurrentQuestionId())) {
627 // time limit saves the user solution as authorized
628 $this->saveQuestionSolution(true, true);
629 }
630 $this->ctrl->redirect($this, ilTestPlayerCommands::REDIRECT_ON_TIME_LIMIT);
631 }
632
633
634 // fau: testNav - new function detectChangesCmd()
640 protected function detectChangesCmd()
641 {
642 $questionId = $this->getCurrentQuestionId();
643 $state = $this->getQuestionInstance($questionId)->lookupForExistingSolutions(
644 $this->testSession->getActiveId(),
645 $this->testSession->getPass()
646 );
647 $result = array();
648 $result['isAnswered'] = $state['authorized'];
649 $result['isAnswerChanged'] = $state['intermediate'];
650
651 echo json_encode($result);
652 exit;
653 }
654 // fau.
655
656 protected function submitIntermediateSolutionCmd()
657 {
658 $this->saveQuestionSolution(false, true);
659 // fau: testNav - set the 'answer changed' parameter when an intermediate solution is submitted
660 $this->setAnswerChangedParameter(true);
661 // fau.
662 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
663 }
664
668 public function toggleSideListCmd()
669 {
670 global $DIC;
671 $ilUser = $DIC['ilUser'];
672
673 $show_side_list = $ilUser->getPref('side_list_of_questions');
674 $ilUser->writePref('side_list_of_questions', !$show_side_list);
675 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
676 }
677
679 {
680 // fau: testNav - handle intermediate submit when marking the question
682 // fau.
683 $this->markQuestionCmd();
684 }
685
689 protected function markQuestionCmd()
690 {
691 $questionId = $this->testSequence->getQuestionForSequence(
693 );
694
695 $this->object->setQuestionSetSolved(1, $questionId, $this->testSession->getUserId());
696
697 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
698 }
699
701 {
702 // fau: testNav - handle intermediate submit when unmarking the question
704 // fau.
705 $this->unmarkQuestionCmd();
706 }
707
711 protected function unmarkQuestionCmd()
712 {
713 $questionId = $this->testSequence->getQuestionForSequence(
715 );
716
717 $this->object->setQuestionSetSolved(0, $questionId, $this->testSession->getUserId());
718
719 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
720 }
721
725 protected function confirmFinishCmd()
726 {
727 $this->finishTestCmd(false);
728 }
729
733 protected function confirmFinishTestCmd()
734 {
738 global $DIC;
739 $ilUser = $DIC['ilUser'];
740
741 require_once 'Services/Utilities/classes/class.ilConfirmationGUI.php';
742 $confirmation = new ilConfirmationGUI();
743 $confirmation->setFormAction($this->ctrl->getFormAction($this, 'confirmFinish'));
744 $confirmation->setHeaderText($this->lng->txt("tst_finish_confirmation_question"));
745 $confirmation->setConfirm($this->lng->txt("tst_finish_confirm_button"), 'confirmFinish');
746 $confirmation->setCancel($this->lng->txt("tst_finish_confirm_cancel_button"), ilTestPlayerCommands::BACK_FROM_FINISHING);
747
748 $this->populateHelperGuiContent($confirmation);
749 }
750
751 public function finishTestCmd($requires_confirmation = true)
752 {
753 unset($_SESSION["tst_next"]);
754
755 $active_id = $this->testSession->getActiveId();
756 $actualpass = ilObjTest::_getPass($active_id);
757
758 $allObligationsAnswered = ilObjTest::allObligationsAnswered($this->testSession->getTestId(), $active_id, $actualpass);
759
760 /*
761 * The following "endgames" are possible prior to actually finishing the test:
762 * - Obligations (Ability to finish the test.)
763 * If not all obligatory questions are answered, the user is presented a list
764 * showing the deficits.
765 * - Examview (Will to finish the test.)
766 * With the examview, the participant can review all answers given in ILIAS or a PDF prior to
767 * commencing to the finished test.
768 * - Last pass allowed (Reassuring the will to finish the test.)
769 * If passes are limited, on the last pass, an additional confirmation is to be displayed.
770 */
771
772
773 if ($this->object->areObligationsEnabled() && !$allObligationsAnswered) {
774 if ($this->object->getListOfQuestions()) {
775 $this->ctrl->redirect($this, ilTestPlayerCommands::QUESTION_SUMMARY_INC_OBLIGATIONS);
776 } else {
778 }
779
780 return;
781 }
782
783 // Examview enabled & !reviewed & requires_confirmation? test_submission_overview (review gui)
784 if ($this->object->getEnableExamview() && !isset($_GET['reviewed']) && $requires_confirmation) {
785 $this->ctrl->redirectByClass('ilTestSubmissionReviewGUI', "show");
786 return;
787 }
788
789 // Last try in limited tries & !confirmed
790 if (($requires_confirmation) && ($actualpass == $this->object->getNrOfTries() - 1)) {
791 // show confirmation page
792 return $this->confirmFinishTestCmd();
793 }
794
795 // Last try in limited tries & confirmed?
796 if (($actualpass == $this->object->getNrOfTries() - 1) && (!$requires_confirmation)) {
797 // @todo: php7 ask mister test
798 #$ilAuth->setIdle(ilSession::getIdleValue(), false);
799 #$ilAuth->setExpire(0);
800 switch ($this->object->getMailNotification()) {
801 case 1:
802 $this->object->sendSimpleNotification($active_id);
803 break;
804 case 2:
805 $this->object->sendAdvancedNotification($active_id);
806 break;
807 }
808 }
809
810 // Non-last try finish
811 if (!$_SESSION['tst_pass_finish']) {
812 if (!$_SESSION['tst_pass_finish']) {
813 $_SESSION['tst_pass_finish'] = 1;
814 }
815 if ($this->object->getMailNotificationType() == 1) {
816 switch ($this->object->getMailNotification()) {
817 case 1:
818 $this->object->sendSimpleNotification($active_id);
819 break;
820 case 2:
821 $this->object->sendAdvancedNotification($active_id);
822 break;
823 }
824 }
825 }
826
827 // no redirect request loops after test pass finished tasks has been performed
828
829 $this->performTestPassFinishedTasks($actualpass);
830
831 $this->ctrl->redirect($this, ilTestPlayerCommands::AFTER_TEST_PASS_FINISHED);
832 }
833
834 protected function performTestPassFinishedTasks($finishedPass)
835 {
836 require_once 'Modules/Test/classes/class.ilTestPassFinishTasks.php';
837
838 $finishTasks = new ilTestPassFinishTasks($this->testSession->getActiveId(), $this->object->getId());
839 $finishTasks->performFinishTasks($this->processLocker);
840 }
841
842 protected function afterTestPassFinishedCmd()
843 {
844 $activeId = $this->testSession->getActiveId();
845 $lastFinishedPass = $this->testSession->getLastFinishedPass();
846
847 // handle test signature
848 if ($this->isTestSignRedirectRequired($activeId, $lastFinishedPass)) {
849 $this->ctrl->redirectByClass('ilTestSignatureGUI', 'invokeSignaturePlugin');
850 }
851
852 // show final statement
853 if (!$_GET['skipfinalstatement']) {
854 if ($this->object->getShowFinalStatement()) {
855 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_FINAL_STATMENT);
856 }
857 }
858
859 // redirect after test
860 $redirection_mode = $this->object->getRedirectionMode();
861 $redirection_url = $this->object->getRedirectionUrl();
862 if ($redirection_url && $redirection_mode) {
863 if ($redirection_mode == REDIRECT_KIOSK) {
864 if ($this->object->getKioskMode()) {
865 ilUtil::redirect($redirection_url);
866 }
867 } else {
868 ilUtil::redirect($redirection_url);
869 }
870 }
871
872 // default redirect (pass overview when enabled, otherwise infoscreen)
873 $this->redirectBackCmd();
874 }
875
876 protected function isTestSignRedirectRequired($activeId, $lastFinishedPass)
877 {
878 if (!$this->object->getSignSubmission()) {
879 return false;
880 }
881
882 if (!is_null(ilSession::get("signed_{$activeId}_{$lastFinishedPass}"))) {
883 return false;
884 }
885
886 global $DIC;
887 $ilPluginAdmin = $DIC['ilPluginAdmin'];
888
889 $activePlugins = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, 'Test', 'tsig');
890
891 if (!count($activePlugins)) {
892 return false;
893 }
894
895 return true;
896 }
897
903 protected function archiveParticipantSubmission($active, $pass)
904 {
905 global $DIC;
906 $ilObjDataCache = $DIC['ilObjDataCache'];
907
908 require_once 'Modules/Test/classes/class.ilTestResultHeaderLabelBuilder.php';
909 $testResultHeaderLabelBuilder = new ilTestResultHeaderLabelBuilder($this->lng, $ilObjDataCache);
910
911 $objectivesList = null;
912
913 if ($this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()) {
914 $testSequence = $this->testSequenceFactory->getSequenceByActiveIdAndPass($this->testSession->getActiveId(), $this->testSession->getPass());
915 $testSequence->loadFromDb();
916 $testSequence->loadQuestions();
917
918 require_once 'Modules/Course/classes/Objectives/class.ilLOTestQuestionAdapter.php';
919 $objectivesAdapter = ilLOTestQuestionAdapter::getInstance($this->testSession);
920
921 $objectivesList = $this->buildQuestionRelatedObjectivesList($objectivesAdapter, $testSequence);
922 $objectivesList->loadObjectivesTitles();
923
924 $testResultHeaderLabelBuilder->setObjectiveOrientedContainerId($this->testSession->getObjectiveOrientedContainerId());
925 $testResultHeaderLabelBuilder->setUserId($this->testSession->getUserId());
926 $testResultHeaderLabelBuilder->setTestObjId($this->object->getId());
927 $testResultHeaderLabelBuilder->setTestRefId($this->object->getRefId());
928 $testResultHeaderLabelBuilder->initObjectiveOrientedMode();
929 }
930
931 $results = $this->object->getTestResult(
932 $active,
933 $pass,
934 false,
935 !$this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()
936 );
937
938 require_once 'class.ilTestEvaluationGUI.php';
939 $testevaluationgui = new ilTestEvaluationGUI($this->object);
940 $results_output = $testevaluationgui->getPassListOfAnswers(
941 $results,
942 $active,
943 $pass,
944 false,
945 false,
946 false,
947 false,
948 false,
949 $objectivesList,
950 $testResultHeaderLabelBuilder
951 );
952
953 require_once './Modules/Test/classes/class.ilTestArchiver.php';
954 global $DIC;
955 $ilSetting = $DIC['ilSetting'];
956 $inst_id = $ilSetting->get('inst_id', null);
957 $archiver = new ilTestArchiver($this->object->getId());
958
959 $path = ilUtil::getWebspaceDir() . '/assessment/' . $this->object->getId() . '/exam_pdf';
960 if (!is_dir($path)) {
962 }
963 $filename = realpath($path) . '/exam_N' . $inst_id . '-' . $this->object->getId()
964 . '-' . $active . '-' . $pass . '.pdf';
965
966 require_once 'Modules/Test/classes/class.ilTestPDFGenerator.php';
968 //$template->setVariable("PDF_FILE_LOCATION", $filename);
969 // Participant submission
970 $archiver->handInParticipantSubmission($active, $pass, $filename, $results_output);
971 //$archiver->handInParticipantMisc( $active, $pass, 'signature_gedoens.sig', $filename );
972 //$archiver->handInParticipantQuestionMaterial( $active, $pass, 123, 'file_upload.pdf', $filename );
973
974 global $DIC;
975 $ilias = $DIC['ilias'];
976 $questions = $this->object->getQuestions();
977 foreach ($questions as $question_id) {
978 $question_object = $this->object->getQuestionDataset($question_id);
979 if ($question_object->type_tag == 'assFileUpload') {
980 // Pfad: /data/default/assessment/tst_2/14/21/files/file_14_4_1370417414.png
981 // /data/ - klar
982 // /assessment/ - Konvention
983 // /tst_2/ = /tst_<test_id> (ilObjTest)
984 // /14/ = /<active_fi>/
985 // /21/ = /<question_id>/ (question_object)
986 // /files/ - Konvention
987 // file_14_4_1370417414.png = file_<active_fi>_<pass>_<some timestamp>.<ext>
988
989 $candidate_path =
990 $ilias->ini_ilias->readVariable('server', 'absolute_path') . ilTestArchiver::DIR_SEP
991 . $ilias->ini_ilias->readVariable('clients', 'path') . ilTestArchiver::DIR_SEP
992 . $ilias->client_id . ilTestArchiver::DIR_SEP
993 . 'assessment' . ilTestArchiver::DIR_SEP
994 . 'tst_' . $this->object->test_id . ilTestArchiver::DIR_SEP
995 . $active . ilTestArchiver::DIR_SEP
996 . $question_id . ilTestArchiver::DIR_SEP
997 . 'files' . ilTestArchiver::DIR_SEP;
998 $handle = opendir($candidate_path);
999 while ($handle !== false && ($file = readdir($handle)) !== false) {
1000 if ($file != null) {
1001 $filename_start = 'file_' . $active . '_' . $pass . '_';
1002
1003 if (strpos($file, $filename_start) === 0) {
1004 $archiver->handInParticipantQuestionMaterial($active, $pass, $question_id, $file, $file);
1005 }
1006 }
1007 }
1008 }
1009 }
1010 $passdata = $this->object->getTestResult(
1011 $active,
1012 $pass,
1013 false,
1014 !$this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()
1015 );
1016 $overview = $testevaluationgui->getPassListOfAnswers(
1017 $passdata,
1018 $active,
1019 $pass,
1020 true,
1021 false,
1022 false,
1023 true,
1024 false,
1025 $objectivesList,
1026 $testResultHeaderLabelBuilder
1027 );
1028 $filename = realpath(ilUtil::getWebspaceDir()) . '/assessment/scores-' . $this->object->getId() . '-' . $active . '-' . $pass . '.pdf';
1030 $archiver->handInTestResult($active, $pass, $filename);
1031 unlink($filename);
1032
1033 return;
1034 }
1035
1036 public function redirectBackCmd()
1037 {
1038 global $DIC; /* @var ILIAS\DI\Container $DIC */
1039 require_once 'Modules/Test/classes/class.ilTestPassesSelector.php';
1040 $testPassesSelector = new ilTestPassesSelector($DIC['ilDB'], $this->object);
1041 $testPassesSelector->setActiveId($this->testSession->getActiveId());
1042 $testPassesSelector->setLastFinishedPass($this->testSession->getLastFinishedPass());
1043
1044 if (count($testPassesSelector->getReportablePasses())) {
1045 if ($this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()) {
1046 $this->ctrl->redirectByClass(array('ilTestResultsGUI', 'ilTestEvalObjectiveOrientedGUI'));
1047 }
1048
1049 $this->ctrl->redirectByClass(array('ilTestResultsGUI', 'ilMyTestResultsGUI', 'ilTestEvaluationGUI'));
1050 }
1051
1052 $this->backToInfoScreenCmd();
1053 }
1054
1055 protected function backToInfoScreenCmd()
1056 {
1057 $this->ctrl->redirectByClass('ilObjTestGUI', 'redirectToInfoScreen');
1058 }
1059
1060 /*
1061 * Presents the final statement of a test
1062 */
1063 public function showFinalStatementCmd()
1064 {
1065 $template = new ilTemplate("tpl.il_as_tst_final_statement.html", true, true, "Modules/Test");
1066 $this->ctrl->setParameter($this, "skipfinalstatement", 1);
1067 $template->setVariable("FORMACTION", $this->ctrl->getFormAction($this, ilTestPlayerCommands::AFTER_TEST_PASS_FINISHED));
1068 $template->setVariable("FINALSTATEMENT", $this->object->prepareTextareaOutput($this->object->getFinalStatement(), true));
1069 $template->setVariable("BUTTON_CONTINUE", $this->lng->txt("btn_next"));
1070 $this->tpl->setVariable($this->getContentBlockName(), $template->get());
1071 }
1072
1073 public function getKioskHead()
1074 {
1075 global $DIC;
1076 $ilUser = $DIC['ilUser'];
1077
1078 $template = new ilTemplate('tpl.il_as_tst_kiosk_head.html', true, true, 'Modules/Test');
1079 if ($this->object->getShowKioskModeTitle()) {
1080 $template->setCurrentBlock("kiosk_show_title");
1081 $template->setVariable("TEST_TITLE", $this->object->getTitle());
1082 $template->parseCurrentBlock();
1083 }
1084 if ($this->object->getShowKioskModeParticipant()) {
1085 $template->setCurrentBlock("kiosk_show_participant");
1086 $template->setVariable("PARTICIPANT_NAME_TXT", $this->lng->txt("login_as"));
1087 $template->setVariable("PARTICIPANT_NAME", $ilUser->getFullname());
1088 $template->setVariable("PARTICIPANT_LOGIN", $ilUser->getLogin());
1089 $template->setVariable("PARTICIPANT_MATRICULATION", $ilUser->getMatriculation());
1090 $template->setVariable("PARTICIPANT_EMAIL", $ilUser->getEmail());
1091 $template->parseCurrentBlock();
1092 }
1093 if ($this->object->isShowExamIdInTestPassEnabled()) {
1094 $exam_id = ilObjTest::buildExamId(
1095 $this->testSession->getActiveId(),
1096 $this->testSession->getPass(),
1097 $this->object->getId()
1098 );
1099
1100 $template->setCurrentBlock("kiosk_show_exam_id");
1101 $template->setVariable("EXAM_ID_TXT", $this->lng->txt("exam_id"));
1102 $template->setVariable("EXAM_ID", $exam_id);
1103 $template->parseCurrentBlock();
1104 }
1105 return $template->get();
1106 }
1107
1111 protected function prepareTestPage($presentationMode, $sequenceElement, $questionId)
1112 {
1113 global $DIC;
1114 $ilUser = $DIC['ilUser'];
1115 $ilNavigationHistory = $DIC['ilNavigationHistory'];
1116
1117 $ilNavigationHistory->addItem(
1118 $this->testSession->getRefId(),
1119 $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::RESUME_PLAYER),
1120 'tst'
1121 );
1122
1123 $this->initTestPageTemplate();
1125 $this->populateSyntaxStyleBlock();
1126
1127 if ($this->isMaxProcessingTimeReached()) {
1128 $this->maxProcessingTimeReached();
1129 return;
1130 }
1131
1132 if ($this->object->endingTimeReached()) {
1133 $this->endingTimeReached();
1134 return;
1135 }
1136
1137 if ($this->isOptionalQuestionAnsweringConfirmationRequired($sequenceElement)) {
1138 $this->ctrl->setParameter($this, "sequence", $sequenceElement);
1140 return;
1141 }
1142
1143 if ($this->object->getKioskMode()) {
1144 $this->populateKioskHead();
1145 }
1146
1147 $this->tpl->setVariable("TEST_ID", $this->object->getTestId());
1148 $this->tpl->setVariable("LOGIN", $ilUser->getLogin());
1149 $this->tpl->setVariable("SEQ_ID", $sequenceElement);
1150 $this->tpl->setVariable("QUEST_ID", $questionId);
1151
1152 if ($this->object->getEnableProcessingTime()) {
1153 $this->outProcessingTime($this->testSession->getActiveId());
1154 }
1155
1156 $this->tpl->setVariable("PAGETITLE", "- " . $this->object->getTitle());
1157
1158 if ($this->object->isShowExamIdInTestPassEnabled() && !$this->object->getKioskMode()) {
1159 $this->tpl->setCurrentBlock('exam_id_footer');
1160 $this->tpl->setVariable('EXAM_ID_VAL', ilObjTest::lookupExamId(
1161 $this->testSession->getActiveId(),
1162 $this->testSession->getPass(),
1163 $this->object->getId()
1164 ));
1165 $this->tpl->setVariable('EXAM_ID_TXT', $this->lng->txt('exam_id'));
1166 $this->tpl->parseCurrentBlock();
1167 }
1168
1169 if ($this->object->getListOfQuestions()) {
1170 $this->showSideList($presentationMode, $sequenceElement);
1171 }
1172 }
1173
1174 abstract protected function isOptionalQuestionAnsweringConfirmationRequired($sequenceElement);
1175
1176 abstract protected function isShowingPostponeStatusReguired($questionId);
1177
1178 protected function showQuestionViewable(assQuestionGUI $questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse)
1179 {
1180 $questionNavigationGUI = $this->buildReadOnlyStateQuestionNavigationGUI($questionGui->object->getId());
1181 $questionNavigationGUI->setQuestionWorkedThrough($isQuestionWorkedThrough);
1182 $questionGui->setNavigationGUI($questionNavigationGUI);
1183
1184 // fau: testNav - set answere status in question header
1185 $questionGui->getQuestionHeaderBlockBuilder()->setQuestionAnswered($isQuestionWorkedThrough);
1186 // fau.
1187
1188 $answerFeedbackEnabled = (
1189 $instantResponse && $this->object->getSpecificAnswerFeedback()
1190 );
1191
1192 $solutionoutput = $questionGui->getSolutionOutput(
1193 $this->testSession->getActiveId(), #active_id
1194 $this->testSession->getPass(), #pass
1195 false, #graphical_output
1196 false, #result_output
1197 true, #show_question_only
1198 $answerFeedbackEnabled, #show_feedback
1199 false, #show_correct_solution
1200 false, #show_manual_scoring
1201 true #show_question_text
1202 );
1203
1204 $pageoutput = $questionGui->outQuestionPage(
1205 "",
1206 $this->isShowingPostponeStatusReguired($questionGui->object->getId()),
1207 $this->testSession->getActiveId(),
1208 $solutionoutput
1209 );
1210
1211 $this->tpl->setCurrentBlock('readonly_css_class');
1212 $this->tpl->touchBlock('readonly_css_class');
1213 $this->tpl->parseCurrentBlock();
1214
1215 $this->tpl->setVariable('QUESTION_OUTPUT', $pageoutput);
1216
1217 $this->tpl->setVariable("FORMACTION", $formAction);
1218 $this->tpl->setVariable("ENCTYPE", 'enctype="' . $questionGui->getFormEncodingType() . '"');
1219 $this->tpl->setVariable("FORM_TIMESTAMP", time());
1220 }
1221
1222 protected function showQuestionEditable(assQuestionGUI $questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse)
1223 {
1224 $questionNavigationGUI = $this->buildEditableStateQuestionNavigationGUI(
1225 $questionGui->object->getId(),
1226 $this->populateCharSelectorIfRequired()
1227 );
1228 if ($isQuestionWorkedThrough) {
1229 $questionNavigationGUI->setDiscardSolutionButtonEnabled(true);
1230 // fau: testNav - set answere status in question header
1231 $questionGui->getQuestionHeaderBlockBuilder()->setQuestionAnswered(true);
1232 // fau.
1233 } elseif ($this->object->isPostponingEnabled()) {
1234 $questionNavigationGUI->setSkipQuestionLinkTarget(
1235 $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::SKIP_QUESTION)
1236 );
1237 }
1238 $questionGui->setNavigationGUI($questionNavigationGUI);
1239
1240 $isPostponed = $this->isShowingPostponeStatusReguired($questionGui->object->getId());
1241
1242 $answerFeedbackEnabled = (
1243 $instantResponse && $this->object->getSpecificAnswerFeedback()
1244 );
1245
1246 if (isset($_GET['save_error']) && $_GET['save_error'] == 1 && isset($_SESSION['previouspost'])) {
1247 $userPostSolution = $_SESSION['previouspost'];
1248 unset($_SESSION['previouspost']);
1249 } else {
1250 $userPostSolution = false;
1251 }
1252
1253 // fau: testNav - add special checkbox for mc question
1254 // moved to another patch block
1255 // fau.
1256
1257 // hey: prevPassSolutions - determine solution pass index and configure gui accordingly
1258 $qstConfig = $questionGui->object->getTestPresentationConfig();
1259
1260 if ($questionGui instanceof assMultipleChoiceGUI) {
1261 $qstConfig->setWorkedThrough($isQuestionWorkedThrough);
1262 $qstConfig->setIsUnchangedAnswerPossible($this->object->getMCScoring());
1263 }
1264
1265 if ($qstConfig->isPreviousPassSolutionReuseAllowed()) {
1266 $passIndex = $this->determineSolutionPassIndex($questionGui); // last pass having solution stored
1267 if ($passIndex < $this->testSession->getPass()) { // it's the previous pass if current pass is higher
1268 $qstConfig->setSolutionInitiallyPrefilled(true);
1269 }
1270 } else {
1271 $passIndex = $this->testSession->getPass();
1272 }
1273 // hey.
1274
1275 // Answer specific feedback is rendered into the display of the test question with in the concrete question types outQuestionForTest-method.
1276 // Notation of the params prior to getting rid of this crap in favor of a class
1277 $questionGui->outQuestionForTest(
1278 $formAction, #form_action
1279 $this->testSession->getActiveId(), #active_id
1280 // hey: prevPassSolutions - prepared pass index having no, current or previous solution
1281 $passIndex, #pass
1282 // hey.
1283 $isPostponed, #is_postponed
1284 $userPostSolution, #user_post_solution
1285 $answerFeedbackEnabled #answer_feedback == inline_specific_feedback
1286 );
1287 // The display of specific inline feedback and specific feedback in an own block is to honor questions, which
1288 // have the possibility to embed the specific feedback into their output while maintaining compatibility to
1289 // questions, which do not have such facilities. E.g. there can be no "specific inline feedback" for essay
1290 // questions, while the multiple-choice questions do well.
1291
1292
1293 $this->populateModals();
1294
1295 // fau: testNav - pouplate the new question edit control instead of the deprecated intermediate solution saver
1296 $this->populateQuestionEditControl($questionGui);
1297 // fau.
1298 }
1299
1300 // hey: prevPassSolutions - determine solution pass index
1301 protected function determineSolutionPassIndex(assQuestionGUI $questionGui)
1302 {
1303 require_once './Modules/Test/classes/class.ilObjTest.php';
1304
1305 if (ilObjTest::_getUsePreviousAnswers($this->testSession->getActiveId(), true)) {
1306 $currentSolutionAvailable = $questionGui->object->authorizedOrIntermediateSolutionExists(
1307 $this->testSession->getActiveId(),
1308 $this->testSession->getPass()
1309 );
1310
1311 if (!$currentSolutionAvailable) {
1312 $previousPass = $questionGui->object->getSolutionMaxPass(
1313 $this->testSession->getActiveId()
1314 );
1315
1316 $previousSolutionAvailable = $questionGui->object->authorizedSolutionExists(
1317 $this->testSession->getActiveId(),
1318 $previousPass
1319 );
1320
1321 if ($previousSolutionAvailable) {
1322 return $previousPass;
1323 }
1324 }
1325 }
1326
1327 return $this->testSession->getPass();
1328 }
1329 // hey.
1330
1331 abstract protected function showQuestionCmd();
1332
1333 abstract protected function editSolutionCmd();
1334
1335 abstract protected function submitSolutionCmd();
1336
1337 // fau: testNav - new function to revert probably auto-saved changes and show the last submitted question state
1338 protected function revertChangesCmd()
1339 {
1341 $this->setAnswerChangedParameter(false);
1342 $this->ctrl->saveParameter($this, 'sequence');
1343 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
1344 }
1345 // fau.
1346
1347 abstract protected function discardSolutionCmd();
1348
1349 abstract protected function skipQuestionCmd();
1350
1351 abstract protected function startTestCmd();
1359 public function checkOnlineTestAccess()
1360 {
1361 global $DIC;
1362 $ilUser = $DIC['ilUser'];
1363
1364 // check if user is invited to participate
1365 $user = $this->object->getInvitedUsers($ilUser->getId());
1366 if (!is_array($user) || count($user) != 1) {
1367 ilUtil::sendInfo($this->lng->txt("user_not_invited"), true);
1368 $this->ctrl->redirectByClass("ilobjtestgui", "backToRepository");
1369 }
1370
1371 $user = array_pop($user);
1372 // check if client ip is set and if current remote addr is equal to stored client-ip
1373 if (strcmp($user["clientip"], "") != 0 && strcmp($user["clientip"], $_SERVER["REMOTE_ADDR"]) != 0) {
1374 ilUtil::sendInfo($this->lng->txt("user_wrong_clientip"), true);
1375 $this->ctrl->redirectByClass("ilobjtestgui", "backToRepository");
1376 }
1377 }
1378
1379
1383 public function isTestAccessible()
1384 {
1385 return !$this->isNrOfTriesReached()
1386 and !$this->isMaxProcessingTimeReached()
1387 and $this->object->startingTimeReached()
1388 and !$this->object->endingTimeReached();
1389 }
1390
1394 public function isNrOfTriesReached()
1395 {
1396 return $this->object->hasNrOfTriesRestriction() && $this->object->isNrOfTriesReached($this->testSession->getPass());
1397 }
1398
1404 public function endingTimeReached()
1405 {
1406 ilUtil::sendInfo(sprintf($this->lng->txt("detail_ending_time_reached"), ilDatePresentation::formatDate(new ilDateTime($this->object->getEndingTime(), IL_CAL_UNIX))));
1407 $this->testSession->increasePass();
1408 $this->testSession->setLastSequence(0);
1409 $this->testSession->saveToDb();
1410
1411 $this->redirectBackCmd();
1412 }
1413
1422 {
1423 $this->suspendTestCmd();
1424 }
1425
1431 public function confirmSubmitAnswers()
1432 {
1433 $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_submit_answers_confirm.html", "Modules/Test");
1434 $this->tpl->setCurrentBlock("adm_content");
1435 if ($this->object->isTestFinished($this->testSession->getActiveId())) {
1436 $this->tpl->setCurrentBlock("not_submit_allowed");
1437 $this->tpl->setVariable("TEXT_ALREADY_SUBMITTED", $this->lng->txt("tst_already_submitted"));
1438 $this->tpl->setVariable("BTN_OK", $this->lng->txt("tst_show_answer_sheet"));
1439 } else {
1440 $this->tpl->setCurrentBlock("submit_allowed");
1441 $this->tpl->setVariable("TEXT_CONFIRM_SUBMIT_RESULTS", $this->lng->txt("tst_confirm_submit_answers"));
1442 $this->tpl->setVariable("BTN_OK", $this->lng->txt("tst_submit_results"));
1443 }
1444 $this->tpl->setVariable("BTN_BACK", $this->lng->txt("back"));
1445 $this->tpl->setVariable("FORMACTION", $this->ctrl->getFormAction($this, "finalSubmission"));
1446 $this->tpl->parseCurrentBlock();
1447 }
1448
1449 public function outProcessingTime($active_id)
1450 {
1451 global $DIC;
1452 $ilUser = $DIC['ilUser'];
1453
1454 $starting_time = $this->object->getStartingTimeOfUser($active_id);
1455 $processing_time = $this->object->getProcessingTimeInSeconds($active_id);
1456 $processing_time_minutes = floor($processing_time / 60);
1457 $processing_time_seconds = $processing_time - $processing_time_minutes * 60;
1458 $str_processing_time = "";
1459 if ($processing_time_minutes > 0) {
1460 $str_processing_time = $processing_time_minutes . " " . ($processing_time_minutes == 1 ? $this->lng->txt("minute") : $this->lng->txt("minutes"));
1461 }
1462 if ($processing_time_seconds > 0) {
1463 if (strlen($str_processing_time) > 0) {
1464 $str_processing_time .= " " . $this->lng->txt("and") . " ";
1465 }
1466 $str_processing_time .= $processing_time_seconds . " " . ($processing_time_seconds == 1 ? $this->lng->txt("second") : $this->lng->txt("seconds"));
1467 }
1468 $time_left = $starting_time + $processing_time - time();
1469 $time_left_minutes = floor($time_left / 60);
1470 $time_left_seconds = $time_left - $time_left_minutes * 60;
1471 $str_time_left = "";
1472 if ($time_left_minutes > 0) {
1473 $str_time_left = $time_left_minutes . " " . ($time_left_minutes == 1 ? $this->lng->txt("minute") : $this->lng->txt("minutes"));
1474 }
1475 if ($time_left < 300) {
1476 if ($time_left_seconds > 0) {
1477 if (strlen($str_time_left) > 0) {
1478 $str_time_left .= " " . $this->lng->txt("and") . " ";
1479 }
1480 $str_time_left .= $time_left_seconds . " " . ($time_left_seconds == 1 ? $this->lng->txt("second") : $this->lng->txt("seconds"));
1481 }
1482 }
1483 $date = getdate($starting_time);
1484 $formattedStartingTime = ilDatePresentation::formatDate(new ilDateTime($date, IL_CAL_FKT_GETDATE));
1485 $datenow = getdate();
1486 $this->tpl->setCurrentBlock("enableprocessingtime");
1487 $this->tpl->setVariable(
1488 "USER_WORKING_TIME",
1489 sprintf(
1490 $this->lng->txt("tst_time_already_spent"),
1491 $formattedStartingTime,
1492 $str_processing_time
1493 )
1494 );
1495 $this->tpl->setVariable("USER_REMAINING_TIME", sprintf($this->lng->txt("tst_time_already_spent_left"), $str_time_left));
1496 $this->tpl->parseCurrentBlock();
1497 $template = new ilTemplate("tpl.workingtime.js.html", true, true, 'Modules/Test');
1498 $template->setVariable("STRING_MINUTE", $this->lng->txt("minute"));
1499 $template->setVariable("STRING_MINUTES", $this->lng->txt("minutes"));
1500 $template->setVariable("STRING_SECOND", $this->lng->txt("second"));
1501 $template->setVariable("STRING_SECONDS", $this->lng->txt("seconds"));
1502 $template->setVariable("STRING_TIMELEFT", $this->lng->txt("tst_time_already_spent_left"));
1503 $template->setVariable("AND", strtolower($this->lng->txt("and")));
1504 $template->setVariable("YEAR", $date["year"]);
1505 $template->setVariable("MONTH", $date["mon"] - 1);
1506 $template->setVariable("DAY", $date["mday"]);
1507 $template->setVariable("HOUR", $date["hours"]);
1508 $template->setVariable("MINUTE", $date["minutes"]);
1509 $template->setVariable("SECOND", $date["seconds"]);
1510 if ($this->object->isEndingTimeEnabled()) {
1511 $date_time = new ilDateTime($this->object->getEndingTime(), IL_CAL_UNIX);
1512 preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $date_time->get(IL_CAL_TIMESTAMP), $matches);
1513 $template->setVariable("ENDYEAR", $matches[1]);
1514 $template->setVariable("ENDMONTH", $matches[2] - 1);
1515 $template->setVariable("ENDDAY", $matches[3]);
1516 $template->setVariable("ENDHOUR", $matches[4]);
1517 $template->setVariable("ENDMINUTE", $matches[5]);
1518 $template->setVariable("ENDSECOND", $matches[6]);
1519 }
1520 $template->setVariable("YEARNOW", $datenow["year"]);
1521 $template->setVariable("MONTHNOW", $datenow["mon"] - 1);
1522 $template->setVariable("DAYNOW", $datenow["mday"]);
1523 $template->setVariable("HOURNOW", $datenow["hours"]);
1524 $template->setVariable("MINUTENOW", $datenow["minutes"]);
1525 $template->setVariable("SECONDNOW", $datenow["seconds"]);
1526 $template->setVariable("PTIME_M", $processing_time_minutes);
1527 $template->setVariable("PTIME_S", $processing_time_seconds);
1528
1529 if($this->ctrl->getCmd() == 'outQuestionSummary') {
1530 $a = $this->ctrl->getFormAction($this, 'redirectAfterDashboardCmd');
1531 $template->setVariable("REDIRECT_URL", $this->ctrl->getFormAction($this, 'redirectAfterDashboardCmd','',false,false));
1532 } else {
1533 $template->setVariable("REDIRECT_URL", "");
1534 }
1535
1536 $this->tpl->setCurrentBlock("HeadContent");
1537 $this->tpl->setVariable("CONTENT_BLOCK", $template->get());
1538 $this->tpl->parseCurrentBlock();
1539
1540 }
1541
1542 protected function showSideList($presentationMode, $currentSequenceElement)
1543 {
1544 global $DIC;
1545 $ilUser = $DIC['ilUser'];
1546
1547 $sideListActive = $ilUser->getPref('side_list_of_questions');
1548
1549 if ($sideListActive) {
1550 $this->tpl->addCss(
1551 ilUtil::getStyleSheetLocation("output", "ta_split.css", "Modules/Test"),
1552 "screen"
1553 );
1554
1555 $questionSummaryData = $this->service->getQuestionSummaryData($this->testSequence, false);
1556
1557 require_once 'Modules/Test/classes/class.ilTestQuestionSideListGUI.php';
1558 $questionSideListGUI = new ilTestQuestionSideListGUI($this->ctrl, $this->lng);
1559 $questionSideListGUI->setTargetGUI($this);
1560 $questionSideListGUI->setQuestionSummaryData($questionSummaryData);
1561 $questionSideListGUI->setCurrentSequenceElement($currentSequenceElement);
1562 // fau: testNav - set side list presentation mode to "view" to allow navigation when question is in edit mode
1563 $questionSideListGUI->setCurrentPresentationMode(ilTestPlayerAbstractGUI::PRESENTATION_MODE_VIEW);
1564 $questionSideListGUI->setDisabled(false);
1565 // $questionSideListGUI->setCurrentPresentationMode($presentationMode);
1566 // $questionSideListGUI->setDisabled($presentationMode == self::PRESENTATION_MODE_EDIT);
1567 // fau.
1568 $this->tpl->setVariable('LIST_OF_QUESTIONS', $questionSideListGUI->getHTML());
1569 }
1570 }
1571
1572 abstract protected function isQuestionSummaryFinishTestButtonRequired();
1573
1577 public function outQuestionSummaryCmd($fullpage = true, $contextFinishTest = false, $obligationsInfo = false, $obligationsFilter = false)
1578 {
1579 if ($fullpage) {
1580 $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_question_summary.html", "Modules/Test");
1581 }
1582
1583 $obligationsFulfilled = \ilObjTest::allObligationsAnswered(
1584 $this->object->getId(),
1585 $this->testSession->getActiveId(),
1586 $this->testSession->getPass()
1587 );
1588
1589 if ($obligationsInfo && $this->object->areObligationsEnabled() && !$obligationsFulfilled) {
1590 ilUtil::sendFailure($this->lng->txt('not_all_obligations_answered'));
1591 }
1592
1593 if ($this->object->getKioskMode() && $fullpage) {
1594 $head = $this->getKioskHead();
1595 if (strlen($head)) {
1596 $this->tpl->setCurrentBlock("kiosk_options");
1597 $this->tpl->setVariable("KIOSK_HEAD", $head);
1598 $this->tpl->parseCurrentBlock();
1599 }
1600 }
1601
1602
1603 $active_id = $this->testSession->getActiveId();
1604 $questionSummaryData = $this->service->getQuestionSummaryData($this->testSequence, $obligationsFilter);
1605
1606 $this->ctrl->setParameter($this, "sequence", $_GET["sequence"]);
1607
1608 if ($fullpage) {
1609 include_once "./Modules/Test/classes/tables/class.ilListOfQuestionsTableGUI.php";
1610 $table_gui = new ilListOfQuestionsTableGUI($this, 'showQuestion');
1611
1612 $table_gui->setShowPointsEnabled(!$this->object->getTitleOutput());
1613 $table_gui->setShowMarkerEnabled($this->object->getShowMarker());
1614 $table_gui->setObligationsNotAnswered(!$obligationsFulfilled);
1615 $table_gui->setShowObligationsEnabled($this->object->areObligationsEnabled());
1616 $table_gui->setObligationsFilterEnabled($obligationsFilter);
1617 $table_gui->setFinishTestButtonEnabled($this->isQuestionSummaryFinishTestButtonRequired());
1618
1619 $table_gui->init();
1620
1621 $table_gui->setData($questionSummaryData);
1622
1623 $this->tpl->setVariable('TABLE_LIST_OF_QUESTIONS', $table_gui->getHTML());
1624
1625 if ($this->object->getEnableProcessingTime()) {
1626 $this->outProcessingTime($active_id);
1627 }
1628
1629 if ($this->object->isShowExamIdInTestPassEnabled()) {
1630 $this->tpl->setCurrentBlock('exam_id_footer');
1631 $this->tpl->setVariable('EXAM_ID_VAL', ilObjTest::lookupExamId(
1632 $this->testSession->getActiveId(),
1633 $this->testSession->getPass(),
1634 $this->object->getId()
1635 ));
1636 $this->tpl->setVariable('EXAM_ID_TXT', $this->lng->txt('exam_id'));
1637 $this->tpl->parseCurrentBlock();
1638 }
1639 }
1640 }
1641
1643 {
1644 return $this->outQuestionSummaryCmd(true, true, true, false);
1645 }
1646
1648 {
1649 return $this->outQuestionSummaryCmd(true, true, true, true);
1650 }
1651
1653 {
1654 $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_max_allowed_users_reached.html", "Modules/Test");
1655 $this->tpl->setCurrentBlock("adm_content");
1656 $this->tpl->setVariable("MAX_ALLOWED_USERS_MESSAGE", sprintf($this->lng->txt("tst_max_allowed_users_message"), $this->object->getAllowedUsersTimeGap()));
1657 $this->tpl->setVariable("MAX_ALLOWED_USERS_HEADING", sprintf($this->lng->txt("tst_max_allowed_users_heading"), $this->object->getAllowedUsersTimeGap()));
1658 $this->tpl->setVariable("CMD_BACK_TO_INFOSCREEN", ilTestPlayerCommands::BACK_TO_INFO_SCREEN);
1659 $this->tpl->setVariable("TXT_BACK_TO_INFOSCREEN", $this->lng->txt("tst_results_back_introduction"));
1660 $this->tpl->setVariable("FORMACTION", $this->ctrl->getFormAction($this));
1661 $this->tpl->parseCurrentBlock();
1662 }
1663
1664 public function backFromFinishingCmd()
1665 {
1666 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
1667 }
1668
1674 public function outCorrectSolution()
1675 {
1676 $this->tpl->addBlockFile("ADM_CONTENT", "adm_content", "tpl.il_as_tst_correct_solution.html", "Modules/Test");
1677
1678 include_once("./Services/Style/Content/classes/class.ilObjStyleSheet.php");
1679 $this->tpl->setCurrentBlock("ContentStyle");
1680 $this->tpl->setVariable("LOCATION_CONTENT_STYLESHEET", ilObjStyleSheet::getContentStylePath(0));
1681 $this->tpl->parseCurrentBlock();
1682
1683 $this->tpl->setCurrentBlock("SyntaxStyle");
1684 $this->tpl->setVariable("LOCATION_SYNTAX_STYLESHEET", ilObjStyleSheet::getSyntaxStylePath());
1685 $this->tpl->parseCurrentBlock();
1686
1687 $this->tpl->addCss(ilUtil::getStyleSheetLocation("output", "test_print.css", "Modules/Test"), "print");
1688 if ($this->object->getShowSolutionAnswersOnly()) {
1689 $this->tpl->addCss(ilUtil::getStyleSheetLocation("output", "test_print_hide_content.css", "Modules/Test"), "print");
1690 }
1691
1692 $this->tpl->setCurrentBlock("adm_content");
1693 $solution = $this->getCorrectSolutionOutput($_GET["evaluation"], $_GET["active_id"], $_GET["pass"]);
1694 $this->tpl->setVariable("OUTPUT_SOLUTION", $solution);
1695 $this->tpl->setVariable("TEXT_BACK", $this->lng->txt("back"));
1696 $this->ctrl->saveParameter($this, "pass");
1697 $this->ctrl->saveParameter($this, "active_id");
1698 $this->tpl->setVariable("URL_BACK", $this->ctrl->getLinkTarget($this, "outUserResultsOverview"));
1699 $this->tpl->parseCurrentBlock();
1700 }
1701
1711 public function showListOfAnswers($active_id, $pass = null, $top_data = "", $bottom_data = "")
1712 {
1713 global $DIC;
1714 $ilUser = $DIC['ilUser'];
1715
1716 $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_finish_list_of_answers.html", "Modules/Test");
1717
1718 $result_array = &$this->object->getTestResult(
1719 $active_id,
1720 $pass,
1721 false,
1722 !$this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()
1723 );
1724
1725 $counter = 1;
1726 // output of questions with solutions
1727 foreach ($result_array as $question_data) {
1728 $question = $question_data["qid"];
1729 if (is_numeric($question)) {
1730 $this->tpl->setCurrentBlock("printview_question");
1731 $question_gui = $this->object->createQuestionGUI("", $question);
1732 $template = new ilTemplate("tpl.il_as_qpl_question_printview.html", true, true, "Modules/TestQuestionPool");
1733 $template->setVariable("COUNTER_QUESTION", $counter . ". ");
1734 $template->setVariable("QUESTION_TITLE", $question_gui->object->getTitle());
1735
1736 $show_question_only = ($this->object->getShowSolutionAnswersOnly()) ? true : false;
1737 $result_output = $question_gui->getSolutionOutput($active_id, $pass, false, false, $show_question_only, $this->object->getShowSolutionFeedback());
1738 $template->setVariable("SOLUTION_OUTPUT", $result_output);
1739 $this->tpl->setVariable("QUESTION_OUTPUT", $template->get());
1740 $this->tpl->parseCurrentBlock();
1741 $counter++;
1742 }
1743 }
1744
1745 $this->tpl->addCss(ilUtil::getStyleSheetLocation("output", "test_print.css", "Modules/Test"), "print");
1746 if ($this->object->getShowSolutionAnswersOnly()) {
1747 $this->tpl->addCss(ilUtil::getStyleSheetLocation("output", "test_print_hide_content.css", "Modules/Test"), "print");
1748 }
1749 if (strlen($top_data)) {
1750 $this->tpl->setCurrentBlock("top_data");
1751 $this->tpl->setVariable("TOP_DATA", $top_data);
1752 $this->tpl->parseCurrentBlock();
1753 }
1754
1755 if (strlen($bottom_data)) {
1756 $this->tpl->setCurrentBlock("bottom_data");
1757 $this->tpl->setVariable("FORMACTION", $this->ctrl->getFormAction($this));
1758 $this->tpl->setVariable("BOTTOM_DATA", $bottom_data);
1759 $this->tpl->parseCurrentBlock();
1760 }
1761
1762 $this->tpl->setCurrentBlock("adm_content");
1763 $this->tpl->setVariable("TXT_ANSWER_SHEET", $this->lng->txt("tst_list_of_answers"));
1764 $user_data = $this->getAdditionalUsrDataHtmlAndPopulateWindowTitle($this->testSession, $active_id, true);
1765 $signature = $this->getResultsSignature();
1766 $this->tpl->setVariable("USER_DETAILS", $user_data);
1767 $this->tpl->setVariable("SIGNATURE", $signature);
1768 $this->tpl->setVariable("TITLE", $this->object->getTitle());
1769 $this->tpl->setVariable("TXT_TEST_PROLOG", $this->lng->txt("tst_your_answers"));
1770 $invited_user = &$this->object->getInvitedUsers($ilUser->getId());
1771 $pagetitle = $this->object->getTitle() . " - " . $this->lng->txt("clientip") .
1772 ": " . $invited_user[$ilUser->getId()]["clientip"] . " - " .
1773 $this->lng->txt("matriculation") . ": " .
1774 $invited_user[$ilUser->getId()]["matriculation"];
1775 $this->tpl->setVariable("PAGETITLE", $pagetitle);
1776 $this->tpl->parseCurrentBlock();
1777 }
1778
1784 public function getContentBlockName()
1785 {
1786 if ($this->object->getKioskMode()) {
1787 $this->tpl->setBodyClass("kiosk");
1788 $this->tpl->setAddFooter(false);
1789 return "CONTENT";
1790 } else {
1791 return "ADM_CONTENT";
1792 }
1793 }
1794
1796 {
1797 $this->ctrl->redirectByClass(
1798 array('ilRepositoryGUI', 'ilObjTestGUI', 'ilTestEvaluationGUI'),
1799 "outUserResultsOverview"
1800 );
1801 }
1802
1806 protected function showRequestedHintListCmd()
1807 {
1808 // fau: testNav - handle intermediate submit for viewing requested hints
1809 $this->handleIntermediateSubmit();
1810 // fau.
1811
1812 $this->ctrl->setParameter($this, 'pmode', self::PRESENTATION_MODE_EDIT);
1813
1814 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintRequestGUI.php';
1815 $this->ctrl->redirectByClass('ilAssQuestionHintRequestGUI', ilAssQuestionHintRequestGUI::CMD_SHOW_LIST);
1816 }
1817
1821 protected function confirmHintRequestCmd()
1822 {
1823 // fau: testNav - handle intermediate submit for confirming hint requests
1824 $this->handleIntermediateSubmit();
1825 // fau.
1826
1827 $this->ctrl->setParameter($this, 'pmode', self::PRESENTATION_MODE_EDIT);
1828
1829 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintRequestGUI.php';
1830 $this->ctrl->redirectByClass('ilAssQuestionHintRequestGUI', ilAssQuestionHintRequestGUI::CMD_CONFIRM_REQUEST);
1831 }
1832
1833 abstract protected function isFirstQuestionInSequence($sequenceElement);
1834
1835 abstract protected function isLastQuestionInSequence($sequenceElement);
1836
1837
1838 abstract protected function handleQuestionActionCmd();
1839
1840 abstract protected function showInstantResponseCmd();
1841
1842 abstract protected function nextQuestionCmd();
1843
1844 abstract protected function previousQuestionCmd();
1845
1846 protected function prepareSummaryPage()
1847 {
1848 $this->tpl->addBlockFile(
1849 $this->getContentBlockName(),
1850 'adm_content',
1851 'tpl.il_as_tst_question_summary.html',
1852 'Modules/Test'
1853 );
1854
1855 if ($this->object->getKioskMode()) {
1856 $this->populateKioskHead();
1857 }
1858 }
1859
1860 protected function initTestPageTemplate()
1861 {
1862 $this->tpl->addBlockFile(
1863 $this->getContentBlockName(),
1864 'adm_content',
1865 'tpl.il_as_tst_output.html',
1866 'Modules/Test'
1867 );
1868 }
1869
1870 protected function populateKioskHead()
1871 {
1872 ilUtil::sendInfo(); // ???
1873
1874 $head = $this->getKioskHead();
1875
1876 if (strlen($head)) {
1877 $this->tpl->setCurrentBlock("kiosk_options");
1878 $this->tpl->setVariable("KIOSK_HEAD", $head);
1879 $this->tpl->parseCurrentBlock();
1880 }
1881 }
1882
1884 {
1885 if ($this->ctrl->getNextClass() == 'iltestpasswordprotectiongui') {
1886 return;
1887 }
1888
1889 if (!$this->passwordChecker->isPasswordProtectionPageRedirectRequired()) {
1890 return;
1891 }
1892
1893 $this->ctrl->setParameter($this, 'lock', $this->getLockParameter());
1894
1895 $nextCommand = $this->ctrl->getCmdClass() . '::' . $this->ctrl->getCmd();
1896 $this->ctrl->setParameterByClass('ilTestPasswordProtectionGUI', 'nextCommand', $nextCommand);
1897 $this->ctrl->redirectByClass('ilTestPasswordProtectionGUI', 'showPasswordForm');
1898 }
1899
1900 protected function isParticipantsAnswerFixed($questionId)
1901 {
1902 if ($this->object->isInstantFeedbackAnswerFixationEnabled() && $this->testSequence->isQuestionChecked($questionId)) {
1903 return true;
1904 }
1905
1906 if ($this->object->isFollowupQuestionAnswerFixationEnabled() && $this->testSequence->isNextQuestionPresented($questionId)) {
1907 return true;
1908 }
1909
1910 return false;
1911 }
1912
1917 {
1918 return $this->lng->txt("save_introduction");
1919 }
1920
1921 protected function initAssessmentSettings()
1922 {
1923 $this->assSettings = new ilSetting('assessment');
1924 }
1925
1930 {
1931 $questionList = $this->buildTestPassQuestionList();
1932 $questionList->load();
1933
1934 $testResults = $this->object->getTestResult($testSession->getActiveId(), $testSession->getPass(), true);
1935
1936 require_once 'Modules/Test/classes/class.ilTestSkillEvaluation.php';
1937 $skillEvaluation = new ilTestSkillEvaluation($this->db, $this->object->getTestId(), $this->object->getRefId());
1938
1939 $skillEvaluation->setUserId($testSession->getUserId());
1940 $skillEvaluation->setActiveId($testSession->getActiveId());
1941 $skillEvaluation->setPass($testSession->getPass());
1942
1943 $skillEvaluation->setNumRequiredBookingsForSkillTriggering($this->assSettings->get(
1944 'ass_skl_trig_num_answ_barrier',
1946 ));
1947
1948
1949 $skillEvaluation->init($questionList);
1950 $skillEvaluation->evaluate($testResults);
1951
1952 $skillEvaluation->handleSkillTriggering();
1953 }
1954
1955 abstract protected function buildTestPassQuestionList();
1956
1958 {
1959 require_once 'Modules/Test/classes/confirmations/class.ilTestAnswerOptionalQuestionsConfirmationGUI.php';
1960 $confirmation = new ilTestAnswerOptionalQuestionsConfirmationGUI($this->lng);
1961
1962 $confirmation->setFormAction($this->ctrl->getFormAction($this));
1963 $confirmation->setCancelCmd('cancelAnswerOptionalQuestions');
1964 $confirmation->setConfirmCmd('confirmAnswerOptionalQuestions');
1965
1966 $confirmation->build($this->object->isFixedTest());
1967
1968 $this->populateHelperGuiContent($confirmation);
1969 }
1970
1972 {
1973 $this->testSequence->setAnsweringOptionalQuestionsConfirmed(true);
1974 $this->testSequence->saveToDb();
1975
1976 $this->ctrl->setParameter($this, 'activecommand', 'gotoquestion');
1977 $this->ctrl->redirect($this, 'redirectQuestion');
1978 }
1979
1981 {
1982 if ($this->object->getListOfQuestions()) {
1983 $this->ctrl->setParameter($this, 'activecommand', 'summary');
1984 } else {
1985 $this->ctrl->setParameter($this, 'activecommand', 'previous');
1986 }
1987
1988 $this->ctrl->redirect($this, 'redirectQuestion');
1989 }
1990
1994 protected function populateHelperGuiContent($helperGui)
1995 {
1996 if ($this->object->getKioskMode()) {
1997 $this->tpl->setBodyClass("kiosk");
1998 //$this->tpl->hideFooter();
1999 $this->tpl->addBlockfile('CONTENT', 'content', "tpl.il_as_tst_kiosk_mode_content.html", "Modules/Test");
2000 $this->tpl->setContent($this->ctrl->getHTML($helperGui));
2001 } else {
2002 $this->tpl->setVariable($this->getContentBlockName(), $this->ctrl->getHTML($helperGui));
2003 }
2004 }
2005
2010 {
2011 global $DIC;
2012 $ilSetting = $DIC['ilSetting'];
2013
2014 if ($ilSetting->get('char_selector_availability') > 0) {
2015 require_once 'Services/UIComponent/CharSelector/classes/class.ilCharSelectorGUI.php';
2016 $char_selector = ilCharSelectorGUI::_getCurrentGUI($this->object);
2017 if ($char_selector->getConfig()->getAvailability() == ilCharSelectorConfig::ENABLED) {
2018 $char_selector->addToPage();
2019 $this->tpl->setCurrentBlock('char_selector');
2020 $this->tpl->setVariable("CHAR_SELECTOR_TEMPLATE", $char_selector->getSelectorHtml());
2021 $this->tpl->parseCurrentBlock();
2022
2023 return true;
2024 }
2025 }
2026
2027 return false;
2028 }
2029
2030 protected function getTestNavigationToolbarGUI()
2031 {
2032 global $DIC;
2033 $ilUser = $DIC['ilUser'];
2034
2035 require_once 'Modules/Test/classes/class.ilTestNavigationToolbarGUI.php';
2036 $navigationToolbarGUI = new ilTestNavigationToolbarGUI($this->ctrl, $this->lng, $this);
2037
2038 $navigationToolbarGUI->setSuspendTestButtonEnabled($this->object->getShowCancel());
2039 $navigationToolbarGUI->setQuestionTreeButtonEnabled($this->object->getListOfQuestions());
2040 $navigationToolbarGUI->setQuestionTreeVisible($ilUser->getPref('side_list_of_questions'));
2041 $navigationToolbarGUI->setQuestionListButtonEnabled($this->object->getListOfQuestions());
2042 $navigationToolbarGUI->setFinishTestCommand($this->getFinishTestCommand());
2043
2044 return $navigationToolbarGUI;
2045 }
2046
2047 protected function buildReadOnlyStateQuestionNavigationGUI($questionId)
2048 {
2049 require_once 'Modules/Test/classes/class.ilTestQuestionNavigationGUI.php';
2050 $navigationGUI = new ilTestQuestionNavigationGUI($this->lng);
2051
2052 if (!$this->isParticipantsAnswerFixed($questionId)) {
2053 $navigationGUI->setEditSolutionCommand(ilTestPlayerCommands::EDIT_SOLUTION);
2054 }
2055
2056 if ($this->object->getShowMarker()) {
2057 include_once "./Modules/Test/classes/class.ilObjTest.php";
2058 $solved_array = ilObjTest::_getSolvedQuestions($this->testSession->getActiveId(), $questionId);
2059 $solved = 0;
2060
2061 if (count($solved_array) > 0) {
2062 $solved = array_pop($solved_array);
2063 $solved = $solved["solved"];
2064 }
2065 // fau: testNav - change question mark command to link target
2066 if ($solved == 1) {
2067 $navigationGUI->setQuestionMarkLinkTarget($this->ctrl->getLinkTarget($this, ilTestPlayerCommands::UNMARK_QUESTION));
2068 $navigationGUI->setQuestionMarked(true);
2069 } else {
2070 $navigationGUI->setQuestionMarkLinkTarget($this->ctrl->getLinkTarget($this, ilTestPlayerCommands::MARK_QUESTION));
2071 $navigationGUI->setQuestionMarked(false);
2072 }
2073 }
2074 // fau.
2075
2076 return $navigationGUI;
2077 }
2078
2079 protected function buildEditableStateQuestionNavigationGUI($questionId, $charSelectorAvailable)
2080 {
2081 require_once 'Modules/Test/classes/class.ilTestQuestionNavigationGUI.php';
2082 $navigationGUI = new ilTestQuestionNavigationGUI($this->lng);
2083
2084 if ($this->object->isForceInstantFeedbackEnabled()) {
2085 $navigationGUI->setSubmitSolutionCommand(ilTestPlayerCommands::SUBMIT_SOLUTION);
2086 } else {
2087 // fau: testNav - use simple "submitSolution" button instead of "submitSolutionAndNext"
2088 $navigationGUI->setSubmitSolutionCommand(ilTestPlayerCommands::SUBMIT_SOLUTION);
2089 // fau.
2090 }
2091
2092 // fau: testNav - add a 'revert changes' link for editable question
2093 $navigationGUI->setRevertChangesLinkTarget($this->ctrl->getLinkTarget($this, ilTestPlayerCommands::REVERT_CHANGES));
2094 // fau.
2095
2096
2097 // feedback
2098 switch (1) {
2099 case $this->object->getSpecificAnswerFeedback():
2100 case $this->object->getGenericAnswerFeedback():
2101 case $this->object->getAnswerFeedbackPoints():
2102 case $this->object->getInstantFeedbackSolution():
2103
2104 $navigationGUI->setAnswerFreezingEnabled($this->object->isInstantFeedbackAnswerFixationEnabled());
2105
2106 if ($this->object->isForceInstantFeedbackEnabled()) {
2107 $navigationGUI->setForceInstantResponseEnabled(true);
2108 $navigationGUI->setInstantFeedbackCommand(ilTestPlayerCommands::SUBMIT_SOLUTION);
2109 } else {
2110 $navigationGUI->setInstantFeedbackCommand(ilTestPlayerCommands::SHOW_INSTANT_RESPONSE);
2111 }
2112 }
2113
2114 // hints
2115 if ($this->object->isOfferingQuestionHintsEnabled()) {
2116 $activeId = $this->testSession->getActiveId();
2117 $pass = $this->testSession->getPass();
2118
2119 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
2120 $questionHintTracking = new ilAssQuestionHintTracking($questionId, $activeId, $pass);
2121
2122 if ($questionHintTracking->requestsPossible()) {
2123 $navigationGUI->setRequestHintCommand(ilTestPlayerCommands::CONFIRM_HINT_REQUEST);
2124 }
2125
2126 if ($questionHintTracking->requestsExist()) {
2127 $navigationGUI->setShowHintsCommand(ilTestPlayerCommands::SHOW_REQUESTED_HINTS_LIST);
2128 }
2129 }
2130
2131 $navigationGUI->setCharSelectorEnabled($charSelectorAvailable);
2132
2133 if ($this->object->getShowMarker()) {
2134 include_once "./Modules/Test/classes/class.ilObjTest.php";
2135 $solved_array = ilObjTest::_getSolvedQuestions($this->testSession->getActiveId(), $questionId);
2136 $solved = 0;
2137
2138 if (count($solved_array) > 0) {
2139 $solved = array_pop($solved_array);
2140 $solved = $solved["solved"];
2141 }
2142
2143 // fau: testNav - change question mark command to link target
2144 if ($solved == 1) {
2145 $navigationGUI->setQuestionMarkLinkTarget($this->ctrl->getLinkTarget($this, ilTestPlayerCommands::UNMARK_QUESTION_SAVE));
2146 $navigationGUI->setQuestionMarked(true);
2147 } else {
2148 $navigationGUI->setQuestionMarkLinkTarget($this->ctrl->getLinkTarget($this, ilTestPlayerCommands::MARK_QUESTION_SAVE));
2149 $navigationGUI->setQuestionMarked(false);
2150 }
2151 }
2152 // fau.
2153 return $navigationGUI;
2154 }
2155
2159 protected function getFinishTestCommand()
2160 {
2161 if (!$this->object->getListOfQuestionsEnd()) {
2162 return 'finishTest';
2163 }
2164
2165 if ($this->object->areObligationsEnabled()) {
2166 $allObligationsAnswered = ilObjTest::allObligationsAnswered(
2167 $this->testSession->getTestId(),
2168 $this->testSession->getActiveId(),
2169 $this->testSession->getPass()
2170 );
2171
2172 if (!$allObligationsAnswered) {
2173 return 'outQuestionSummaryWithObligationsInfo';
2174 }
2175 }
2176
2177 return 'outQuestionSummary';
2178 }
2179
2180 // fau: testNav - populateIntermediateSolutionSaver is obsolete and can be deletd.
2181 // /**
2182 // * @param assQuestionGUI $questionGui
2183 // */
2184 // protected function populateIntermediateSolutionSaver(assQuestionGUI $questionGui)
2185 // {
2186 // $this->tpl->addJavaScript(ilUtil::getJSLocation("autosave.js", "Modules/Test"));
2187//
2188 // $this->tpl->setVariable("AUTOSAVE_URL", $this->ctrl->getFormAction(
2189 // $this, ilTestPlayerCommands::AUTO_SAVE, "", true
2190 // ));
2191//
2192 // if( $questionGui->isAutosaveable() && $this->object->getAutosave() )
2193 // {
2194 // $formAction = $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::AUTO_SAVE, '', false, false);
2195//
2196 // $this->tpl->touchBlock('autosave');
2197 // $this->tpl->setVariable("AUTOSAVEFORMACTION", $formAction);
2198 // $this->tpl->setVariable("AUTOSAVEINTERVAL", $this->object->getAutosaveIval());
2199 // }
2200 // }
2201 // fau.
2202
2203 // fau: testNav - new function populateInstantResponseModal()
2204 protected function populateInstantResponseModal(assQuestionGUI $questionGui, $navUrl)
2205 {
2206 $questionGui->setNavigationGUI(null);
2207 $questionGui->getQuestionHeaderBlockBuilder()->setQuestionAnswered(true);
2208
2209 $answerFeedbackEnabled = $this->object->getSpecificAnswerFeedback();
2210
2211 $solutionoutput = $questionGui->getSolutionOutput(
2212 $this->testSession->getActiveId(), #active_id
2213 $this->testSession->getPass(), #pass
2214 false, #graphical_output
2215 false, #result_output
2216 true, #show_question_only
2217 $answerFeedbackEnabled, #show_feedback
2218 false, #show_correct_solution
2219 false, #show_manual_scoring
2220 true #show_question_text
2221 );
2222
2223 $pageoutput = $questionGui->outQuestionPage(
2224 "",
2225 $this->isShowingPostponeStatusReguired($questionGui->object->getId()),
2226 $this->testSession->getActiveId(),
2227 $solutionoutput
2228 );
2229
2230 $tpl = new ilTemplate('tpl.tst_player_response_modal.html', true, true, 'Modules/Test');
2231
2232 // populate the instant response blocks in the
2233 $saved_tpl = $this->tpl;
2234 $this->tpl = $tpl;
2235 $this->populateInstantResponseBlocks($questionGui, true);
2236 $this->tpl = $saved_tpl;
2237
2238 $tpl->setVariable('QUESTION_OUTPUT', $pageoutput);
2239
2240 $button = ilLinkButton::getInstance();
2241 $button->setId('tst_confirm_feedback');
2242 $button->setUrl($navUrl);
2243 $button->setCaption('proceed');
2244 $button->setPrimary(true);
2245 $tpl->setVariable('BUTTON', $button->render());
2246
2247
2248 require_once('Services/UIComponent/Modal/classes/class.ilModalGUI.php');
2249 $modal = ilModalGUI::getInstance();
2250 $modal->setType(ilModalGUI::TYPE_LARGE);
2251 $modal->setId('tst_question_feedback_modal');
2252 $modal->setHeading($this->lng->txt('tst_instant_feedback'));
2253 $modal->setBody($tpl->get());
2254
2255 $this->tpl->addOnLoadCode("$('#tst_question_feedback_modal').modal('show');");
2256 $this->tpl->setVariable('INSTANT_RESPONSE_MODAL', $modal->getHTML());
2257 }
2258 // fau;
2259
2260
2264 protected function populateInstantResponseBlocks(assQuestionGUI $questionGui, $authorizedSolution)
2265 {
2267 !$this->object->getSpecificAnswerFeedback() || !$questionGui->hasInlineFeedback()
2268 );
2269
2270 // This controls if the solution should be shown.
2271 // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Solutions"
2272 if ($this->object->getInstantFeedbackSolution()) {
2273 $show_question_inline_score = $this->determineInlineScoreDisplay();
2274
2275 // Notation of the params prior to getting rid of this crap in favor of a class
2276 $solutionoutput = $questionGui->getSolutionOutput(
2277 $this->testSession->getActiveId(), #active_id
2278 null, #pass
2279 false, #graphical_output
2280 $show_question_inline_score, #result_output
2281 true, #show_question_only
2282 false, #show_feedback
2283 true, #show_correct_solution
2284 false, #show_manual_scoring
2285 false #show_question_text
2286 );
2287 $solutionoutput = str_replace('<h1 class="ilc_page_title_PageTitle"></h1>', '', $solutionoutput);
2288 $this->populateSolutionBlock($solutionoutput);
2289 }
2290
2291 $reachedPoints = $questionGui->object->getAdjustedReachedPoints(
2292 $this->testSession->getActiveId(),
2293 null,
2294 $authorizedSolution
2295 );
2296
2297 $maxPoints = $questionGui->object->getMaximumPoints();
2298
2299 $solutionCorrect = ($reachedPoints == $maxPoints);
2300
2301 // This controls if the score should be shown.
2302 // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Results (Only Points)"
2303 if ($this->object->getAnswerFeedbackPoints()) {
2304 $this->populateScoreBlock($reachedPoints, $maxPoints);
2305 }
2306
2307 // This controls if the generic feedback should be shown.
2308 // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Solutions"
2309 if ($this->object->getGenericAnswerFeedback()) {
2310 $this->populateGenericFeedbackBlock($questionGui, $solutionCorrect);
2311 }
2312
2313 // This controls if the specific feedback should be shown.
2314 // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Answer-Specific Feedback"
2315 if ($this->object->getSpecificAnswerFeedback()) {
2316 $this->populateSpecificFeedbackBlock($questionGui);
2317 }
2318 }
2319
2320 protected function populateFeedbackBlockHeader($withFocusAnchor)
2321 {
2322 if ($withFocusAnchor) {
2323 $this->tpl->setCurrentBlock('inst_resp_id');
2324 $this->tpl->setVariable('INSTANT_RESPONSE_FOCUS_ID', 'focus');
2325 $this->tpl->parseCurrentBlock();
2326 }
2327
2328 $this->tpl->setCurrentBlock('instant_response_header');
2329 $this->tpl->setVariable('INSTANT_RESPONSE_HEADER', $this->lng->txt('tst_feedback'));
2330 $this->tpl->parseCurrentBlock();
2331 }
2332
2333 protected function getCurrentSequenceElement()
2334 {
2335 if ($this->getSequenceElementParameter()) {
2336 return $this->getSequenceElementParameter();
2337 }
2338
2339 return $this->testSession->getLastSequence();
2340 }
2341
2343 {
2344 unset($_GET['sequence']);
2345 $this->ctrl->setParameter($this, 'sequence', null);
2346 }
2347
2348 protected function getSequenceElementParameter()
2349 {
2350 if (isset($_GET['sequence'])) {
2351 return $_GET['sequence'];
2352 }
2353
2354 return null;
2355 }
2356
2357 protected function getPresentationModeParameter()
2358 {
2359 if (isset($_GET['pmode'])) {
2360 return $_GET['pmode'];
2361 }
2362
2363 return null;
2364 }
2365
2366 protected function getInstantResponseParameter()
2367 {
2368 if (isset($_GET['instresp'])) {
2369 return $_GET['instresp'];
2370 }
2371
2372 return null;
2373 }
2374
2375 protected function getNextCommandParameter()
2376 {
2377 if (isset($_POST['nextcmd']) && strlen($_POST['nextcmd'])) {
2378 return $_POST['nextcmd'];
2379 }
2380
2381 return null;
2382 }
2383
2384 protected function getNextSequenceParameter()
2385 {
2386 if (isset($_POST['nextseq']) && is_numeric($_POST['nextseq'])) {
2387 return (int) $_POST['nextseq'];
2388 }
2389
2390 return null;
2391 }
2392
2393 // fau: testNav - get the navigation url set by a submit from ilTestPlayerNavigationControl.js
2394 protected function getNavigationUrlParameter()
2395 {
2396 if (isset($_POST['test_player_navigation_url'])) {
2397 return $_POST['test_player_navigation_url'];
2398 }
2399 return null;
2400 }
2401 // fau.
2402
2403 // fau: testNav - get set and check the 'answer_changed' url parameter
2409 protected function getAnswerChangedParameter()
2410 {
2411 return !empty($_GET['test_answer_changed']);
2412 }
2413
2418 protected function setAnswerChangedParameter($changed = true)
2419 {
2420 $this->ctrl->setParameter($this, 'test_answer_changed', $changed ? '1' : '0');
2421 }
2422
2423
2429 protected function handleIntermediateSubmit()
2430 {
2431 if ($this->getAnswerChangedParameter()) {
2432 $this->saveQuestionSolution(false);
2433 } else {
2435 }
2437 }
2438 // fau.
2439
2440 // fau: testNav - save the switch to prevent the navigation confirmation
2445 {
2446 if (!empty($_POST['save_on_navigation_prevent_confirmation'])) {
2447 $_SESSION['save_on_navigation_prevent_confirmation'] = true;
2448 }
2449
2450 if (!empty($_POST[self::FOLLOWUP_QST_LOCKS_PREVENT_CONFIRMATION_PARAM])) {
2452 }
2453 }
2454 // fau.
2455
2459 private $cachedQuestionGuis = array();
2460
2466 protected function getQuestionGuiInstance($questionId, $fromCache = true)
2467 {
2468 global $DIC;
2469 $tpl = $DIC['tpl'];
2470
2471 if (!$fromCache || !isset($this->cachedQuestionGuis[$questionId])) {
2472 $questionGui = $this->object->createQuestionGUI("", $questionId);
2473 $questionGui->setTargetGui($this);
2474 $questionGui->setPresentationContext(assQuestionGUI::PRESENTATION_CONTEXT_TEST);
2475 $questionGui->object->setObligationsToBeConsidered($this->object->areObligationsEnabled());
2476 $questionGui->populateJavascriptFilesRequiredForWorkForm($tpl);
2477 $questionGui->object->setOutputType(OUTPUT_JAVASCRIPT);
2478 $questionGui->object->setShuffler($this->buildQuestionAnswerShuffler($questionId));
2479
2480 // hey: prevPassSolutions - determine solution pass index and configure gui accordingly
2481 $this->initTestQuestionConfig($questionGui->object);
2482 // hey.
2483
2484 $this->cachedQuestionGuis[$questionId] = $questionGui;
2485 }
2486
2487 return $this->cachedQuestionGuis[$questionId];
2488 }
2489
2493 private $cachedQuestionObjects = array();
2494
2499 protected function getQuestionInstance($questionId, $fromCache = true)
2500 {
2501 global $DIC;
2502 $ilDB = $DIC['ilDB'];
2503 $ilUser = $DIC['ilUser'];
2504
2505 if (!$fromCache || !isset($this->cachedQuestionObjects[$questionId])) {
2506 $questionOBJ = assQuestion::_instantiateQuestion($questionId);
2507
2508 $assSettings = new ilSetting('assessment');
2509 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionProcessLockerFactory.php';
2510 $processLockerFactory = new ilAssQuestionProcessLockerFactory($assSettings, $ilDB);
2511 $processLockerFactory->setQuestionId($questionOBJ->getId());
2512 $processLockerFactory->setUserId($ilUser->getId());
2513 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
2514 $processLockerFactory->setAssessmentLogEnabled(ilObjAssessmentFolder::_enabledAssessmentLogging());
2515 $questionOBJ->setProcessLocker($processLockerFactory->getLocker());
2516
2517 $questionOBJ->setObligationsToBeConsidered($this->object->areObligationsEnabled());
2518 $questionOBJ->setOutputType(OUTPUT_JAVASCRIPT);
2519
2520 // hey: prevPassSolutions - determine solution pass index and configure gui accordingly
2521 $this->initTestQuestionConfig($questionOBJ);
2522 // hey.
2523
2524 $this->cachedQuestionObjects[$questionId] = $questionOBJ;
2525 }
2526
2527 return $this->cachedQuestionObjects[$questionId];
2528 }
2529
2530 // hey: prevPassSolutions - determine solution pass index and configure gui accordingly
2531 protected function initTestQuestionConfig(assQuestion $questionOBJ)
2532 {
2533 $questionOBJ->getTestPresentationConfig()->setPreviousPassSolutionReuseAllowed(
2534 $this->object->isPreviousSolutionReuseEnabled($this->testSession->getActiveId())
2535 );
2536 }
2537 // hey.
2538
2543 protected function buildQuestionAnswerShuffler($questionId)
2544 {
2545 require_once 'Services/Randomization/classes/class.ilArrayElementShuffler.php';
2546 $shuffler = new ilArrayElementShuffler();
2547
2548 $fixedSeed = $this->buildFixedShufflerSeed($questionId);
2549 $shuffler->setSeed($fixedSeed);
2550
2551 return $shuffler;
2552 }
2553
2559 protected function handleTearsAndAngerQuestionIsNull($questionId, $sequenceElement)
2560 {
2561 global $DIC;
2562 $ilLog = $DIC['ilLog'];
2563
2564 $ilLog->write(
2565 "INV SEQ:"
2566 . "active={$this->testSession->getActiveId()} "
2567 . "qId=$questionId seq=$sequenceElement "
2568 . serialize($this->testSequence)
2569 );
2570
2571 $ilLog->logStack('INV SEQ');
2572
2573 $this->ctrl->setParameter($this, 'sequence', $this->testSequence->getFirstSequence());
2574 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
2575 }
2576
2580 protected function populateMessageContent($contentHTML)
2581 {
2582 if ($this->object->getKioskMode()) {
2583 $this->tpl->addBlockfile($this->getContentBlockName(), 'content', "tpl.il_as_tst_kiosk_mode_content.html", "Modules/Test");
2584 $this->tpl->setContent($contentHTML);
2585 } else {
2586 $this->tpl->setVariable($this->getContentBlockName(), $contentHTML);
2587 }
2588 }
2589
2590 protected function populateModals()
2591 {
2592 require_once 'Services/UIComponent/Button/classes/class.ilSubmitButton.php';
2593 require_once 'Services/UIComponent/Button/classes/class.ilLinkButton.php';
2594 require_once 'Services/UIComponent/Modal/classes/class.ilModalGUI.php';
2595
2597 // fau: testNav - populateNavWhenChangedModal instead of populateNavWhileEditModal
2599 // fau.
2600
2601 if ($this->object->isFollowupQuestionAnswerFixationEnabled()) {
2603
2605 }
2606
2607 if ($this->object->getKioskMode()) {
2608 $this->tpl->addJavaScript(ilUIFramework::BOWER_BOOTSTRAP_JS, true);
2609 }
2610 }
2611
2612 protected function populateDiscardSolutionModal()
2613 {
2614 $tpl = new ilTemplate('tpl.tst_player_confirmation_modal.html', true, true, 'Modules/Test');
2615
2616 $tpl->setVariable('CONFIRMATION_TEXT', $this->lng->txt('discard_answer_confirmation'));
2617
2618 $button = ilSubmitButton::getInstance();
2619 $button->setCommand(ilTestPlayerCommands::DISCARD_SOLUTION);
2620 $button->setCaption('discard_answer');
2621 $tpl->setCurrentBlock('buttons');
2622 $tpl->setVariable('BUTTON', $button->render());
2623 $tpl->parseCurrentBlock();
2624
2625 $button = ilLinkButton::getInstance();
2626 $button->setId('tst_cancel_discard_button');
2627 $button->setCaption('cancel');
2628 $button->setPrimary(true);
2629 $tpl->setCurrentBlock('buttons');
2630 $tpl->setVariable('BUTTON', $button->render());
2631 $tpl->parseCurrentBlock();
2632
2633 $modal = ilModalGUI::getInstance();
2634 $modal->setId('tst_discard_solution_modal');
2635 $modal->setHeading($this->lng->txt('discard_answer'));
2636 $modal->setBody($tpl->get());
2637
2638 $this->tpl->setCurrentBlock('discard_solution_modal');
2639 $this->tpl->setVariable('DISCARD_SOLUTION_MODAL', $modal->getHTML());
2640 $this->tpl->parseCurrentBlock();
2641
2642 // fau: testNav - the discard solution modal is now handled by ilTestPlayerNavigationControl.js
2643// $this->tpl->addJavaScript('Modules/Test/js/ilTestPlayerDiscardSolutionModal.js', true);
2644// fau.
2645 }
2646
2647 // fau: testNav - populateNavWhileEditModal is obsolete and can be deleted.
2648 // protected function populateNavWhileEditModal()
2649 // {
2650 // require_once 'Services/Form/classes/class.ilFormPropertyGUI.php';
2651 // require_once 'Services/Form/classes/class.ilHiddenInputGUI.php';
2652//
2653 // $tpl = new ilTemplate('tpl.tst_player_confirmation_modal.html', true, true, 'Modules/Test');
2654//
2655 // $tpl->setVariable('CONFIRMATION_TEXT', $this->lng->txt('tst_nav_while_edit_modal_text'));
2656//
2657 // $button = ilSubmitButton::getInstance();
2658 // $button->setCommand(ilTestPlayerCommands::SUBMIT_SOLUTION);
2659 // $button->setCaption('tst_nav_while_edit_modal_save_btn');
2660 // $button->setPrimary(true);
2661 // $tpl->setCurrentBlock('buttons');
2662 // $tpl->setVariable('BUTTON', $button->render());
2663 // $tpl->parseCurrentBlock();
2664//
2665 // foreach(array('nextcmd', 'nextseq') as $hiddenPostVar)
2666 // {
2667 // $nextCmdInp = new ilHiddenInputGUI($hiddenPostVar);
2668 // $nextCmdInp->setValue('');
2669 // $tpl->setCurrentBlock('hidden_inputs');
2670 // $tpl->setVariable('HIDDEN_INPUT', $nextCmdInp->getToolbarHTML());
2671 // $tpl->parseCurrentBlock();
2672 // }
2673//
2674 // $button = ilLinkButton::getInstance();
2675 // $this->ctrl->setParameter($this, 'pmode', self::PRESENTATION_MODE_VIEW);
2676 // $button->setId('nextCmdLink');
2677 // $button->setUrl('#');
2678 // $this->ctrl->setParameter($this, 'pmode', $this->getPresentationModeParameter());
2679 // $button->setCaption('tst_nav_while_edit_modal_nosave_btn');
2680 // $tpl->setCurrentBlock('buttons');
2681 // $tpl->setVariable('BUTTON', $button->render());
2682 // $tpl->parseCurrentBlock();
2683//
2684 // $button = ilLinkButton::getInstance();
2685 // $button->setId('tst_cancel_nav_while_edit_button');
2686 // $button->setCaption('tst_nav_while_edit_modal_cancel_btn');
2687 // $tpl->setCurrentBlock('buttons');
2688 // $tpl->setVariable('BUTTON', $button->render());
2689 // $tpl->parseCurrentBlock();
2690//
2691 // $modal = ilModalGUI::getInstance();
2692 // $modal->setId('tst_nav_while_edit_modal');
2693 // $modal->setHeading($this->lng->txt('tst_nav_while_edit_modal_header'));
2694 // $modal->setBody($tpl->get());
2695//
2696 // $this->tpl->setCurrentBlock('nav_while_edit_modal');
2697 // $this->tpl->setVariable('NAV_WHILE_EDIT_MODAL', $modal->getHTML());
2698 // $this->tpl->parseCurrentBlock();
2699//
2700 // $this->tpl->addJavaScript('Modules/Test/js/ilTestPlayerNavWhileEditModal.js', true);
2701 // }
2702 // fau.
2703
2704 // fau: testNav - new function populateNavWhenChangedModal
2705 protected function populateNavWhenChangedModal()
2706 {
2707 return; // usibility fix: get rid of popup
2708
2709 if (!empty($_SESSION['save_on_navigation_prevent_confirmation'])) {
2710 return;
2711 }
2712
2713 $tpl = new ilTemplate('tpl.tst_player_confirmation_modal.html', true, true, 'Modules/Test');
2714
2715 if ($this->object->isInstantFeedbackAnswerFixationEnabled() && $this->object->isForceInstantFeedbackEnabled()) {
2716 $text = $this->lng->txt('save_on_navigation_locked_confirmation');
2717 } else {
2718 $text = $this->lng->txt('save_on_navigation_confirmation');
2719 }
2720 if ($this->object->isForceInstantFeedbackEnabled()) {
2721 $text .= " " . $this->lng->txt('save_on_navigation_forced_feedback_hint');
2722 }
2723 $tpl->setVariable('CONFIRMATION_TEXT', $text);
2724
2725
2726 $button = ilLinkButton::getInstance();
2727 $button->setId('tst_save_on_navigation_button');
2728 $button->setUrl('#');
2729 $button->setCaption('tst_save_and_proceed');
2730 $button->setPrimary(true);
2731 $tpl->setCurrentBlock('buttons');
2732 $tpl->setVariable('BUTTON', $button->render());
2733 $tpl->parseCurrentBlock();
2734
2735 $button = ilLinkButton::getInstance();
2736 $button->setId('tst_cancel_on_navigation_button');
2737 $button->setUrl('#');
2738 $button->setCaption('cancel');
2739 $button->setPrimary(false);
2740 $tpl->setCurrentBlock('buttons');
2741 $tpl->setVariable('BUTTON', $button->render());
2742 $tpl->parseCurrentBlock();
2743
2744 $tpl->setCurrentBlock('checkbox');
2745 $tpl->setVariable('CONFIRMATION_CHECKBOX_NAME', 'save_on_navigation_prevent_confirmation');
2746 $tpl->setVariable('CONFIRMATION_CHECKBOX_LABEL', $this->lng->txt('tst_dont_show_msg_again_in_current_session'));
2747 $tpl->parseCurrentBlock();
2748
2749 $modal = ilModalGUI::getInstance();
2750 $modal->setId('tst_save_on_navigation_modal');
2751 $modal->setHeading($this->lng->txt('save_on_navigation'));
2752 $modal->setBody($tpl->get());
2753
2754 $this->tpl->setCurrentBlock('nav_while_edit_modal');
2755 $this->tpl->setVariable('NAV_WHILE_EDIT_MODAL', $modal->getHTML());
2756 $this->tpl->parseCurrentBlock();
2757 }
2758 // fau.
2759
2761 {
2762 require_once 'Modules/Test/classes/class.ilTestPlayerConfirmationModal.php';
2763 $modal = new ilTestPlayerConfirmationModal();
2764 $modal->setModalId('tst_next_locks_unchanged_modal');
2765
2766 $modal->setHeaderText($this->lng->txt('tst_nav_next_locks_empty_answer_header'));
2767 $modal->setConfirmationText($this->lng->txt('tst_nav_next_locks_empty_answer_confirm'));
2768
2769 $button = $modal->buildModalButtonInstance('tst_nav_next_empty_answer_button');
2770 $button->setCaption('tst_proceed');
2771 $button->setPrimary(false);
2772 $modal->addButton($button);
2773
2774 $button = $modal->buildModalButtonInstance('tst_cancel_next_empty_answer_button');
2775 $button->setCaption('cancel');
2776 $button->setPrimary(true);
2777 $modal->addButton($button);
2778
2779 $this->tpl->setCurrentBlock('next_locks_unchanged_modal');
2780 $this->tpl->setVariable('NEXT_LOCKS_UNCHANGED_MODAL', $modal->getHTML());
2781 $this->tpl->parseCurrentBlock();
2782 }
2783
2785 {
2787 return;
2788 }
2789
2790 require_once 'Modules/Test/classes/class.ilTestPlayerConfirmationModal.php';
2791 $modal = new ilTestPlayerConfirmationModal();
2792 $modal->setModalId('tst_next_locks_changed_modal');
2793
2794 $modal->setHeaderText($this->lng->txt('tst_nav_next_locks_current_answer_header'));
2795 $modal->setConfirmationText($this->lng->txt('tst_nav_next_locks_current_answer_confirm'));
2796
2797 $modal->setConfirmationCheckboxName(self::FOLLOWUP_QST_LOCKS_PREVENT_CONFIRMATION_PARAM);
2798 $modal->setConfirmationCheckboxLabel($this->lng->txt('tst_dont_show_msg_again_in_current_session'));
2799
2800 $button = $modal->buildModalButtonInstance('tst_nav_next_changed_answer_button');
2801 $button->setCaption('tst_save_and_proceed');
2802 $button->setPrimary(true);
2803 $modal->addButton($button);
2804
2805 $button = $modal->buildModalButtonInstance('tst_cancel_next_changed_answer_button');
2806 $button->setCaption('cancel');
2807 $button->setPrimary(false);
2808 $modal->addButton($button);
2809
2810 $this->tpl->setCurrentBlock('next_locks_changed_modal');
2811 $this->tpl->setVariable('NEXT_LOCKS_CHANGED_MODAL', $modal->getHTML());
2812 $this->tpl->parseCurrentBlock();
2813 }
2814
2815 const FOLLOWUP_QST_LOCKS_PREVENT_CONFIRMATION_PARAM = 'followup_qst_locks_prevent_confirmation';
2816
2818 {
2820 }
2821
2823 {
2824 if (!isset($_SESSION[self::FOLLOWUP_QST_LOCKS_PREVENT_CONFIRMATION_PARAM])) {
2825 return false;
2826 }
2827
2829 }
2830
2831 // fau: testNav - new function populateQuestionEditControl
2837 protected function populateQuestionEditControl($questionGUI)
2838 {
2839 // configuration for ilTestPlayerQuestionEditControl.js
2840 $config = array();
2841
2842 // set the initial state of the question
2843 $state = $questionGUI->object->lookupForExistingSolutions($this->testSession->getActiveId(), $this->testSession->getPass());
2844 $config['isAnswered'] = $state['authorized'];
2845 $config['isAnswerChanged'] = $state['intermediate'] || $this->getAnswerChangedParameter();
2846
2847 // set url to which the for should be submitted when the working time is over
2848 // don't use asynch url because the form is submitted directly
2849 // but use simple '&' because url is copied by javascript into the form action
2850 $config['saveOnTimeReachedUrl'] = str_replace('&amp;', '&', $this->ctrl->getFormAction($this, ilTestPlayerCommands::AUTO_SAVE_ON_TIME_LIMIT));
2851
2852 // enable the auto saving function
2853 // the autosave url is asynch because it will be used by an ajax request
2854 if ($questionGUI->isAutosaveable() && $this->object->getAutosave()) {
2855 $config['autosaveUrl'] = $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::AUTO_SAVE, '', true);
2856 $config['autosaveInterval'] = $this->object->getAutosaveIval();
2857 } else {
2858 $config['autosaveUrl'] = '';
2859 $config['autosaveInterval'] = 0;
2860 }
2861
2863 // hey: prevPassSolutions - refactored method identifiers
2864 $questionConfig = $questionGUI->object->getTestPresentationConfig();
2865 // hey.
2866
2867 // Normal questions: changes are done in form fields an can be detected there
2868 $config['withFormChangeDetection'] = $questionConfig->isFormChangeDetectionEnabled();
2869
2870 // Flash and Java questions: changes are directly sent to ilias and have to be polled from there
2871 $config['withBackgroundChangeDetection'] = $questionConfig->isBackgroundChangeDetectionEnabled();
2872 $config['backgroundDetectorUrl'] = $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::DETECT_CHANGES, '', true);
2873
2874 // Forced feedback will change the navigation saving command
2875 $config['forcedInstantFeedback'] = $this->object->isForceInstantFeedbackEnabled();
2876 $config['nextQuestionLocks'] = $this->object->isFollowupQuestionAnswerFixationEnabled();
2877
2878 $this->tpl->addJavascript('./Modules/Test/js/ilTestPlayerQuestionEditControl.js');
2879 $this->tpl->addOnLoadCode('il.TestPlayerQuestionEditControl.init(' . json_encode($config) . ')');
2880 }
2881 // fau.
2882
2883 protected function getQuestionsDefaultPresentationMode($isQuestionWorkedThrough)
2884 {
2885 // fau: testNav - always set default presentation mode to "edit"
2887 // fau.
2888 }
2889
2894 protected function buildFixedShufflerSeed($questionId)
2895 {
2896 $fixedSeed = $questionId . $this->testSession->getActiveId() . $this->testSession->getPass();
2897
2899 $fixedSeed *= (
2901 );
2902 }
2903
2904 return $fixedSeed;
2905 }
2906
2907 protected function registerForcedFeedbackNavUrl($forcedFeedbackNavUrl)
2908 {
2909 if (!isset($_SESSION['forced_feedback_navigation_url'])) {
2910 $_SESSION['forced_feedback_navigation_url'] = array();
2911 }
2912
2913 $_SESSION['forced_feedback_navigation_url'][$this->testSession->getActiveId()] = $forcedFeedbackNavUrl;
2914 }
2915
2917 {
2918 if (!isset($_SESSION['forced_feedback_navigation_url'])) {
2919 return null;
2920 }
2921
2922 if (!isset($_SESSION['forced_feedback_navigation_url'][$this->testSession->getActiveId()])) {
2923 return null;
2924 }
2925
2926 return $_SESSION['forced_feedback_navigation_url'][$this->testSession->getActiveId()];
2927 }
2928
2930 {
2931 return !empty($this->getRegisteredForcedFeedbackNavUrl());
2932 }
2933
2935 {
2936 if (isset($_SESSION['forced_feedback_navigation_url'][$this->testSession->getActiveId()])) {
2937 unset($_SESSION['forced_feedback_navigation_url'][$this->testSession->getActiveId()]);
2938 }
2939 }
2940}
$result
$path
Definition: aliased.php:25
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
Definition: linkback.php:10
exit
Definition: backend.php:16
$filename
Definition: buildRTE.php:89
$_GET["client_id"]
$_POST["username"]
$_SESSION["AccountId"]
An exception for terminatinating execution or to throw for unit testing.
const IL_COMP_MODULE
const IL_CAL_FKT_GETDATE
const IL_CAL_TIMESTAMP
const IL_CAL_UNIX
Multiple choice question GUI representation.
Basic GUI class for assessment questions.
getSolutionOutput( $active_id, $pass=null, $graphicalOutput=false, $result_output=false, $show_question_only=true, $show_feedback=false, $show_correct_solution=false, $show_manual_scoring=false, $show_question_text=true)
setNavigationGUI($navigationGUI)
outQuestionForTest( $formaction, $active_id, $pass, $is_question_postponed=false, $user_post_solutions=false, $show_specific_inline_feedback=false)
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="", $inlineFeedbackEnabled=false)
output question page
getSpecificFeedbackOutput($userSolution)
Returns the answer specific feedback for the question.
hasInlineFeedback()
this method can be overwritten per question type
getGenericFeedbackOutput($active_id, $pass)
Returns the answer specific feedback for the question.
Abstract basic class which is to be extended by the concrete assessment question type classes.
static _instantiateQuestion($question_id)
getTestPresentationConfig()
Get the test question configuration (initialised once)
static _getCurrentGUI(ilObjTest $a_test_obj=null)
Get the GUI that is used for the currently available selector (other GUI instances may exist for conf...
Confirmation screen class.
static formatDate(ilDateTime $date, $a_skip_day=false, $a_include_wd=false, $include_seconds=false)
Format a date @access public.
@classDescription Date and time handling
static getInstance(ilTestSession $a_test_session)
static getInstance()
Factory.
static getInstance()
Get instance.
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
static getSyntaxStylePath()
get syntax style path
static getContentStylePath($a_style_id, $add_random=true)
get content style path
static lookupExamId($active_id, $pass)
static _getUsePreviousAnswers($active_id, $user_active_user_setting=false)
Returns if the previous results should be hidden for a learner.
static allObligationsAnswered($test_id, $active_id, $pass)
checks wether all questions marked as obligatory were answered within the test pass with given testId...
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static buildExamId($active_id, $pass, $test_obj_id=null)
static _getSolvedQuestions($active_id, $question_fi=null)
get solved questions
static get($a_var)
Get a value.
ILIAS Setting Class.
static getInstance()
Factory.
Class ilTaggingGUI.
special template class to simplify handling of ITX/PEAR
Class ilTestArchiver.
Output class for assessment test evaluation.
Base Exception for all Exceptions relating to Modules/Test.
static generatePDF($pdf_output, $output_mode, $filename=null, $purpose=null)
Class ilTestPassFinishTasks.
Output class for assessment test execution.
handleSkillTriggering(ilTestSession $testSession)
checkTestSessionUser(ilTestSession $testSession)
populateNextButtons($disabled, $primaryNext)
redirectAfterAutosaveCmd()
Redirect the user after an automatic save when the time limit is reached.
isShowingPostponeStatusReguired($questionId)
saveTagsCmd()
Save tags for tagging gui.
initTestQuestionConfig(assQuestion $questionOBJ)
confirmHintRequestCmd()
Go to hint request confirmation.
__construct($a_object)
ilTestOutputGUI constructor
removeIntermediateSolution()
remove an auto-saved solution of the current question
autosaveOnTimeLimitCmd()
Automatically save a user answer when the limited duration of a test run is reached (called by synchr...
setAnswerChangedParameter($changed=true)
Set the 'answer changed' url parameter for generated links.
confirmFinishCmd()
The final submission of a test was confirmed.
buildEditableStateQuestionNavigationGUI($questionId, $charSelectorAvailable)
startPlayerCmd()
Start a test for the first time.
prepareTestPage($presentationMode, $sequenceElement, $questionId)
isOptionalQuestionAnsweringConfirmationRequired($sequenceElement)
handleTearsAndAngerQuestionIsNull($questionId, $sequenceElement)
getQuestionInstance($questionId, $fromCache=true)
saveQuestionSolution($authorized=true, $force=false)
saves the user input of a question
isFirstQuestionInSequence($sequenceElement)
populateGenericFeedbackBlock(assQuestionGUI $question_gui, $solutionCorrect)
unmarkQuestionCmd()
Set a question unsolved.
showSideList($presentationMode, $currentSequenceElement)
populateUpperNextButtonBlock($disabled, $primaryNext)
initTestCmd()
Start a test for the first time after a redirect.
populateInstantResponseModal(assQuestionGUI $questionGui, $navUrl)
endingTimeReached()
handle endingTimeReached
outQuestionSummaryCmd($fullpage=true, $contextFinishTest=false, $obligationsInfo=false, $obligationsFilter=false)
Output of a summary of all test questions for test participants.
maxProcessingTimeReached()
Outputs a message when the maximum processing time is reached.
showQuestionEditable(assQuestionGUI $questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse)
getQuestionGuiInstance($questionId, $fromCache=true)
resumePlayerCmd()
Resume a test at the last position.
handleIntermediateSubmit()
Check the 'answer changed' parameter when a question form is intermediately submitted.
confirmSubmitAnswers()
confirm submit results if confirm then results are submitted and the screen will be redirected to the...
autosaveCmd()
Automatically save a user answer while working on the test (called repeatedly by asynchronous posts i...
ensureExistingTestSession(ilTestSession $testSession)
populateScoreBlock($reachedPoints, $maxPoints)
updateWorkingTime()
updates working time and stores state saveresult to see if question has to be stored or not
handleUserSettings()
Handles some form parameters on starting and resuming a test.
populateInstantResponseBlocks(assQuestionGUI $questionGui, $authorizedSolution)
getQuestionsDefaultPresentationMode($isQuestionWorkedThrough)
outCorrectSolution()
Creates an output of the solution of an answer compared to the correct solution.
detectChangesCmd()
Detect changes sent in the background to the server This is called by ajax from ilTestPlayerQuestionE...
populateLowerNextButtonBlock($disabled, $primaryNext)
finishTestCmd($requires_confirmation=true)
markQuestionCmd()
Set a question solved.
populateTestNavigationToolbar(ilTestNavigationToolbarGUI $toolbarGUI)
isTestSignRedirectRequired($activeId, $lastFinishedPass)
getAnswerChangedParameter()
Get the 'answer changed' status from the current request It may be set by ilTestPlayerNavigationContr...
determineSolutionPassIndex(assQuestionGUI $questionGui)
populateSpecificFeedbackBlock(assQuestionGUI $question_gui)
showRequestedHintListCmd()
Go to requested hint list.
isMaxProcessingTimeReached()
Checks wheather the maximum processing time is reached or not.
setAnonymousIdCmd()
Sets a session variable with the test access code for an anonymous test user.
saveNavigationPreventConfirmation()
Save the save the switch to prevent the navigation confirmation.
registerForcedFeedbackNavUrl($forcedFeedbackNavUrl)
isLastQuestionInSequence($sequenceElement)
checkOnlineTestAccess()
check access restrictions like client ip, partipating user etc.
populateQuestionNavigation($sequenceElement, $disabled, $primaryNext)
buildNextButtonInstance($disabled, $primaryNext)
isTestAccessible()
test accessible returns true if the user can perform the test
showQuestionViewable(assQuestionGUI $questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse)
getContentBlockName()
Returns the name of the current content block (depends on the kiosk mode setting)
showListOfAnswers($active_id, $pass=null, $top_data="", $bottom_data="")
Creates an output of the list of answers for a test participant during the test (only the actual pass...
Service GUI class for tests.
getCorrectSolutionOutput($question_id, $active_id, $pass, ilTestQuestionRelatedObjectivesList $objectivesList=null)
Returns an output of the solution to an answer compared to the correct solution.
getResultsSignature()
Returns HTML code for a signature field.
buildQuestionRelatedObjectivesList(ilLOTestQuestionAdapter $objectivesAdapter, ilTestQuestionSequence $testSequence)
getAdditionalUsrDataHtmlAndPopulateWindowTitle($testSession, $active_id, $overwrite_anonymity=false)
Returns the user data for a test results output.
Test session handler.
static getWebspaceDir($mode="filesystem")
get webspace directory
static getStyleSheetLocation($mode="output", $a_css_name="", $a_css_location="")
get full style sheet file name (path inclusive) of current user
static redirect($a_script)
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
static makeDirParents($a_dir)
Create a new directory and all parent directories.
static sendInfo($a_info="", $a_keep=false)
Send Info Message to Screen.
$template
const OUTPUT_JAVASCRIPT
const REDIRECT_KIOSK
$config
Definition: bootstrap.php:15
$user
Definition: migrateto20.php:57
redirection script todo: (a better solution should control the processing via a xml file)
global $ilSetting
Definition: privfeed.php:17
$url
global $DIC
Definition: saml.php:7
foreach($_POST as $key=> $value) $res
global $ilDB
$results
Definition: svg-scanner.php:47
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
$ilUser
Definition: imgupload.php:18
$text
Definition: errorreport.php:18