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