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