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