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 
4 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
5 require_once './Modules/Test/classes/class.ilTestPlayerCommands.php';
6 require_once './Modules/Test/classes/class.ilTestServiceGUI.php';
7 require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
8 require_once './Services/UIComponent/Button/classes/class.ilSubmitButton.php';
9 require_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;
33  public $saveResult;
34  public $sequence;
35  public $cmdCtrl;
38 
42  protected $passwordChecker;
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()
180  public 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 
210  public function isMaxProcessingTimeReached()
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
643  $this->handleIntermediateSubmit();
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
665  $this->handleIntermediateSubmit();
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 {
740  $this->ctrl->redirect($this, ilTestPlayerCommands::QUESTION_SUMMARY_OBLIGATIONS_ONLY);
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)) {
921  ilUtil::makeDirParents($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();
1075  $this->populateContentStyleBlock();
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(),
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  {
1291  $this->removeIntermediateSolution();
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 
1371  public function maxProcessingTimeReached()
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 
1717  public function outUserResultsOverviewCmd()
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 
1838  protected function getIntroductionPageButtonLabel()
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 
1929  protected function populateCharSelectorIfRequired()
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 
2242  protected function resetSequenceElementParameter()
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 {
2334  $this->removeIntermediateSolution();
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 
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 
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
2490  $this->populateNavWhenChangedModal();
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"
2698  return self::PRESENTATION_MODE_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 
2746  protected function unregisterForcedFeedbackNavUrl()
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 }
checkTestSessionUser(ilTestSession $testSession)
static makeDirParents($a_dir)
Create a new directory and all parent directories.
saveQuestionSolution($authorized=true, $force=false)
saves the user input of a question
startPlayerCmd()
Start a test for the first time.
removeIntermediateSolution()
remove an auto-saved solution of the current question
Class ilTestPassFinishTasks.
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
static allObligationsAnswered($test_id, $active_id, $pass)
checks wether all questions marked as obligatory were answered within the test pass with given testId...
getAdditionalUsrDataHtmlAndPopulateWindowTitle($testSession, $active_id, $overwrite_anonymity=false)
Returns the user data for a test results output.
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
setAnswerChangedParameter($changed=true)
Set the &#39;answer changed&#39; url parameter for generated links.
isFirstQuestionInSequence($sequenceElement)
$_SESSION["AccountId"]
$result
$template
checkOnlineTestAccess()
check access restrictions like client ip, partipating user etc.
setAnonymousIdCmd()
Sets a session variable with the test access code for an anonymous test user.
global $DIC
Definition: saml.php:7
determineSolutionPassIndex(assQuestionGUI $questionGui)
static getStyleSheetLocation($mode="output", $a_css_name="", $a_css_location="")
get full style sheet file name (path inclusive) of current user
$_GET["client_id"]
showQuestionEditable(assQuestionGUI $questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse)
Abstract basic class which is to be extended by the concrete assessment question type classes...
isLastQuestionInSequence($sequenceElement)
Class ilTaggingGUI.
confirmSubmitAnswers()
confirm submit results if confirm then results are submitted and the screen will be redirected to the...
updateWorkingTime()
updates working time and stores state saveresult to see if question has to be stored or not ...
saveTagsCmd()
Save tags for tagging gui.
static formatDate(ilDateTime $date, $a_skip_day=false, $a_include_wd=false)
Format a date public.
isTestSignRedirectRequired($activeId, $lastFinishedPass)
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
static get($a_var)
Get a value.
getResultsSignature()
Returns HTML code for a signature field.
setAnonymousId($anonymous_id)
handleUserSettings()
Handles some form parameters on starting and resuming a test.
const IL_CAL_UNIX
const OUTPUT_JAVASCRIPT
getContentBlockName()
Returns the name of the current content block (depends on the kiosk mode setting) ...
confirmHintRequestCmd()
Go to hint request confirmation.
__construct($a_object)
ilTestOutputGUI constructor
Base Exception for all Exceptions relating to Modules/Test.
maxProcessingTimeReached()
Outputs a message when the maximum processing time is reached.
populateLowerNextButtonBlock($disabled, $primaryNext)
showRequestedHintListCmd()
Go to requested hint list.
prepareTestPage($presentationMode, $sequenceElement, $questionId)
getCorrectSolutionOutput($question_id, $active_id, $pass, ilTestQuestionRelatedObjectivesList $objectivesList=null)
Returns an output of the solution to an answer compared to the correct solution.
populateInstantResponseBlocks(assQuestionGUI $questionGui, $authorizedSolution)
static sendInfo($a_info="", $a_keep=false)
Send Info Message to Screen.
$counter
buildQuestionRelatedObjectivesList(ilLOTestQuestionAdapter $objectivesAdapter, ilTestQuestionSequence $testSequence)
ensureExistingTestSession(ilTestSession $testSession)
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
Definition: linkback.php:10
populateQuestionNavigation($sequenceElement, $disabled, $primaryNext)
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...
static getInstance(ilTestSession $a_test_session)
initTestQuestionConfig(assQuestion $questionOBJ)
confirmFinishCmd()
The final submission of a test was confirmed.
markQuestionCmd()
Set a question solved.
handleIntermediateSubmit()
Check the &#39;answer changed&#39; parameter when a question form is intermediately submitted.
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
foreach($_POST as $key=> $value) $res
handleSkillTriggering(ilTestSession $testSession)
buildEditableStateQuestionNavigationGUI($questionId, $charSelectorAvailable)
showSideList($presentationMode, $currentSequenceElement)
populateGenericFeedbackBlock(assQuestionGUI $question_gui, $solutionCorrect)
outCorrectSolution()
Creates an output of the solution of an answer compared to the correct solution.
special template class to simplify handling of ITX/PEAR
resumePlayerCmd()
Resume a test at the last position.
saveNavigationPreventConfirmation()
Save the save the switch to prevent the navigation confirmation.
endingTimeReached()
handle endingTimeReached
$text
Definition: errorreport.php:18
static _getSolvedQuestions($active_id, $question_fi=null)
get solved questions
Output class for assessment test evaluation.
populateTestNavigationToolbar(ilTestNavigationToolbarGUI $toolbarGUI)
const IL_COMP_MODULE
Date and time handling
$ilUser
Definition: imgupload.php:18
redirection script todo: (a better solution should control the processing via a xml file) ...
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...
Test session handler.
static getSyntaxStylePath()
get syntax style path
static getContentStylePath($a_style_id, $add_random=true)
get content style path
getAnswerChangedParameter()
Get the &#39;answer changed&#39; status from the current request It may be set by ilTestPlayerNavigationContr...
Basic GUI class for assessment questions.
detectChangesCmd()
Detect changes sent in the background to the server This is called by ajax from ilTestPlayerQuestionE...
const REDIRECT_KIOSK
Create styles array
The data for the language used.
static _instantiateQuestion($question_id)
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
isShowingPostponeStatusReguired($questionId)
outQuestionSummaryCmd($fullpage=true, $contextFinishTest=false, $obligationsNotAnswered=false, $obligationsFilter=false)
Output of a summary of all test questions for test participants.
unmarkQuestionCmd()
Set a question unsolved.
const IL_CAL_FKT_GETDATE
Multiple choice question GUI representation.
registerForcedFeedbackNavUrl($forcedFeedbackNavUrl)
Class ilTestArchiver.
static getInstance()
Get instance.
populateUpperNextButtonBlock($disabled, $primaryNext)
isOptionalQuestionAnsweringConfirmationRequired($sequenceElement)
showQuestionViewable(assQuestionGUI $questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse)
isTestAccessible()
test accessible returns true if the user can perform the test
populateNextButtons($disabled, $primaryNext)
performFinishTasks(ilTestProcessLocker $processLocker)
Create new PHPExcel object
obj_idprivate
global $ilSetting
Definition: privfeed.php:17
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)
populateInstantResponseModal(assQuestionGUI $questionGui, $navUrl)
$results
Definition: svg-scanner.php:47
getTestPresentationConfig()
Get the test question configuration (initialised once)
outQuestionForTest( $formaction, $active_id, $pass, $is_question_postponed=false, $user_post_solutions=false, $show_specific_inline_feedback=false)
global $ilDB
getQuestionGuiInstance($questionId, $fromCache=true)
static buildExamId($active_id, $pass, $test_obj_id=null)
getQuestionInstance($questionId, $fromCache=true)
getQuestionsDefaultPresentationMode($isQuestionWorkedThrough)
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="")
output question page
$url
Service GUI class for tests.
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
static generatePDF($pdf_output, $output_mode, $filename=null, $purpose=null)
const IL_CAL_TIMESTAMP
initTestCmd()
Start a test for the first time after a redirect.
getGenericFeedbackOutput($active_id, $pass)
Returns the answer specific feedback for the question.
finishTestCmd($requires_confirmation=true)
handleTearsAndAngerQuestionIsNull($questionId, $sequenceElement)
isMaxProcessingTimeReached()
Checks wheather the maximum processing time is reached or not.
buildNextButtonInstance($disabled, $primaryNext)
static _getUsePreviousAnswers($active_id, $user_active_user_setting=false)
Returns if the previous results should be hidden for a learner.
static redirect($a_script)
static getWebspaceDir($mode="filesystem")
get webspace directory
$_POST["username"]
populateScoreBlock($reachedPoints, $maxPoints)
Output class for assessment test execution.
Confirmation screen class.
static lookupExamId($active_id, $pass)
setNavigationGUI($navigationGUI)