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