ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
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  var $ref_id;
32  var $sequence;
33  var $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::ilTestServiceGUI($a_object);
70  $this->ref_id = $_GET["ref_id"];
71 
72  global $rbacsystem, $ilUser, $lng;
73  require_once 'Modules/Test/classes/class.ilTestPasswordChecker.php';
74  $this->passwordChecker = new ilTestPasswordChecker($rbacsystem, $ilUser, $this->object, $lng);
75 
76  $this->processLocker = null;
77  $this->testSession = null;
78  $this->assSettings = null;
79  }
80 
81  protected function checkReadAccess()
82  {
83  global $rbacsystem;
84 
85  if(!$rbacsystem->checkAccess("read", $this->object->getRefId()))
86  {
87  // only with read access it is possible to run the test
88  $this->ilias->raiseError($this->lng->txt("cannot_execute_test"), $this->ilias->error_obj->MESSAGE);
89  }
90  }
91 
92  protected function checkTestExecutable()
93  {
94  $executable = $this->object->isExecutable($this->testSession, $this->testSession->getUserId());
95 
96  if( !$executable['executable'] )
97  {
98  ilUtil::sendInfo($executable['errormessage'], true);
99  $this->ctrl->redirectByClass("ilobjtestgui", "infoScreen");
100  }
101  }
102 
104  {
105  if( $testSession->getActiveId() )
106  {
107  return;
108  }
109 
110  global $ilUser;
111 
112  $testSession->setUserId($ilUser->getId());
113 
114  if( $testSession->isAnonymousUser() )
115  {
116  if( !$testSession->doesAccessCodeInSessionExists() )
117  {
118  return;
119  }
120 
121  $testSession->setAnonymousId($testSession->getAccessCodeFromSession());
122  }
123 
124  $testSession->saveToDb();
125  }
126 
127  protected function initProcessLocker($activeId)
128  {
129  global $ilDB;
130 
131  require_once 'Modules/Test/classes/class.ilTestProcessLockerFactory.php';
132  $processLockerFactory = new ilTestProcessLockerFactory($this->assSettings, $ilDB);
133 
134  $processLockerFactory->setActiveId($activeId);
135 
136  $this->processLocker = $processLockerFactory->getLocker();
137  }
138 
145  function saveTagsCmd()
146  {
147  include_once("./Services/Tagging/classes/class.ilTaggingGUI.php");
148  $tagging_gui = new ilTaggingGUI();
149  $tagging_gui->setObject($this->object->getId(), $this->object->getType());
150  $tagging_gui->saveInput();
151  $this->ctrl->redirectByClass("ilobjtestgui", "infoScreen");
152  }
153 
157  function updateWorkingTime()
158  {
159  if ($_SESSION["active_time_id"])
160  {
161  $this->object->updateWorkingTime($_SESSION["active_time_id"]);
162  }
163 
164  $_SESSION["active_time_id"] = $this->object->startWorkingTime(
165  $this->testSession->getActiveId(), $this->testSession->getPass()
166  );
167  }
168 
172  abstract public function saveQuestionSolution($authorized = true, $force = false);
173 
174  abstract protected function canSaveResult();
175 
176  public function suspendTestCmd()
177  {
178  $this->ctrl->redirectByClass("ilobjtestgui", "infoScreen");
179  }
180 
188  public function isMaxProcessingTimeReached()
189  {
190  global $ilUser;
191  $active_id = $this->testSession->getActiveId();
192  $starting_time = $this->object->getStartingTimeOfUser($active_id);
193  if ($starting_time === FALSE)
194  {
195  return FALSE;
196  }
197  else
198  {
199  return $this->object->isMaxProcessingTimeReached($starting_time, $active_id);
200  }
201  }
202 
203  protected function determineInlineScoreDisplay()
204  {
205  $show_question_inline_score = FALSE;
206  if ($this->object->getAnswerFeedbackPoints())
207  {
208  $show_question_inline_score = TRUE;
209  return $show_question_inline_score;
210  }
211  return $show_question_inline_score;
212  }
213 
215  {
216  $this->tpl->setCurrentBlock('test_nav_toolbar');
217  $this->tpl->setVariable('TEST_NAV_TOOLBAR', $toolbarGUI->getHTML());
218  $this->tpl->parseCurrentBlock();
219  }
220 
221  protected function populateQuestionNavigation($sequenceElement, $disabled)
222  {
223  if( !$this->isFirstQuestionInSequence($sequenceElement) )
224  {
225  $this->populatePreviousButtons($disabled);
226  }
227 
228  if( !$this->isLastQuestionInSequence($sequenceElement) )
229  {
230  $this->populateNextButtons($disabled);
231  }
232  }
233 
234  protected function populatePreviousButtons($disabled)
235  {
236  $this->populateUpperPreviousButtonBlock($disabled);
237  $this->populateLowerPreviousButtonBlock($disabled);
238  }
239 
240  protected function populateNextButtons($disabled)
241  {
242  $this->populateUpperNextButtonBlock($disabled);
243  $this->populateLowerNextButtonBlock($disabled);
244  }
245 
246  protected function populateLowerNextButtonBlock($disabled)
247  {
248  $button = $this->buildNextButtonInstance($disabled);
249  $button->setId('bottomnextbutton');
250 
251  $this->tpl->setCurrentBlock( "next_bottom" );
252  $this->tpl->setVariable( "BTN_NEXT", $button->render());
253  $this->tpl->parseCurrentBlock();
254  }
255 
256  protected function populateUpperNextButtonBlock($disabled)
257  {
258  $button = $this->buildNextButtonInstance($disabled);
259  $button->setId('nextbutton');
260 
261  $this->tpl->setCurrentBlock( "next" );
262  $this->tpl->setVariable( "BTN_NEXT", $button->render());
263  $this->tpl->parseCurrentBlock();
264  }
265 
266  protected function populateLowerPreviousButtonBlock($disabled)
267  {
268  $button = $this->buildPreviousButtonInstance($disabled);
269  $button->setId('bottomprevbutton');
270 
271  $this->tpl->setCurrentBlock( "prev_bottom" );
272  $this->tpl->setVariable("BTN_PREV", $button->render());
273  $this->tpl->parseCurrentBlock();
274  }
275 
276  protected function populateUpperPreviousButtonBlock($disabled)
277  {
278  $button = $this->buildPreviousButtonInstance($disabled);
279  $button->setId('prevbutton');
280 
281  $this->tpl->setCurrentBlock( "prev" );
282  $this->tpl->setVariable("BTN_PREV", $button->render());
283  $this->tpl->parseCurrentBlock();
284  }
285 
290  private function buildNextButtonInstance($disabled)
291  {
293  $button->setPrimary(false);
294  $button->setNextCommand(ilTestPlayerCommands::NEXT_QUESTION);
295  $button->setUrl($this->ctrl->getLinkTarget($this, ilTestPlayerCommands::NEXT_QUESTION));
296  $button->setCaption('next_question');
297  $button->addCSSClass('ilTstNavElem');
298  //$button->setDisabled($disabled);
299  return $button;
300  }
301 
306  private function buildPreviousButtonInstance($disabled)
307  {
309  $button->setNextCommand(ilTestPlayerCommands::PREVIOUS_QUESTION);
310  $button->setUrl($this->ctrl->getLinkTarget($this, ilTestPlayerCommands::PREVIOUS_QUESTION));
311  $button->setCaption('previous_question');
312  $button->addCSSClass('ilTstNavElem');
313  //$button->setDisabled($disabled);
314  return $button;
315  }
316 
317  protected function populateSpecificFeedbackBlock($question_gui)
318  {
319  $this->tpl->setCurrentBlock( "specific_feedback" );
320  $this->tpl->setVariable( "SPECIFIC_FEEDBACK",
321  $question_gui->getSpecificFeedbackOutput(
322  $this->testSession->getActiveId(),
323  $this->testSequence->getPass()
324  )
325  );
326  $this->tpl->parseCurrentBlock();
327  }
328 
329  protected function populateGenericFeedbackBlock(assQuestionGUI $question_gui, $solutionCorrect)
330  {
331  $feedback = $question_gui->getGenericFeedbackOutput(
332  $this->testSession->getActiveId(),$this->testSequence->getPass()
333  );
334 
335  if( strlen($feedback) )
336  {
337  $cssClass = ( $solutionCorrect ?
339  );
340 
341  $this->tpl->setCurrentBlock( "answer_feedback" );
342  $this->tpl->setVariable( "ANSWER_FEEDBACK", $feedback);
343  $this->tpl->setVariable( "ILC_FB_CSS_CLASS", $cssClass);
344  $this->tpl->parseCurrentBlock();
345  }
346  }
347 
348  protected function populateScoreBlock($reachedPoints, $maxPoints)
349  {
350  $scoreInformation = sprintf(
351  $this->lng->txt( "you_received_a_of_b_points" ), $reachedPoints, $maxPoints
352  );
353 
354  $this->tpl->setCurrentBlock( "received_points_information" );
355  $this->tpl->setVariable("RECEIVED_POINTS_INFORMATION", $scoreInformation);
356  $this->tpl->parseCurrentBlock();
357  }
358 
359  protected function populateSolutionBlock($solutionoutput)
360  {
361  $this->tpl->setCurrentBlock( "solution_output" );
362  $this->tpl->setVariable( "CORRECT_SOLUTION", $this->lng->txt( "tst_best_solution_is" ) );
363  $this->tpl->setVariable( "QUESTION_FEEDBACK", $solutionoutput );
364  $this->tpl->parseCurrentBlock();
365  }
366 
367  protected function populateSyntaxStyleBlock()
368  {
369  $this->tpl->setCurrentBlock( "SyntaxStyle" );
370  $this->tpl->setVariable( "LOCATION_SYNTAX_STYLESHEET",
372  );
373  $this->tpl->parseCurrentBlock();
374  }
375 
376  protected function populateContentStyleBlock()
377  {
378  include_once("./Services/Style/classes/class.ilObjStyleSheet.php");
379  $this->tpl->setCurrentBlock( "ContentStyle" );
380  $this->tpl->setVariable( "LOCATION_CONTENT_STYLESHEET",
382  );
383  $this->tpl->parseCurrentBlock();
384  }
385 
391  public function setAnonymousIdCmd()
392  {
393  if( $this->testSession->isAnonymousUser() )
394  {
395  $this->testSession->setAccessCodeToSession($_POST['anonymous_id']);
396  }
397 
398  $this->ctrl->redirectByClass("ilobjtestgui", "infoScreen");
399  }
400 
407  protected function startPlayerCmd()
408  {
409  $testStartLock = $this->getLockParameter();
410  $isFirstTestStartRequest = false;
411 
412  $this->processLocker->requestTestStartLockCheckLock();
413 
414  if( $this->testSession->lookupTestStartLock() != $testStartLock )
415  {
416  $this->testSession->persistTestStartLock($testStartLock);
417  $isFirstTestStartRequest = true;
418  }
419 
420  $this->processLocker->releaseTestStartLockCheckLock();
421 
422  if( $isFirstTestStartRequest )
423  {
424  $this->handleUserSettings();
425  $this->ctrl->redirect($this, ilTestPlayerCommands::INIT_TEST);
426  }
427 
428  $this->ctrl->setParameterByClass('ilObjTestGUI', 'lock', $testStartLock);
429  $this->ctrl->redirectByClass("ilobjtestgui", "redirectToInfoScreen");
430  }
431 
432  public function getLockParameter()
433  {
434  if( isset($_POST['lock']) && strlen($_POST['lock']) )
435  {
436  return $_POST['lock'];
437  }
438  elseif( isset($_GET['lock']) && strlen($_GET['lock']) )
439  {
440  return $_GET['lock'];
441  }
442 
443  return null;
444  }
445 
449  abstract protected function resumePlayerCmd();
450 
454  protected function initTestCmd()
455  {
456  if ($this->object->checkMaximumAllowedUsers() == FALSE)
457  {
459  }
460 
461  if( $this->testSession->isAnonymousUser() && !$this->testSession->getActiveId() )
462  {
463  $accessCode = $this->testSession->createNewAccessCode();
464 
465  $this->testSession->setAccessCodeToSession($accessCode);
466  $this->testSession->setAnonymousId($accessCode);
467  $this->testSession->saveToDb();
468 
469  $this->ctrl->redirect($this, ilTestPlayerCommands::DISPLAY_ACCESS_CODE);
470  }
471 
472  $this->testSession->unsetAccessCodeInSession();
473  $this->ctrl->redirect($this, ilTestPlayerCommands::START_TEST);
474  }
475 
477  {
478  $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_anonymous_code_presentation.html", "Modules/Test");
479  $this->tpl->setCurrentBlock("adm_content");
480  $this->tpl->setVariable("TEXT_ANONYMOUS_CODE_CREATED", $this->lng->txt("tst_access_code_created"));
481  $this->tpl->setVariable("TEXT_ANONYMOUS_CODE", $this->testSession->getAccessCodeFromSession());
482  $this->tpl->setVariable("FORMACTION", $this->ctrl->getFormAction($this));
483  $this->tpl->setVariable("CMD_CONFIRM", ilTestPlayerCommands::ACCESS_CODE_CONFIRMED);
484  $this->tpl->setVariable("TXT_CONFIRM", $this->lng->txt("continue_work"));
485  $this->tpl->parseCurrentBlock();
486  }
487 
489  {
490  $this->ctrl->redirect($this, ilTestPlayerCommands::START_TEST);
491  }
492 
496  public function handleUserSettings()
497  {
498  global $ilUser;
499 
500  if ($_POST["chb_javascript"])
501  {
502  $ilUser->writePref("tst_javascript", 1);
503  }
504  else
505  {
506  $ilUser->writePref("tst_javascript", 0);
507  }
508 
509  // hide previous results
510  if ($this->object->getNrOfTries() != 1)
511  {
512  if ($this->object->getUsePreviousAnswers() == 1)
513  {
514  if ($_POST["chb_use_previous_answers"])
515  {
516  $ilUser->writePref("tst_use_previous_answers", 1);
517  }
518  else
519  {
520  $ilUser->writePref("tst_use_previous_answers", 0);
521  }
522  }
523  }
524  }
525 
527  {
528  $active_id = $this->testSession->getActiveId();
529  $actualpass = $this->object->_getPass($active_id);
530 
531  $this->performTestPassFinishedTasks($actualpass);
532 
533  $this->testSession->setLastFinishedPass($this->testSession->getPass());
534  $this->testSession->increaseTestPass();
535 
536  $url = $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::AFTER_TEST_PASS_FINISHED);
537 
538  $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_redirect_autosave.html", "Modules/Test");
539  $this->tpl->setVariable("TEXT_REDIRECT", $this->lng->txt("redirectAfterSave"));
540  $this->tpl->setCurrentBlock("HeadContent");
541  $this->tpl->setVariable("CONTENT_BLOCK", "<meta http-equiv=\"refresh\" content=\"5; url=" . $url . "\">");
542  $this->tpl->parseCurrentBlock();
543  }
544 
545  abstract protected function getCurrentQuestionId();
546 
547  function autosaveCmd()
548  {
549  $canSaveResult = $this->canSaveResult();
550  $authorizedSolution = !$canSaveResult;
551 
552  $result = "";
553  if (is_array($_POST) && count($_POST) > 0)
554  {
555  if( $this->isParticipantsAnswerFixed($this->getCurrentQuestionId()) )
556  {
557  $result = '-IGNORE-';
558  }
559  else
560  {
561  $res = $this->saveQuestionSolution($authorizedSolution, true);
562  if ($res)
563  {
564  $result = $this->lng->txt("autosave_success");
565  }
566  else
567  {
568  $result = $this->lng->txt("autosave_failed");
569  }
570  }
571  }
572  if (!$canSaveResult)
573  {
574  // this was the last action in the test, saving is no longer allowed
575  $result = $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::REDIRECT_ON_TIME_LIMIT, "", true);
576  }
577  echo $result;
578  exit;
579  }
580 
581  protected function submitIntermediateSolutionCmd()
582  {
583  $this->saveQuestionSolution(false, true);
584  $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
585  }
586 
590  public function toggleSideListCmd()
591  {
592  global $ilUser;
593 
594  $show_side_list = $ilUser->getPref('side_list_of_questions');
595  $ilUser->writePref('side_list_of_questions', !$show_side_list);
596  $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
597  }
598 
600  {
601  $this->saveQuestionSolution(false);
602  $this->markQuestionCmd();
603  }
604 
608  protected function markQuestionCmd()
609  {
610  $questionId = $this->testSequence->getQuestionForSequence(
612  );
613 
614  $this->object->setQuestionSetSolved(1, $questionId, $this->testSession->getUserId());
615 
616  $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
617  }
618 
620  {
621  $this->saveQuestionSolution(false);
622  $this->unmarkQuestionCmd();
623  }
624 
628  protected function unmarkQuestionCmd()
629  {
630  $questionId = $this->testSequence->getQuestionForSequence(
632  );
633 
634  $this->object->setQuestionSetSolved(0, $questionId, $this->testSession->getUserId());
635 
636  $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
637  }
638 
642  protected function confirmFinishCmd()
643  {
644  $this->finishTestCmd(false);
645  }
646 
650  protected function confirmFinishTestCmd()
651  {
655  global $ilUser;
656 
657  require_once 'Services/Utilities/classes/class.ilConfirmationGUI.php';
658  $confirmation = new ilConfirmationGUI();
659  $confirmation->setFormAction($this->ctrl->getFormAction($this, 'confirmFinish'));
660  $confirmation->setHeaderText($this->lng->txt("tst_finish_confirmation_question"));
661  $confirmation->setConfirm($this->lng->txt("tst_finish_confirm_button"), 'confirmFinish');
662  $confirmation->setCancel($this->lng->txt("tst_finish_confirm_cancel_button"), ilTestPlayerCommands::BACK_FROM_FINISHING);
663 
664  $this->populateHelperGuiContent($confirmation);
665  }
666 
667  function finishTestCmd($requires_confirmation = true)
668  {
669  global $ilAuth;
670 
671  unset($_SESSION["tst_next"]);
672 
673  $active_id = $this->testSession->getActiveId();
674  $actualpass = $this->object->_getPass($active_id);
675 
676  $allObligationsAnswered = ilObjTest::allObligationsAnswered($this->testSession->getTestId(), $active_id, $actualpass);
677 
678  /*
679  * The following "endgames" are possible prior to actually finishing the test:
680  * - Obligations (Ability to finish the test.)
681  * If not all obligatory questions are answered, the user is presented a list
682  * showing the deficits.
683  * - Examview (Will to finish the test.)
684  * With the examview, the participant can review all answers given in ILIAS or a PDF prior to
685  * commencing to the finished test.
686  * - Last pass allowed (Reassuring the will to finish the test.)
687  * If passes are limited, on the last pass, an additional confirmation is to be displayed.
688  */
689 
690 
691  if( $this->object->areObligationsEnabled() && !$allObligationsAnswered )
692  {
693  if( $this->object->getListOfQuestions() )
694  {
695  $this->ctrl->redirect($this, ilTestPlayerCommands::QUESTION_SUMMARY_INC_OBLIGATIONS);
696  }
697  else
698  {
699  $this->ctrl->redirect($this, ilTestPlayerCommands::QUESTION_SUMMARY_OBLIGATIONS_ONLY);
700  }
701 
702  return;
703  }
704 
705  // Examview enabled & !reviewed & requires_confirmation? test_submission_overview (review gui)
706  if ($this->object->getEnableExamview() && !isset($_GET['reviewed']) && $requires_confirmation)
707  {
708  $this->ctrl->redirectByClass('ilTestSubmissionReviewGUI', "show");
709  return;
710  }
711 
712  // Last try in limited tries & !confirmed
713  if (($requires_confirmation) && ($actualpass == $this->object->getNrOfTries() - 1))
714  {
715  // show confirmation page
716  return $this->confirmFinishTestCmd();
717  }
718 
719  // Last try in limited tries & confirmed?
720  if(($actualpass == $this->object->getNrOfTries() - 1) && (!$requires_confirmation))
721  {
722  $ilAuth->setIdle(ilSession::getIdleValue(), false);
723  $ilAuth->setExpire(0);
724  switch($this->object->getMailNotification())
725  {
726  case 1:
727  $this->object->sendSimpleNotification($active_id);
728  break;
729  case 2:
730  $this->object->sendAdvancedNotification($active_id);
731  break;
732  }
733  }
734 
735  // Non-last try finish
736  if(!$_SESSION['tst_pass_finish'])
737  {
738  if(!$_SESSION['tst_pass_finish']) $_SESSION['tst_pass_finish'] = 1;
739  if($this->object->getMailNotificationType() == 1)
740  {
741  switch($this->object->getMailNotification())
742  {
743  case 1:
744  $this->object->sendSimpleNotification($active_id);
745  break;
746  case 2:
747  $this->object->sendAdvancedNotification($active_id);
748  break;
749  }
750  }
751  }
752 
753  // no redirect request loops after test pass finished tasks has been performed
754 
755  $this->performTestPassFinishedTasks($actualpass);
756 
757  $this->testSession->setLastFinishedPass($this->testSession->getPass());
758  $this->testSession->increaseTestPass();
759 
760  $this->ctrl->redirect($this, ilTestPlayerCommands::AFTER_TEST_PASS_FINISHED);
761  }
762 
763  protected function performTestPassFinishedTasks($finishedPass)
764  {
765  if( !$this->testSession->isSubmitted() )
766  {
767  $this->testSession->setSubmitted(1);
768  $this->testSession->setSubmittedTimestamp(date('Y-m-d H:i:s'));
769  $this->testSession->saveToDb();
770  }
771  }
772 
773  protected function afterTestPassFinishedCmd()
774  {
775  $activeId = $this->testSession->getActiveId();
776  $lastFinishedPass = $this->testSession->getLastFinishedPass();
777 
778  // handle test signature
779  if ( $this->isTestSignRedirectRequired($activeId, $lastFinishedPass) )
780  {
781  $this->ctrl->redirectByClass('ilTestSignatureGUI', 'invokeSignaturePlugin');
782  }
783 
784  // show final statement
785  if(!$_GET['skipfinalstatement'])
786  {
787  if ($this->object->getShowFinalStatement())
788  {
789  $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_FINAL_STATMENT);
790  }
791  }
792 
793  // redirect after test
794  $redirection_mode = $this->object->getRedirectionMode();
795  $redirection_url = $this->object->getRedirectionUrl();
796  if( $redirection_url && $redirection_mode && !$this->object->canViewResults() )
797  {
798  if( $redirection_mode == REDIRECT_KIOSK )
799  {
800  if( $this->object->getKioskMode() )
801  {
802  ilUtil::redirect($redirection_url);
803  }
804  }
805  else
806  {
807  ilUtil::redirect($redirection_url);
808  }
809  }
810 
811  // default redirect (pass overview when enabled, otherwise infoscreen)
812  $this->redirectBackCmd();
813  }
814 
815  protected function isTestSignRedirectRequired($activeId, $lastFinishedPass)
816  {
817  if( !$this->object->getSignSubmission() )
818  {
819  return false;
820  }
821 
822  if( !is_null(ilSession::get("signed_{$activeId}_{$lastFinishedPass}")) )
823  {
824  return false;
825  }
826 
827  global $ilPluginAdmin;
828 
829  $activePlugins = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, 'Test', 'tsig');
830 
831  if( !count($activePlugins) )
832  {
833  return false;
834  }
835 
836  return true;
837  }
838 
844  protected function archiveParticipantSubmission( $active, $pass )
845  {
846  global $ilObjDataCache;
847 
848  require_once 'Modules/Test/classes/class.ilTestResultHeaderLabelBuilder.php';
849  $testResultHeaderLabelBuilder = new ilTestResultHeaderLabelBuilder($this->lng, $ilObjDataCache);
850 
851  $objectivesList = null;
852 
853  if( $this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired() )
854  {
855  $testSequence = $this->testSequenceFactory->getSequenceByActiveIdAndPass($this->testSession->getActiveId(), $this->testSession->getPass());
856  $testSequence->loadFromDb();
857  $testSequence->loadQuestions();
858 
859  require_once 'Modules/Course/classes/Objectives/class.ilLOTestQuestionAdapter.php';
860  $objectivesAdapter = ilLOTestQuestionAdapter::getInstance($this->testSession);
861 
862  $objectivesList = $this->buildQuestionRelatedObjectivesList($objectivesAdapter, $testSequence);
863  $objectivesList->loadObjectivesTitles();
864 
865  $testResultHeaderLabelBuilder->setObjectiveOrientedContainerId($this->testSession->getObjectiveOrientedContainerId());
866  $testResultHeaderLabelBuilder->setUserId($this->testSession->getUserId());
867  $testResultHeaderLabelBuilder->setTestObjId($this->object->getId());
868  $testResultHeaderLabelBuilder->setTestRefId($this->object->getRefId());
869  $testResultHeaderLabelBuilder->initObjectiveOrientedMode();
870  }
871 
872  $results = $this->object->getTestResult(
873  $active, $pass, false, !$this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()
874  );
875 
876  require_once 'class.ilTestEvaluationGUI.php';
877  $testevaluationgui = new ilTestEvaluationGUI($this->object);
878  $results_output = $testevaluationgui->getPassListOfAnswers(
879  $results, $active, $pass, false, false, false, false, false, $objectivesList, $testResultHeaderLabelBuilder
880  );
881 
882  require_once './Modules/Test/classes/class.ilTestArchiver.php';
883  global $ilSetting;
884  $inst_id = $ilSetting->get('inst_id', null);
885  $archiver = new ilTestArchiver($this->object->getId());
886 
887  $path = ilUtil::getWebspaceDir() . '/assessment/'. $this->object->getId() . '/exam_pdf';
888  if (!is_dir($path))
889  {
890  ilUtil::makeDirParents($path);
891  }
892  $filename = realpath($path) . '/exam_N' . $inst_id . '-' . $this->object->getId()
893  . '-' . $active . '-' . $pass . '.pdf';
894 
895  require_once 'class.ilTestPDFGenerator.php';
897  //$template->setVariable("PDF_FILE_LOCATION", $filename);
898  // Participant submission
899  $archiver->handInParticipantSubmission( $active, $pass, $filename, $results_output );
900  //$archiver->handInParticipantMisc( $active, $pass, 'signature_gedoens.sig', $filename );
901  //$archiver->handInParticipantQuestionMaterial( $active, $pass, 123, 'file_upload.pdf', $filename );
902 
903  global $ilias;
904  $questions = $this->object->getQuestions();
905  foreach ($questions as $question_id)
906  {
907  $question_object = $this->object->getQuestionDataset( $question_id );
908  if ($question_object->type_tag == 'assFileUpload')
909  {
910  // Pfad: /data/default/assessment/tst_2/14/21/files/file_14_4_1370417414.png
911  // /data/ - klar
912  // /assessment/ - Konvention
913  // /tst_2/ = /tst_<test_id> (ilObjTest)
914  // /14/ = /<active_fi>/
915  // /21/ = /<question_id>/ (question_object)
916  // /files/ - Konvention
917  // file_14_4_1370417414.png = file_<active_fi>_<pass>_<some timestamp>.<ext>
918 
919  $candidate_path =
920  $ilias->ini_ilias->readVariable( 'server', 'absolute_path' ) . ilTestArchiver::DIR_SEP
921  . $ilias->ini_ilias->readVariable( 'clients', 'path' ) . ilTestArchiver::DIR_SEP
922  . $ilias->client_id . ilTestArchiver::DIR_SEP
923  . 'assessment' . ilTestArchiver::DIR_SEP
924  . 'tst_' . $this->object->test_id . ilTestArchiver::DIR_SEP
925  . $active . ilTestArchiver::DIR_SEP
926  . $question_id . ilTestArchiver::DIR_SEP
927  . 'files' . ilTestArchiver::DIR_SEP;
928  $handle = opendir( $candidate_path );
929  while ($handle !== false && ($file = readdir( $handle )) !== false)
930  {
931  if ($file != null)
932  {
933  $filename_start = 'file_' . $active . '_' . $pass . '_';
934 
935  if (strpos( $file, $filename_start ) === 0)
936  {
937  $archiver->handInParticipantQuestionMaterial( $active, $pass, $question_id, $file, $file );
938  }
939  }
940  }
941  }
942  }
943  $passdata = $this->object->getTestResult(
944  $active, $pass, false, !$this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()
945  );
946  $overview = $testevaluationgui->getPassListOfAnswers(
947  $passdata, $active, $pass, true, false, false, true, false, $objectivesList, $testResultHeaderLabelBuilder
948  );
949  $filename = realpath(ilUtil::getWebspaceDir()) . '/assessment/scores-'.$this->object->getId() . '-' . $active . '-' . $pass . '.pdf';
951  $archiver->handInTestResult($active, $pass, $filename);
952  unlink($filename);
953 
954  return;
955  }
956 
957  public function redirectBackCmd()
958  {
959  require_once 'Modules/Test/classes/class.ilTestPassesSelector.php';
960  $testPassesSelector = new ilTestPassesSelector($GLOBALS['ilDB'], $this->object);
961  $testPassesSelector->setActiveId($this->testSession->getActiveId());
962  $testPassesSelector->setLastFinishedPass($this->testSession->getLastFinishedPass());
963 
964  if( count($testPassesSelector->getReportablePasses()) )
965  {
966  $this->ctrl->redirectByClass("ilTestEvaluationGUI", "outUserResultsOverview");
967  }
968 
969  $this->backToInfoScreenCmd();
970  }
971 
972  protected function backToInfoScreenCmd()
973  {
974  $this->ctrl->redirectByClass('ilObjTestGUI', 'redirectToInfoScreen');
975  }
976 
977  /*
978  * Presents the final statement of a test
979  */
980  public function showFinalStatementCmd()
981  {
982  $template = new ilTemplate("tpl.il_as_tst_final_statement.html", TRUE, TRUE, "Modules/Test");
983  $this->ctrl->setParameter($this, "skipfinalstatement", 1);
984  $template->setVariable("FORMACTION", $this->ctrl->getFormAction($this, ilTestPlayerCommands::AFTER_TEST_PASS_FINISHED));
985  $template->setVariable("FINALSTATEMENT", $this->object->prepareTextareaOutput($this->object->getFinalStatement(), true));
986  $template->setVariable("BUTTON_CONTINUE", $this->lng->txt("btn_next"));
987  $this->tpl->setVariable($this->getContentBlockName(), $template->get());
988  }
989 
990  public function getKioskHead()
991  {
992  global $ilUser;
993 
994  $template = new ilTemplate('tpl.il_as_tst_kiosk_head.html', true, true, 'Modules/Test');
995  if ($this->object->getShowKioskModeTitle())
996  {
997  $template->setCurrentBlock("kiosk_show_title");
998  $template->setVariable("TEST_TITLE", $this->object->getTitle());
999  $template->parseCurrentBlock();
1000  }
1001  if ($this->object->getShowKioskModeParticipant())
1002  {
1003  $template->setCurrentBlock("kiosk_show_participant");
1004  $template->setVariable("PARTICIPANT_NAME_TXT", $this->lng->txt("login_as"));
1005  $template->setVariable("PARTICIPANT_NAME", $ilUser->getFullname());
1006  $template->setVariable("PARTICIPANT_LOGIN", $ilUser->getLogin());
1007  $template->setVariable("PARTICIPANT_MATRICULATION", $ilUser->getMatriculation());
1008  $template->setVariable("PARTICIPANT_EMAIL", $ilUser->getEmail());
1009  $template->parseCurrentBlock();
1010  }
1011  if ($this->object->isShowExamIdInTestPassEnabled())
1012  {
1013  $exam_id = ilObjTest::buildExamId(
1014  $this->testSession->getActiveId() , $this->testSession->getPass(), $this->object->getId()
1015  );
1016 
1017  $template->setCurrentBlock("kiosk_show_exam_id");
1018  $template->setVariable("EXAM_ID_TXT", $this->lng->txt("exam_id"));
1019  $template->setVariable( "EXAM_ID", $exam_id);
1020  $template->parseCurrentBlock();
1021  }
1022  return $template->get();
1023  }
1024 
1028  protected function prepareTestPage($presentationMode, $sequenceElement, $questionId)
1029  {
1030  global $ilUser, $ilNavigationHistory;
1031 
1032  $ilNavigationHistory->addItem($this->testSession->getRefId(),
1033  $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::RESUME_PLAYER), 'tst'
1034  );
1035 
1036  $this->initTestPageTemplate();
1037  $this->populateContentStyleBlock();
1038  $this->populateSyntaxStyleBlock();
1039 
1040  if ($this->isMaxProcessingTimeReached())
1041  {
1042  $this->maxProcessingTimeReached();
1043  return;
1044  }
1045 
1046  if ($this->object->endingTimeReached())
1047  {
1048  $this->endingTimeReached();
1049  return;
1050  }
1051 
1052  if( $this->isOptionalQuestionAnsweringConfirmationRequired($sequenceElement) )
1053  {
1054  $this->ctrl->setParameter($this, "sequence", $sequenceElement);
1056  return;
1057  }
1058 
1059  if ($this->object->getKioskMode())
1060  {
1061  $this->populateKioskHead();
1062  }
1063 
1064  $this->tpl->setVariable("TEST_ID", $this->object->getTestId());
1065  $this->tpl->setVariable("LOGIN", $ilUser->getLogin());
1066  $this->tpl->setVariable("SEQ_ID", $sequenceElement);
1067  $this->tpl->setVariable("QUEST_ID", $questionId);
1068 
1069  if ($this->object->getEnableProcessingTime())
1070  {
1071  $this->outProcessingTime($this->testSession->getActiveId());
1072  }
1073 
1074  $this->tpl->setVariable("PAGETITLE", "- " . $this->object->getTitle());
1075 
1076  if ($this->object->isShowExamIdInTestPassEnabled() && !$this->object->getKioskMode())
1077  {
1078  $this->tpl->setCurrentBlock('exam_id_footer');
1079  $this->tpl->setVariable('EXAM_ID_VAL', ilObjTest::lookupExamId(
1080  $this->testSession->getActiveId(), $this->testSession->getPass(), $this->object->getId()
1081  ));
1082  $this->tpl->setVariable('EXAM_ID_TXT', $this->lng->txt('exam_id'));
1083  $this->tpl->parseCurrentBlock();
1084  }
1085 
1086  if ($this->object->getListOfQuestions())
1087  {
1088  $this->showSideList($presentationMode, $sequenceElement);
1089  }
1090  }
1091 
1092  abstract protected function isOptionalQuestionAnsweringConfirmationRequired($sequenceElement);
1093 
1094  abstract protected function isShowingPostponeStatusReguired($questionId);
1095 
1096  protected function showQuestionViewable(assQuestionGUI $questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse)
1097  {
1098  $questionNavigationGUI = $this->buildReadOnlyStateQuestionNavigationGUI($questionGui->object->getId());
1099  $questionNavigationGUI->setQuestionWorkedThrough($isQuestionWorkedThrough);
1100  $questionGui->setNavigationGUI($questionNavigationGUI);
1101 
1102  $answerFeedbackEnabled = (
1103  $instantResponse && $this->object->getSpecificAnswerFeedback()
1104  );
1105 
1106  $solutionoutput = $questionGui->getSolutionOutput(
1107  $this->testSession->getActiveId(), #active_id
1108  $this->testSession->getPass(), #pass
1109  false, #graphical_output
1110  false, #result_output
1111  true, #show_question_only
1112  $answerFeedbackEnabled, #show_feedback
1113  false, #show_correct_solution
1114  false, #show_manual_scoring
1115  true #show_question_text
1116  );
1117 
1118  $pageoutput = $questionGui->outQuestionPage(
1119  "",
1120  $this->isShowingPostponeStatusReguired($questionGui->object->getId()),
1121  $this->testSession->getActiveId(),
1122  $solutionoutput
1123  );
1124 
1125  $this->tpl->setCurrentBlock('readonly_css_class');
1126  $this->tpl->touchBlock('readonly_css_class');
1127  $this->tpl->parseCurrentBlock();
1128 
1129  $this->tpl->setVariable('QUESTION_OUTPUT', $pageoutput);
1130 
1131  $this->tpl->setVariable("FORMACTION", $formAction);
1132  $this->tpl->setVariable("ENCTYPE", 'enctype="'.$questionGui->getFormEncodingType().'"');
1133  $this->tpl->setVariable("FORM_TIMESTAMP", time());
1134  }
1135 
1136  protected function showQuestionEditable(assQuestionGUI $questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse)
1137  {
1138  $questionNavigationGUI = $this->buildEditableStateQuestionNavigationGUI(
1139  $questionGui->object->getId(), $this->populateCharSelectorIfRequired()
1140  );
1141  if( $isQuestionWorkedThrough )
1142  {
1143  $questionNavigationGUI->setDiscardSolutionButtonEnabled(true);
1144  }
1145  else
1146  {
1147  $questionNavigationGUI->setSkipQuestionLinkTarget(
1148  $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::SKIP_QUESTION)
1149  );
1150  }
1151  $questionGui->setNavigationGUI($questionNavigationGUI);
1152 
1153  $isPostponed = $this->isShowingPostponeStatusReguired($questionGui->object->getId());
1154 
1155  $answerFeedbackEnabled = (
1156  $instantResponse && $this->object->getSpecificAnswerFeedback()
1157  );
1158 
1159  if( isset($_GET['save_error']) && $_GET['save_error'] == 1 && isset($_SESSION['previouspost']) )
1160  {
1161  $userPostSolution = $_SESSION['previouspost'];
1162  unset($_SESSION['previouspost']);
1163  }
1164  else
1165  {
1166  $userPostSolution = false;
1167  }
1168 
1169  // hey: prevPassSolutions - determine solution pass index and configure gui accordingly
1170  $solutionPassIndex = $this->determineSolutionPassIndex($questionGui);
1171  $questionGui->setPreviousSolutionPrefilled($solutionPassIndex < $this->testSession->getPass());
1172  // hey.
1173 
1174  // Answer specific feedback is rendered into the display of the test question with in the concrete question types outQuestionForTest-method.
1175  // Notation of the params prior to getting rid of this crap in favor of a class
1176  $questionGui->outQuestionForTest(
1177  $formAction, #form_action
1178  $this->testSession->getActiveId(), #active_id
1179  // hey: prevPassSolutions - prepared pass index having no, current or previous solution
1180  $solutionPassIndex, #pass
1181  // hey.
1182  $isPostponed, #is_postponed
1183  $userPostSolution, #user_post_solution
1184  $answerFeedbackEnabled #answer_feedback == inline_specific_feedback
1185  );
1186  // The display of specific inline feedback and specific feedback in an own block is to honor questions, which
1187  // have the possibility to embed the specific feedback into their output while maintaining compatibility to
1188  // questions, which do not have such facilities. E.g. there can be no "specific inline feedback" for essay
1189  // questions, while the multiple-choice questions do well.
1190 
1191 
1192  $this->populateModals();
1193 
1194  $this->populateIntermediateSolutionSaver($questionGui);
1195  }
1196 
1197  // hey: prevPassSolutions - determine solution pass index
1198  protected function determineSolutionPassIndex(assQuestionGUI $questionGui)
1199  {
1200  require_once './Modules/Test/classes/class.ilObjTest.php';
1201 
1202  if( ilObjTest::_getUsePreviousAnswers($this->testSession->getActiveId(), true) )
1203  {
1204  $currentSolutionAvailable = $questionGui->object->authorizedOrIntermediateSolutionExists(
1205  $this->testSession->getActiveId(), $this->testSession->getPass()
1206  );
1207 
1208  if( !$currentSolutionAvailable )
1209  {
1210  $previousPass = $questionGui->object->getSolutionMaxPass(
1211  $this->testSession->getActiveId()
1212  );
1213 
1214  $previousSolutionAvailable = $questionGui->object->authorizedSolutionExists(
1215  $this->testSession->getActiveId(), $previousPass
1216  );
1217 
1218  if( $previousSolutionAvailable )
1219  {
1220  return $previousPass;
1221  }
1222  }
1223  }
1224 
1225  return $this->testSession->getPass();
1226  }
1227  // hey.
1228 
1229  abstract protected function showQuestionCmd();
1230 
1231  abstract protected function editSolutionCmd();
1232 
1233  abstract protected function submitSolutionCmd();
1234 
1235  abstract protected function discardSolutionCmd();
1236 
1237  abstract protected function skipQuestionCmd();
1238 
1239  abstract protected function startTestCmd();
1248  {
1249  global $ilUser;
1250 
1251  // check if user is invited to participate
1252  $user = $this->object->getInvitedUsers($ilUser->getId());
1253  if (!is_array ($user) || count($user)!=1)
1254  {
1255  ilUtil::sendInfo($this->lng->txt("user_not_invited"), true);
1256  $this->ctrl->redirectByClass("ilobjtestgui", "backToRepository");
1257  }
1258 
1259  $user = array_pop($user);
1260  // check if client ip is set and if current remote addr is equal to stored client-ip
1261  if (strcmp($user["clientip"],"")!=0 && strcmp($user["clientip"],$_SERVER["REMOTE_ADDR"])!=0)
1262  {
1263  ilUtil::sendInfo($this->lng->txt("user_wrong_clientip"), true);
1264  $this->ctrl->redirectByClass("ilobjtestgui", "backToRepository");
1265  }
1266  }
1267 
1268 
1272  function isTestAccessible()
1273  {
1274  return !$this->isNrOfTriesReached()
1275  and !$this->isMaxProcessingTimeReached()
1276  and $this->object->startingTimeReached()
1277  and !$this->object->endingTimeReached();
1278  }
1279 
1284  {
1285  return $this->object->hasNrOfTriesRestriction() && $this->object->isNrOfTriesReached($this->testSession->getPass());
1286  }
1287 
1293  function endingTimeReached()
1294  {
1295  ilUtil::sendInfo(sprintf($this->lng->txt("detail_ending_time_reached"), ilFormat::ftimestamp2datetimeDB($this->object->getEndingTime())));
1296  $this->testSession->increasePass();
1297  $this->testSession->setLastSequence(0);
1298  $this->testSession->saveToDb();
1299 
1300  $this->redirectBackCmd();
1301  }
1302 
1311  {
1312  $this->suspendTestCmd();
1313  }
1314 
1321  {
1322  $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_submit_answers_confirm.html", "Modules/Test");
1323  $this->tpl->setCurrentBlock("adm_content");
1324  if ($this->object->isTestFinished($this->testSession->getActiveId()))
1325  {
1326  $this->tpl->setCurrentBlock("not_submit_allowed");
1327  $this->tpl->setVariable("TEXT_ALREADY_SUBMITTED", $this->lng->txt("tst_already_submitted"));
1328  $this->tpl->setVariable("BTN_OK", $this->lng->txt("tst_show_answer_sheet"));
1329  } else
1330  {
1331  $this->tpl->setCurrentBlock("submit_allowed");
1332  $this->tpl->setVariable("TEXT_CONFIRM_SUBMIT_RESULTS", $this->lng->txt("tst_confirm_submit_answers"));
1333  $this->tpl->setVariable("BTN_OK", $this->lng->txt("tst_submit_results"));
1334  }
1335  $this->tpl->setVariable("BTN_BACK", $this->lng->txt("back"));
1336  $this->tpl->setVariable("FORMACTION", $this->ctrl->getFormAction($this, "finalSubmission"));
1337  $this->tpl->parseCurrentBlock();
1338  }
1339 
1340  function outProcessingTime($active_id)
1341  {
1342  global $ilUser;
1343 
1344  $starting_time = $this->object->getStartingTimeOfUser($active_id);
1345  $processing_time = $this->object->getProcessingTimeInSeconds($active_id);
1346  $processing_time_minutes = floor($processing_time / 60);
1347  $processing_time_seconds = $processing_time - $processing_time_minutes * 60;
1348  $str_processing_time = "";
1349  if ($processing_time_minutes > 0)
1350  {
1351  $str_processing_time = $processing_time_minutes . " " . ($processing_time_minutes == 1 ? $this->lng->txt("minute") : $this->lng->txt("minutes"));
1352  }
1353  if ($processing_time_seconds > 0)
1354  {
1355  if (strlen($str_processing_time) > 0) $str_processing_time .= " " . $this->lng->txt("and") . " ";
1356  $str_processing_time .= $processing_time_seconds . " " . ($processing_time_seconds == 1 ? $this->lng->txt("second") : $this->lng->txt("seconds"));
1357  }
1358  $time_left = $starting_time + $processing_time - mktime();
1359  $time_left_minutes = floor($time_left / 60);
1360  $time_left_seconds = $time_left - $time_left_minutes * 60;
1361  $str_time_left = "";
1362  if ($time_left_minutes > 0)
1363  {
1364  $str_time_left = $time_left_minutes . " " . ($time_left_minutes == 1 ? $this->lng->txt("minute") : $this->lng->txt("minutes"));
1365  }
1366  if ($time_left < 300)
1367  {
1368  if ($time_left_seconds > 0)
1369  {
1370  if (strlen($str_time_left) > 0) $str_time_left .= " " . $this->lng->txt("and") . " ";
1371  $str_time_left .= $time_left_seconds . " " . ($time_left_seconds == 1 ? $this->lng->txt("second") : $this->lng->txt("seconds"));
1372  }
1373  }
1374  $date = getdate($starting_time);
1375  $formattedStartingTime = ilDatePresentation::formatDate(new ilDateTime($date,IL_CAL_FKT_GETDATE));
1376  /*
1377  $formattedStartingTime = ilFormat::formatDate(
1378  $date["year"]."-".
1379  sprintf("%02d", $date["mon"])."-".
1380  sprintf("%02d", $date["mday"])." ".
1381  sprintf("%02d", $date["hours"]).":".
1382  sprintf("%02d", $date["minutes"]).":".
1383  sprintf("%02d", $date["seconds"])
1384  );
1385  */
1386  $datenow = getdate();
1387  $this->tpl->setCurrentBlock("enableprocessingtime");
1388  $this->tpl->setVariable("USER_WORKING_TIME",
1389  sprintf(
1390  $this->lng->txt("tst_time_already_spent"),
1391  $formattedStartingTime,
1392  $str_processing_time
1393  )
1394  );
1395  $this->tpl->setVariable("USER_REMAINING_TIME", sprintf($this->lng->txt("tst_time_already_spent_left"), $str_time_left));
1396  $this->tpl->parseCurrentBlock();
1397  $template = new ilTemplate("tpl.workingtime.js.html", TRUE, TRUE, 'Modules/Test');
1398  $template->setVariable("STRING_MINUTE", $this->lng->txt("minute"));
1399  $template->setVariable("STRING_MINUTES", $this->lng->txt("minutes"));
1400  $template->setVariable("STRING_SECOND", $this->lng->txt("second"));
1401  $template->setVariable("STRING_SECONDS", $this->lng->txt("seconds"));
1402  $template->setVariable("STRING_TIMELEFT", $this->lng->txt("tst_time_already_spent_left"));
1403  $template->setVariable("AND", strtolower($this->lng->txt("and")));
1404  $template->setVariable("YEAR", $date["year"]);
1405  $template->setVariable("MONTH", $date["mon"]-1);
1406  $template->setVariable("DAY", $date["mday"]);
1407  $template->setVariable("HOUR", $date["hours"]);
1408  $template->setVariable("MINUTE", $date["minutes"]);
1409  $template->setVariable("SECOND", $date["seconds"]);
1410  if ($this->object->isEndingTimeEnabled() && preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->object->getEndingTime(), $matches))
1411  {
1412  $template->setVariable("ENDYEAR", $matches[1]);
1413  $template->setVariable("ENDMONTH", $matches[2]-1);
1414  $template->setVariable("ENDDAY", $matches[3]);
1415  $template->setVariable("ENDHOUR", $matches[4]);
1416  $template->setVariable("ENDMINUTE", $matches[5]);
1417  $template->setVariable("ENDSECOND", $matches[6]);
1418  }
1419  $template->setVariable("YEARNOW", $datenow["year"]);
1420  $template->setVariable("MONTHNOW", $datenow["mon"]-1);
1421  $template->setVariable("DAYNOW", $datenow["mday"]);
1422  $template->setVariable("HOURNOW", $datenow["hours"]);
1423  $template->setVariable("MINUTENOW", $datenow["minutes"]);
1424  $template->setVariable("SECONDNOW", $datenow["seconds"]);
1425  $template->setVariable("PTIME_M", $processing_time_minutes);
1426  $template->setVariable("PTIME_S", $processing_time_seconds);
1427 
1428  $this->tpl->setCurrentBlock("HeadContent");
1429  $this->tpl->setVariable("CONTENT_BLOCK", $template->get());
1430  $this->tpl->parseCurrentBlock();
1431  }
1432 
1433  protected function showSideList($presentationMode, $currentSequenceElement)
1434  {
1435  global $ilUser;
1436 
1437  $sideListActive = $ilUser->getPref('side_list_of_questions');
1438 
1439  if($sideListActive)
1440  {
1441  $this->tpl->addCss(
1442  ilUtil::getStyleSheetLocation("output", "ta_split.css", "Modules/Test"), "screen"
1443  );
1444 
1445  $questionSummaryData = $this->service->getQuestionSummaryData($this->testSequence, false);
1446 
1447  require_once 'Modules/Test/classes/class.ilTestQuestionSideListGUI.php';
1448  $questionSideListGUI = new ilTestQuestionSideListGUI($this->ctrl, $this->lng);
1449  $questionSideListGUI->setTargetGUI($this);
1450  $questionSideListGUI->setQuestionSummaryData($questionSummaryData);
1451  $questionSideListGUI->setCurrentSequenceElement($currentSequenceElement);
1452  $questionSideListGUI->setCurrentPresentationMode($presentationMode);
1453  $questionSideListGUI->setDisabled($presentationMode == self::PRESENTATION_MODE_EDIT);
1454  $this->tpl->setVariable('LIST_OF_QUESTIONS', $questionSideListGUI->getHTML());
1455  }
1456  }
1457 
1458  abstract protected function isQuestionSummaryFinishTestButtonRequired();
1459 
1463  public function outQuestionSummaryCmd($fullpage = true, $contextFinishTest = false, $obligationsNotAnswered = false, $obligationsFilter = false)
1464  {
1465  if( $fullpage )
1466  {
1467  $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_question_summary.html", "Modules/Test");
1468  }
1469 
1470  if( $obligationsNotAnswered )
1471  {
1472  ilUtil::sendFailure($this->lng->txt('not_all_obligations_answered'));
1473  }
1474 
1475  if( $this->object->getKioskMode() && $fullpage )
1476  {
1477  $head = $this->getKioskHead();
1478  if( strlen($head) )
1479  {
1480  $this->tpl->setCurrentBlock("kiosk_options");
1481  $this->tpl->setVariable("KIOSK_HEAD", $head);
1482  $this->tpl->parseCurrentBlock();
1483  }
1484  }
1485 
1486 
1487  $active_id = $this->testSession->getActiveId();
1488  $questionSummaryData = $this->service->getQuestionSummaryData($this->testSequence, $obligationsFilter);
1489 
1490  $this->ctrl->setParameter($this, "sequence", $_GET["sequence"]);
1491 
1492  if( $fullpage )
1493  {
1494  include_once "./Modules/Test/classes/tables/class.ilListOfQuestionsTableGUI.php";
1495  $table_gui = new ilListOfQuestionsTableGUI($this, 'backFromSummary');
1496 
1497  $table_gui->setShowPointsEnabled( !$this->object->getTitleOutput() );
1498  $table_gui->setShowMarkerEnabled( $this->object->getShowMarker() );
1499  $table_gui->setObligationsNotAnswered( $obligationsNotAnswered );
1500  $table_gui->setShowObligationsEnabled( $this->object->areObligationsEnabled() );
1501  $table_gui->setObligationsFilterEnabled( $obligationsFilter );
1502  $table_gui->setFinishTestButtonEnabled($this->isQuestionSummaryFinishTestButtonRequired());
1503 
1504  $table_gui->init();
1505 
1506  $table_gui->setData($questionSummaryData);
1507 
1508  $this->tpl->setVariable('TABLE_LIST_OF_QUESTIONS', $table_gui->getHTML());
1509 
1510  if( $this->object->getEnableProcessingTime() )
1511  {
1512  $this->outProcessingTime($active_id);
1513  }
1514  }
1515  }
1516 
1518  {
1519  return $this->outQuestionSummaryCmd(true, true, true, false);
1520  }
1521 
1523  {
1524  return $this->outQuestionSummaryCmd(true, true, true, true);
1525  }
1526 
1528  {
1529  $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_max_allowed_users_reached.html", "Modules/Test");
1530  $this->tpl->setCurrentBlock("adm_content");
1531  $this->tpl->setVariable("MAX_ALLOWED_USERS_MESSAGE", sprintf($this->lng->txt("tst_max_allowed_users_message"), $this->object->getAllowedUsersTimeGap()));
1532  $this->tpl->setVariable("MAX_ALLOWED_USERS_HEADING", sprintf($this->lng->txt("tst_max_allowed_users_heading"), $this->object->getAllowedUsersTimeGap()));
1533  $this->tpl->setVariable("CMD_BACK_TO_INFOSCREEN", ilTestPlayerCommands::BACK_TO_INFO_SCREEN);
1534  $this->tpl->setVariable("TXT_BACK_TO_INFOSCREEN", $this->lng->txt("tst_results_back_introduction"));
1535  $this->tpl->setVariable("FORMACTION", $this->ctrl->getFormAction($this));
1536  $this->tpl->parseCurrentBlock();
1537  }
1538 
1540  {
1541  $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
1542  }
1543 
1550  {
1551  $this->tpl->addBlockFile("ADM_CONTENT", "adm_content", "tpl.il_as_tst_correct_solution.html", "Modules/Test");
1552 
1553  include_once("./Services/Style/classes/class.ilObjStyleSheet.php");
1554  $this->tpl->setCurrentBlock("ContentStyle");
1555  $this->tpl->setVariable("LOCATION_CONTENT_STYLESHEET", ilObjStyleSheet::getContentStylePath(0));
1556  $this->tpl->parseCurrentBlock();
1557 
1558  $this->tpl->setCurrentBlock("SyntaxStyle");
1559  $this->tpl->setVariable("LOCATION_SYNTAX_STYLESHEET", ilObjStyleSheet::getSyntaxStylePath());
1560  $this->tpl->parseCurrentBlock();
1561 
1562  $this->tpl->addCss(ilUtil::getStyleSheetLocation("output", "test_print.css", "Modules/Test"), "print");
1563  if ($this->object->getShowSolutionAnswersOnly())
1564  {
1565  $this->tpl->addCss(ilUtil::getStyleSheetLocation("output", "test_print_hide_content.css", "Modules/Test"), "print");
1566  }
1567 
1568  $this->tpl->setCurrentBlock("adm_content");
1569  $solution = $this->getCorrectSolutionOutput($_GET["evaluation"], $_GET["active_id"], $_GET["pass"]);
1570  $this->tpl->setVariable("OUTPUT_SOLUTION", $solution);
1571  $this->tpl->setVariable("TEXT_BACK", $this->lng->txt("back"));
1572  $this->ctrl->saveParameter($this, "pass");
1573  $this->ctrl->saveParameter($this, "active_id");
1574  $this->tpl->setVariable("URL_BACK", $this->ctrl->getLinkTarget($this, "outUserResultsOverview"));
1575  $this->tpl->parseCurrentBlock();
1576  }
1577 
1587  function showListOfAnswers($active_id, $pass = NULL, $top_data = "", $bottom_data = "")
1588  {
1589  global $ilUser;
1590 
1591  $this->tpl->addBlockFile($this->getContentBlockName(), "adm_content", "tpl.il_as_tst_finish_list_of_answers.html", "Modules/Test");
1592 
1593  $result_array =& $this->object->getTestResult(
1594  $active_id, $pass, false, !$this->getObjectiveOrientedContainer()->isObjectiveOrientedPresentationRequired()
1595  );
1596 
1597  $counter = 1;
1598  // output of questions with solutions
1599  foreach ($result_array as $question_data)
1600  {
1601  $question = $question_data["qid"];
1602  if (is_numeric($question))
1603  {
1604  $this->tpl->setCurrentBlock("printview_question");
1605  $question_gui = $this->object->createQuestionGUI("", $question);
1606  $template = new ilTemplate("tpl.il_as_qpl_question_printview.html", TRUE, TRUE, "Modules/TestQuestionPool");
1607  $template->setVariable("COUNTER_QUESTION", $counter.". ");
1608  $template->setVariable("QUESTION_TITLE", $question_gui->object->getTitle());
1609 
1610  $show_question_only = ($this->object->getShowSolutionAnswersOnly()) ? TRUE : FALSE;
1611  $result_output = $question_gui->getSolutionOutput($active_id, $pass, FALSE, FALSE, $show_question_only, $this->object->getShowSolutionFeedback());
1612  $template->setVariable("SOLUTION_OUTPUT", $result_output);
1613  $this->tpl->setVariable("QUESTION_OUTPUT", $template->get());
1614  $this->tpl->parseCurrentBlock();
1615  $counter ++;
1616  }
1617  }
1618 
1619  $this->tpl->addCss(ilUtil::getStyleSheetLocation("output", "test_print.css", "Modules/Test"), "print");
1620  if ($this->object->getShowSolutionAnswersOnly())
1621  {
1622  $this->tpl->addCss(ilUtil::getStyleSheetLocation("output", "test_print_hide_content.css", "Modules/Test"), "print");
1623  }
1624  if (strlen($top_data))
1625  {
1626  $this->tpl->setCurrentBlock("top_data");
1627  $this->tpl->setVariable("TOP_DATA", $top_data);
1628  $this->tpl->parseCurrentBlock();
1629  }
1630 
1631  if (strlen($bottom_data))
1632  {
1633  $this->tpl->setCurrentBlock("bottom_data");
1634  $this->tpl->setVariable("FORMACTION", $this->ctrl->getFormAction($this));
1635  $this->tpl->setVariable("BOTTOM_DATA", $bottom_data);
1636  $this->tpl->parseCurrentBlock();
1637  }
1638 
1639  $this->tpl->setCurrentBlock("adm_content");
1640  $this->tpl->setVariable("TXT_ANSWER_SHEET", $this->lng->txt("tst_list_of_answers"));
1641  $user_data = $this->getAdditionalUsrDataHtmlAndPopulateWindowTitle($this->testSession, $active_id, TRUE);
1642  $signature = $this->getResultsSignature();
1643  $this->tpl->setVariable("USER_DETAILS", $user_data);
1644  $this->tpl->setVariable("SIGNATURE", $signature);
1645  $this->tpl->setVariable("TITLE", $this->object->getTitle());
1646  $this->tpl->setVariable("TXT_TEST_PROLOG", $this->lng->txt("tst_your_answers"));
1647  $invited_user =& $this->object->getInvitedUsers($ilUser->getId());
1648  $pagetitle = $this->object->getTitle() . " - " . $this->lng->txt("clientip") .
1649  ": " . $invited_user[$ilUser->getId()]["clientip"] . " - " .
1650  $this->lng->txt("matriculation") . ": " .
1651  $invited_user[$ilUser->getId()]["matriculation"];
1652  $this->tpl->setVariable("PAGETITLE", $pagetitle);
1653  $this->tpl->parseCurrentBlock();
1654  }
1655 
1661  public function getContentBlockName()
1662  {
1663  if ($this->object->getKioskMode())
1664  {
1665  $this->tpl->setBodyClass("kiosk");
1666  $this->tpl->setAddFooter(FALSE);
1667  return "CONTENT";
1668  }
1669  else
1670  {
1671  return "ADM_CONTENT";
1672  }
1673  }
1674 
1676  {
1677  $this->ctrl->redirectByClass(
1678  array('ilRepositoryGUI', 'ilObjTestGUI', 'ilTestEvaluationGUI'), "outUserResultsOverview"
1679  );
1680  }
1681 
1685  protected function showRequestedHintListCmd()
1686  {
1687  $this->saveQuestionSolution(false);
1688 
1689  $this->ctrl->setParameter($this, 'pmode', self::PRESENTATION_MODE_EDIT);
1690 
1691  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintRequestGUI.php';
1692  $this->ctrl->redirectByClass('ilAssQuestionHintRequestGUI', ilAssQuestionHintRequestGUI::CMD_SHOW_LIST);
1693  }
1694 
1698  protected function confirmHintRequestCmd()
1699  {
1700  $this->saveQuestionSolution(false);
1701 
1702  $this->ctrl->setParameter($this, 'pmode', self::PRESENTATION_MODE_EDIT);
1703 
1704  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintRequestGUI.php';
1705  $this->ctrl->redirectByClass('ilAssQuestionHintRequestGUI', ilAssQuestionHintRequestGUI::CMD_CONFIRM_REQUEST);
1706  }
1707 
1708  abstract protected function isFirstQuestionInSequence($sequenceElement);
1709 
1710  abstract protected function isLastQuestionInSequence($sequenceElement);
1711 
1712 
1713  abstract protected function handleQuestionActionCmd();
1714 
1715  abstract protected function showInstantResponseCmd();
1716 
1717  abstract protected function nextQuestionCmd();
1718 
1719  abstract protected function previousQuestionCmd();
1720 
1721  protected function prepareSummaryPage()
1722  {
1723  $this->tpl->addBlockFile(
1724  $this->getContentBlockName(), 'adm_content', 'tpl.il_as_tst_question_summary.html', 'Modules/Test'
1725  );
1726 
1727  if ($this->object->getKioskMode())
1728  {
1729  $this->populateKioskHead();
1730  }
1731  }
1732 
1733  protected function initTestPageTemplate()
1734  {
1735  $this->tpl->addBlockFile(
1736  $this->getContentBlockName(), 'adm_content', 'tpl.il_as_tst_output.html', 'Modules/Test'
1737  );
1738  }
1739 
1740  protected function populateKioskHead()
1741  {
1742  ilUtil::sendInfo(); // ???
1743 
1744  $head = $this->getKioskHead();
1745 
1746  if (strlen($head))
1747  {
1748  $this->tpl->setCurrentBlock("kiosk_options");
1749  $this->tpl->setVariable("KIOSK_HEAD", $head);
1750  $this->tpl->parseCurrentBlock();
1751  }
1752  }
1753 
1755  {
1756  if( $this->ctrl->getNextClass() == 'iltestpasswordprotectiongui' )
1757  {
1758  return;
1759  }
1760 
1761  if( !$this->passwordChecker->isPasswordProtectionPageRedirectRequired() )
1762  {
1763  return;
1764  }
1765 
1766  $this->ctrl->setParameter($this, 'lock', $this->getLockParameter());
1767 
1768  $nextCommand = $this->ctrl->getCmdClass().'::'.$this->ctrl->getCmd();
1769  $this->ctrl->setParameterByClass('ilTestPasswordProtectionGUI', 'nextCommand', $nextCommand);
1770  $this->ctrl->redirectByClass('ilTestPasswordProtectionGUI', 'showPasswordForm');
1771  }
1772 
1773  protected function isParticipantsAnswerFixed($questionId)
1774  {
1775  if( !$this->object->isInstantFeedbackAnswerFixationEnabled() )
1776  {
1777  return false;
1778  }
1779 
1780  if( !$this->testSequence->isQuestionChecked($questionId) )
1781  {
1782  return false;
1783  }
1784 
1785  return true;
1786  }
1787 
1791  protected function getIntroductionPageButtonLabel()
1792  {
1793  return $this->lng->txt("save_introduction");
1794  }
1795 
1796  protected function initAssessmentSettings()
1797  {
1798  $this->assSettings = new ilSetting('assessment');
1799  }
1800 
1805  {
1806  $questionList = $this->buildTestPassQuestionList();
1807  $questionList->load();
1808 
1809  $testResults = $this->object->getTestResult($testSession->getActiveId(), $testSession->getPass(), true);
1810 
1811  require_once 'Modules/Test/classes/class.ilTestSkillEvaluation.php';
1812  $skillEvaluation = new ilTestSkillEvaluation($this->db, $this->object->getTestId(), $this->object->getRefId());
1813 
1814  $skillEvaluation->setUserId($testSession->getUserId());
1815  $skillEvaluation->setActiveId($testSession->getActiveId());
1816  $skillEvaluation->setPass($testSession->getPass());
1817 
1818  $skillEvaluation->setNumRequiredBookingsForSkillTriggering($this->assSettings->get(
1819  'ass_skl_trig_num_answ_barrier', ilObjAssessmentFolder::DEFAULT_SKL_TRIG_NUM_ANSWERS_BARRIER
1820  ));
1821 
1822 
1823  $skillEvaluation->init($questionList);
1824  $skillEvaluation->evaluate($testResults);
1825 
1826  $skillEvaluation->handleSkillTriggering();
1827  }
1828 
1829  abstract protected function buildTestPassQuestionList();
1830 
1832  {
1833  require_once 'Modules/Test/classes/confirmations/class.ilTestAnswerOptionalQuestionsConfirmationGUI.php';
1834  $confirmation = new ilTestAnswerOptionalQuestionsConfirmationGUI($this->lng);
1835 
1836  $confirmation->setFormAction($this->ctrl->getFormAction($this));
1837  $confirmation->setCancelCmd('cancelAnswerOptionalQuestions');
1838  $confirmation->setConfirmCmd('confirmAnswerOptionalQuestions');
1839 
1840  $confirmation->build($this->object->isFixedTest());
1841 
1842  $this->populateHelperGuiContent($confirmation);
1843  }
1844 
1846  {
1847  $this->testSequence->setAnsweringOptionalQuestionsConfirmed(true);
1848  $this->testSequence->saveToDb();
1849 
1850  $this->ctrl->setParameter($this, 'activecommand', 'gotoquestion');
1851  $this->ctrl->redirect($this, 'redirectQuestion');
1852  }
1853 
1855  {
1856  if( $this->object->getListOfQuestions() )
1857  {
1858  $this->ctrl->setParameter($this, 'activecommand', 'summary');
1859  }
1860  else
1861  {
1862  $this->ctrl->setParameter($this, 'activecommand', 'previous');
1863  }
1864 
1865  $this->ctrl->redirect($this, 'redirectQuestion');
1866  }
1867 
1871  protected function populateHelperGuiContent($helperGui)
1872  {
1873  if($this->object->getKioskMode())
1874  {
1875  $this->tpl->addBlockfile($this->getContentBlockName(), 'content', "tpl.il_as_tst_kiosk_mode_content.html", "Modules/Test");
1876  $this->tpl->setContent($this->ctrl->getHTML($helperGui));
1877  }
1878  else
1879  {
1880  $this->tpl->setVariable($this->getContentBlockName(), $this->ctrl->getHTML($helperGui));
1881  }
1882  }
1883 
1887  protected function populateCharSelectorIfRequired()
1888  {
1889  global $ilSetting;
1890 
1891  if ($ilSetting->get('char_selector_availability') > 0)
1892  {
1893  require_once 'Services/UIComponent/CharSelector/classes/class.ilCharSelectorGUI.php';
1894  $char_selector = ilCharSelectorGUI::_getCurrentGUI($this->object);
1895  if ($char_selector->getConfig()->getAvailability() == ilCharSelectorConfig::ENABLED)
1896  {
1897  $char_selector->addToPage();
1898  $this->tpl->setCurrentBlock('char_selector');
1899  $this->tpl->setVariable("CHAR_SELECTOR_TEMPLATE", $char_selector->getSelectorHtml());
1900  $this->tpl->parseCurrentBlock();
1901 
1902  return true;
1903  }
1904  }
1905 
1906  return false;
1907  }
1908 
1909  protected function getTestNavigationToolbarGUI()
1910  {
1911  global $ilUser;
1912 
1913  require_once 'Modules/Test/classes/class.ilTestNavigationToolbarGUI.php';
1914  $navigationToolbarGUI = new ilTestNavigationToolbarGUI($this->ctrl, $this->lng, $this);
1915 
1916  $navigationToolbarGUI->setSuspendTestButtonEnabled($this->object->getShowCancel());
1917  $navigationToolbarGUI->setQuestionTreeButtonEnabled($this->object->getListOfQuestions());
1918  $navigationToolbarGUI->setQuestionTreeVisible($ilUser->getPref('side_list_of_questions'));
1919  $navigationToolbarGUI->setQuestionListButtonEnabled($this->object->getListOfQuestions());
1920  $navigationToolbarGUI->setFinishTestCommand($this->getFinishTestCommand());
1921 
1922  return $navigationToolbarGUI;
1923  }
1924 
1925  protected function buildReadOnlyStateQuestionNavigationGUI($questionId)
1926  {
1927  require_once 'Modules/Test/classes/class.ilTestQuestionNavigationGUI.php';
1928  $navigationGUI = new ilTestQuestionNavigationGUI($this->lng);
1929 
1930  if( !$this->isParticipantsAnswerFixed($questionId) )
1931  {
1932  $navigationGUI->setEditSolutionCommand(ilTestPlayerCommands::EDIT_SOLUTION);
1933  }
1934 
1935  if($this->object->getShowMarker())
1936  {
1937  include_once "./Modules/Test/classes/class.ilObjTest.php";
1938  $solved_array = ilObjTest::_getSolvedQuestions($this->testSession->getActiveId(), $questionId);
1939  $solved = 0;
1940 
1941  if(count($solved_array) > 0)
1942  {
1943  $solved = array_pop($solved_array);
1944  $solved = $solved["solved"];
1945  }
1946 
1947  if($solved == 1)
1948  {
1949  $navigationGUI->setQuestionMarkCommand(ilTestPlayerCommands::UNMARK_QUESTION);
1950  $navigationGUI->setQuestionMarked(true);
1951  }
1952  else
1953  {
1954  $navigationGUI->setQuestionMarkCommand(ilTestPlayerCommands::MARK_QUESTION);
1955  $navigationGUI->setQuestionMarked(false);
1956  }
1957  }
1958 
1959  return $navigationGUI;
1960  }
1961 
1962  protected function buildEditableStateQuestionNavigationGUI($questionId, $charSelectorAvailable)
1963  {
1964  require_once 'Modules/Test/classes/class.ilTestQuestionNavigationGUI.php';
1965  $navigationGUI = new ilTestQuestionNavigationGUI($this->lng);
1966 
1967  if( $this->object->isForceInstantFeedbackEnabled() )
1968  {
1969  $navigationGUI->setSubmitSolutionCommand(ilTestPlayerCommands::SUBMIT_SOLUTION);
1970  }
1971  else
1972  {
1973  $navigationGUI->setSubmitSolutionCommand(ilTestPlayerCommands::SUBMIT_SOLUTION_AND_NEXT);
1974  }
1975 
1976 
1977  // feedback
1978  switch( 1 )
1979  {
1980  case $this->object->getSpecificAnswerFeedback():
1981  case $this->object->getGenericAnswerFeedback():
1982  case $this->object->getAnswerFeedbackPoints():
1983  case $this->object->getInstantFeedbackSolution():
1984 
1985  $navigationGUI->setAnswerFreezingEnabled($this->object->isInstantFeedbackAnswerFixationEnabled());
1986 
1987  if( $this->object->isForceInstantFeedbackEnabled() )
1988  {
1989  $navigationGUI->setForceInstantResponseEnabled(true);
1990  $navigationGUI->setInstantFeedbackCommand(ilTestPlayerCommands::SUBMIT_SOLUTION);
1991  }
1992  else
1993  {
1994  $navigationGUI->setInstantFeedbackCommand(ilTestPlayerCommands::SHOW_INSTANT_RESPONSE);
1995  }
1996  }
1997 
1998  // hints
1999  if( $this->object->isOfferingQuestionHintsEnabled() )
2000  {
2001  $activeId = $this->testSession->getActiveId();
2002  $pass = $this->testSession->getPass();
2003 
2004  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
2005  $questionHintTracking = new ilAssQuestionHintTracking($questionId, $activeId, $pass);
2006 
2007  if( $questionHintTracking->requestsPossible() )
2008  {
2009  $navigationGUI->setRequestHintCommand(ilTestPlayerCommands::CONFIRM_HINT_REQUEST);
2010  }
2011 
2012  if( $questionHintTracking->requestsExist() )
2013  {
2014  $navigationGUI->setShowHintsCommand(ilTestPlayerCommands::SHOW_REQUESTED_HINTS_LIST);
2015  }
2016  }
2017 
2018  $navigationGUI->setCharSelectorEnabled($charSelectorAvailable);
2019 
2020  if($this->object->getShowMarker())
2021  {
2022  include_once "./Modules/Test/classes/class.ilObjTest.php";
2023  $solved_array = ilObjTest::_getSolvedQuestions($this->testSession->getActiveId(), $questionId);
2024  $solved = 0;
2025 
2026  if(count($solved_array) > 0)
2027  {
2028  $solved = array_pop($solved_array);
2029  $solved = $solved["solved"];
2030  }
2031 
2032  if($solved == 1)
2033  {
2034  $navigationGUI->setQuestionMarkCommand(ilTestPlayerCommands::UNMARK_QUESTION_SAVE);
2035  $navigationGUI->setQuestionMarked(true);
2036  }
2037  else
2038  {
2039  $navigationGUI->setQuestionMarkCommand(ilTestPlayerCommands::MARK_QUESTION_SAVE);
2040  $navigationGUI->setQuestionMarked(false);
2041  }
2042  }
2043 
2044  return $navigationGUI;
2045  }
2046 
2050  protected function getFinishTestCommand()
2051  {
2052  if( !$this->object->getListOfQuestionsEnd() )
2053  {
2054  return 'finishTest';
2055  }
2056 
2057  if( $this->object->areObligationsEnabled() )
2058  {
2059  $allObligationsAnswered = ilObjTest::allObligationsAnswered(
2060  $this->testSession->getTestId(), $this->testSession->getActiveId(), $this->testSession->getPass()
2061  );
2062 
2063  if( !$allObligationsAnswered )
2064  {
2065  return 'outQuestionSummaryWithObligationsInfo';
2066  }
2067  }
2068 
2069  return 'outQuestionSummary';
2070  }
2071 
2075  protected function populateIntermediateSolutionSaver(assQuestionGUI $questionGui)
2076  {
2077  $this->tpl->addJavaScript(ilUtil::getJSLocation("autosave.js", "Modules/Test"));
2078 
2079  $this->tpl->setVariable("AUTOSAVE_URL", $this->ctrl->getFormAction(
2080  $this, ilTestPlayerCommands::AUTO_SAVE, "", true
2081  ));
2082 
2083  if( $questionGui->isAutosaveable() && $this->object->getAutosave() )
2084  {
2085  $formAction = $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::AUTO_SAVE, '', false, false);
2086 
2087  $this->tpl->touchBlock('autosave');
2088  $this->tpl->setVariable("AUTOSAVEFORMACTION", $formAction);
2089  $this->tpl->setVariable("AUTOSAVEINTERVAL", $this->object->getAutosaveIval());
2090  }
2091  }
2092 
2096  protected function populateInstantResponseBlocks(assQuestionGUI $questionGui, $authorizedSolution)
2097  {
2098  // This controls if the solution should be shown.
2099  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Solutions"
2100  if($this->object->getInstantFeedbackSolution())
2101  {
2102  $show_question_inline_score = $this->determineInlineScoreDisplay();
2103 
2104  // Notation of the params prior to getting rid of this crap in favor of a class
2105  $solutionoutput = $questionGui->getSolutionOutput($this->testSession->getActiveId(), #active_id
2106  $this->testSession->getPass(), #pass
2107  FALSE, #graphical_output
2108  $show_question_inline_score, #result_output
2109  FALSE, #show_question_only
2110  FALSE, #show_feedback
2111  TRUE, #show_correct_solution
2112  FALSE, #show_manual_scoring
2113  FALSE #show_question_text
2114  );
2115  $this->populateSolutionBlock($solutionoutput);
2116  }
2117 
2118  $reachedPoints = $questionGui->object->getAdjustedReachedPoints(
2119  $this->testSession->getActiveId(), NULL, $authorizedSolution
2120  );
2121 
2122  $maxPoints = $questionGui->object->getMaximumPoints();
2123 
2124  $solutionCorrect = ($reachedPoints == $maxPoints);
2125 
2126  // This controls if the score should be shown.
2127  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Results (Only Points)"
2128  if($this->object->getAnswerFeedbackPoints())
2129  {
2130  $this->populateScoreBlock($reachedPoints, $maxPoints);
2131  }
2132 
2133  // This controls if the generic feedback should be shown.
2134  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Solutions"
2135  if($this->object->getGenericAnswerFeedback())
2136  {
2137  $this->populateGenericFeedbackBlock($questionGui, $solutionCorrect);
2138  }
2139 
2140  // This controls if the specific feedback should be shown.
2141  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Answer-Specific Feedback"
2142  if($this->object->getSpecificAnswerFeedback())
2143  {
2144  $this->populateSpecificFeedbackBlock($questionGui);
2145  }
2146  }
2147 
2148  protected function getCurrentSequenceElement()
2149  {
2150  if( $this->getSequenceElementParameter() )
2151  {
2152  return $this->getSequenceElementParameter();
2153  }
2154 
2155  return $this->testSession->getLastSequence();
2156  }
2157 
2158  protected function getSequenceElementParameter()
2159  {
2160  if( isset($_GET['sequence']) )
2161  {
2162  return $_GET['sequence'];
2163  }
2164 
2165  return null;
2166  }
2167 
2168  protected function getPresentationModeParameter()
2169  {
2170  if( isset($_GET['pmode']) )
2171  {
2172  return $_GET['pmode'];
2173  }
2174 
2175  return null;
2176  }
2177 
2178  protected function getInstantResponseParameter()
2179  {
2180  if( isset($_GET['instresp']) )
2181  {
2182  return $_GET['instresp'];
2183  }
2184 
2185  return null;
2186  }
2187 
2188  protected function getNextCommandParameter()
2189  {
2190  if( isset($_POST['nextcmd']) && strlen($_POST['nextcmd']) )
2191  {
2192  return $_POST['nextcmd'];
2193  }
2194 
2195  return null;
2196  }
2197 
2198  protected function getNextSequenceParameter()
2199  {
2200  if( isset($_POST['nextseq']) && is_numeric($_POST['nextseq']) )
2201  {
2202  return (int)$_POST['nextseq'];
2203  }
2204 
2205  return null;
2206  }
2207 
2211  private $cachedQuestionGuis = array();
2212 
2218  protected function getQuestionGuiInstance($questionId, $fromCache = true)
2219  {
2220  if( !$fromCache || !isset($this->cachedQuestionGuis[$questionId]) )
2221  {
2222  $questionGui = $this->object->createQuestionGUI("", $questionId);
2223  $questionGui->setTargetGui($this);
2224  $questionGui->setPresentationContext(assQuestionGUI::PRESENTATION_CONTEXT_TEST);
2225  $questionGui->object->setObligationsToBeConsidered($this->object->areObligationsEnabled());
2226  $questionGui->object->setOutputType(OUTPUT_JAVASCRIPT);
2227  $questionGui->object->setShuffler($this->buildQuestionAnswerShuffler($questionId));
2228 
2229  $this->cachedQuestionGuis[$questionId] = $questionGui;
2230  }
2231 
2232  return $this->cachedQuestionGuis[$questionId];
2233  }
2234 
2238  private $cachedQuestionObjects = array();
2239 
2244  protected function getQuestionInstance($questionId, $fromCache = true)
2245  {
2246  global $ilDB, $ilUser;
2247 
2248  if( !$fromCache || !isset($this->cachedQuestionObjects[$questionId]) )
2249  {
2250  $questionOBJ = assQuestion::_instantiateQuestion($questionId);
2251 
2252  $assSettings = new ilSetting('assessment');
2253  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionProcessLockerFactory.php';
2254  $processLockerFactory = new ilAssQuestionProcessLockerFactory($assSettings, $ilDB);
2255  $processLockerFactory->setQuestionId($questionOBJ->getId());
2256  $processLockerFactory->setUserId($ilUser->getId());
2257  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
2258  $processLockerFactory->setAssessmentLogEnabled(ilObjAssessmentFolder::_enabledAssessmentLogging());
2259  $questionOBJ->setProcessLocker($processLockerFactory->getLocker());
2260 
2261  $questionOBJ->setObligationsToBeConsidered($this->object->areObligationsEnabled());
2262  $questionOBJ->setOutputType(OUTPUT_JAVASCRIPT);
2263 
2264  $this->cachedQuestionObjects[$questionId] = $questionOBJ;
2265  }
2266 
2267  return $this->cachedQuestionObjects[$questionId];
2268  }
2269 
2274  protected function buildQuestionAnswerShuffler($questionId)
2275  {
2276  require_once 'Services/Randomization/classes/class.ilArrayElementShuffler.php';
2277  $shuffler = new ilArrayElementShuffler();
2278 
2279  $shuffler->setSeed(
2280  $questionId.$this->testSession->getActiveId().$this->testSession->getPass()
2281  );
2282 
2283  return $shuffler;
2284  }
2285 
2291  protected function handleTearsAndAngerQuestionIsNull($questionId, $sequenceElement)
2292  {
2293  global $ilLog;
2294 
2295  $ilLog->write("INV SEQ:"
2296  ."active={$this->testSession->getActiveId()} "
2297  ."qId=$questionId seq=$sequenceElement "
2298  . serialize($this->testSequence)
2299  );
2300 
2301  $ilLog->logStack('INV SEQ');
2302 
2303  $this->ctrl->setParameter($this, 'sequence', $this->testSequence->getFirstSequence());
2304  $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION);
2305  }
2306 
2310  protected function populateMessageContent($contentHTML)
2311  {
2312  if($this->object->getKioskMode())
2313  {
2314  $this->tpl->addBlockfile($this->getContentBlockName(), 'content', "tpl.il_as_tst_kiosk_mode_content.html", "Modules/Test");
2315  $this->tpl->setContent($contentHTML);
2316  }
2317  else
2318  {
2319  $this->tpl->setVariable($this->getContentBlockName(), $contentHTML);
2320  }
2321  }
2322 
2323  protected function populateModals()
2324  {
2325  require_once 'Services/UIComponent/Button/classes/class.ilSubmitButton.php';
2326  require_once 'Services/UIComponent/Button/classes/class.ilLinkButton.php';
2327  require_once 'Services/UIComponent/Modal/classes/class.ilModalGUI.php';
2328 
2330  $this->populateNavWhileEditModal();
2331 
2332  if( $this->object->getKioskMode() )
2333  {
2334  $this->tpl->addJavaScript('Services/UICore/lib/bootstrap-3.2.0/dist/js/bootstrap.min.js', true);
2335  }
2336  }
2337 
2338  protected function populateDiscardSolutionModal()
2339  {
2340  $tpl = new ilTemplate('tpl.tst_player_confirmation_modal.html', true, true, 'Modules/Test');
2341 
2342  $tpl->setVariable('CONFIRMATION_TEXT', $this->lng->txt('discard_answer_confirmation'));
2343 
2344  $button = ilSubmitButton::getInstance();
2345  $button->setCommand(ilTestPlayerCommands::DISCARD_SOLUTION);
2346  $button->setCaption('yes');
2347  $tpl->setCurrentBlock('buttons');
2348  $tpl->setVariable('BUTTON', $button->render());
2349  $tpl->parseCurrentBlock();
2350 
2351  $button = ilLinkButton::getInstance();
2352  $button->setId('tst_cancel_discard_button');
2353  $button->setCaption('no');
2354  $button->setPrimary(true);
2355  $tpl->setCurrentBlock('buttons');
2356  $tpl->setVariable('BUTTON', $button->render());
2357  $tpl->parseCurrentBlock();
2358 
2359  $modal = ilModalGUI::getInstance();
2360  $modal->setId('tst_discard_solution_modal');
2361  $modal->setHeading($this->lng->txt('discard_answer'));
2362  $modal->setBody($tpl->get());
2363 
2364  $this->tpl->setCurrentBlock('discard_solution_modal');
2365  $this->tpl->setVariable('DISCARD_SOLUTION_MODAL', $modal->getHTML());
2366  $this->tpl->parseCurrentBlock();
2367 
2368  $this->tpl->addJavaScript('Modules/Test/js/ilTestPlayerDiscardSolutionModal.js', true);
2369  }
2370 
2371  protected function populateNavWhileEditModal()
2372  {
2373  require_once 'Services/Form/classes/class.ilFormPropertyGUI.php';
2374  require_once 'Services/Form/classes/class.ilHiddenInputGUI.php';
2375 
2376  $tpl = new ilTemplate('tpl.tst_player_confirmation_modal.html', true, true, 'Modules/Test');
2377 
2378  $tpl->setVariable('CONFIRMATION_TEXT', $this->lng->txt('tst_nav_while_edit_modal_text'));
2379 
2380  $button = ilSubmitButton::getInstance();
2381  $button->setCommand(ilTestPlayerCommands::SUBMIT_SOLUTION);
2382  $button->setCaption('tst_nav_while_edit_modal_save_btn');
2383  $button->setPrimary(true);
2384  $tpl->setCurrentBlock('buttons');
2385  $tpl->setVariable('BUTTON', $button->render());
2386  $tpl->parseCurrentBlock();
2387 
2388  foreach(array('nextcmd', 'nextseq') as $hiddenPostVar)
2389  {
2390  $nextCmdInp = new ilHiddenInputGUI($hiddenPostVar);
2391  $nextCmdInp->setValue('');
2392  $tpl->setCurrentBlock('hidden_inputs');
2393  $tpl->setVariable('HIDDEN_INPUT', $nextCmdInp->getToolbarHTML());
2394  $tpl->parseCurrentBlock();
2395  }
2396 
2397  $button = ilLinkButton::getInstance();
2398  $this->ctrl->setParameter($this, 'pmode', self::PRESENTATION_MODE_VIEW);
2399  $button->setId('nextCmdLink');
2400  $button->setUrl('#');
2401  $this->ctrl->setParameter($this, 'pmode', $this->getPresentationModeParameter());
2402  $button->setCaption('tst_nav_while_edit_modal_nosave_btn');
2403  $tpl->setCurrentBlock('buttons');
2404  $tpl->setVariable('BUTTON', $button->render());
2405  $tpl->parseCurrentBlock();
2406 
2407  $button = ilLinkButton::getInstance();
2408  $button->setId('tst_cancel_nav_while_edit_button');
2409  $button->setCaption('tst_nav_while_edit_modal_cancel_btn');
2410  $tpl->setCurrentBlock('buttons');
2411  $tpl->setVariable('BUTTON', $button->render());
2412  $tpl->parseCurrentBlock();
2413 
2414  $modal = ilModalGUI::getInstance();
2415  $modal->setId('tst_nav_while_edit_modal');
2416  $modal->setHeading($this->lng->txt('tst_nav_while_edit_modal_header'));
2417  $modal->setBody($tpl->get());
2418 
2419  $this->tpl->setCurrentBlock('nav_while_edit_modal');
2420  $this->tpl->setVariable('NAV_WHILE_EDIT_MODAL', $modal->getHTML());
2421  $this->tpl->parseCurrentBlock();
2422 
2423  $this->tpl->addJavaScript('Modules/Test/js/ilTestPlayerNavWhileEditModal.js', true);
2424  }
2425 
2426  protected function getQuestionsDefaultPresentationMode($isQuestionWorkedThrough)
2427  {
2428  if( $isQuestionWorkedThrough )
2429  {
2430  return self::PRESENTATION_MODE_VIEW;
2431  }
2432 
2433  return self::PRESENTATION_MODE_EDIT;
2434  }
2435 }
ftimestamp2datetimeDB($aTimestamp)
Timestamp to database datetime.
static makeDirParents($a_dir)
Create a new directory and all parent directories.
saveQuestionSolution($authorized=true, $force=false)
saves the user input of a question
ILIAS Setting Class.
startPlayerCmd()
Start a test for the first time.
print $file
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)
exit
Definition: login.php:54
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
static allObligationsAnswered($test_id, $active_id, $pass)
checks wether all questions marked as obligatory were answered within the test pass with given testId...
$_POST['username']
Definition: cron.php:12
isFirstQuestionInSequence($sequenceElement)
$_SESSION["AccountId"]
$result
checkOnlineTestAccess()
check access restrictions like client ip, partipating user etc.
setAnonymousIdCmd()
Sets a session variable with the test access code for an anonymous test user.
determineSolutionPassIndex(assQuestionGUI $questionGui)
static getStyleSheetLocation($mode="output", $a_css_name="", $a_css_location="")
get full style sheet file name (path inclusive) of current user
$_GET["client_id"]
showQuestionEditable(assQuestionGUI $questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse)
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 ...
setPreviousSolutionPrefilled($previousSolutionPrefilled)
saveTagsCmd()
Save tags for tagging gui.
isTestSignRedirectRequired($activeId, $lastFinishedPass)
static get($a_var)
Get a value.
getResultsSignature()
Returns HTML code for a signature field.
$url
Definition: shib_logout.php:72
setAnonymousId($anonymous_id)
_getSolvedQuestions($active_id, $question_fi=null)
get solved questions
handleUserSettings()
Handles some form parameters on starting and resuming a test.
_enabledAssessmentLogging()
check wether assessment logging is enabled or not
showListOfAnswers($active_id, $pass=NULL, $top_data="", $bottom_data="")
Creates an output of the list of answers for a test participant during the test (only the actual pass...
const OUTPUT_JAVASCRIPT
getContentBlockName()
Returns the name of the current content block (depends on the kiosk mode setting) ...
confirmHintRequestCmd()
Go to hint request confirmation.
getSyntaxStylePath()
get syntax style path
__construct($a_object)
ilTestOutputGUI constructor
maxProcessingTimeReached()
Outputs a message when the maximum processing time is reached.
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)
This class represents a hidden form property in a property form.
static getInstance(ilTestSession $a_test_session)
confirmFinishCmd()
The final submission of a test was confirmed.
markQuestionCmd()
Set a question solved.
handleSkillTriggering(ilTestSession $testSession)
buildEditableStateQuestionNavigationGUI($questionId, $charSelectorAvailable)
showSideList($presentationMode, $currentSequenceElement)
populateGenericFeedbackBlock(assQuestionGUI $question_gui, $solutionCorrect)
static getJSLocation($a_js_name, $a_js_location="", $add_version=FALSE)
get full javascript file name (path inclusive) of current user
outCorrectSolution()
Creates an output of the solution of an answer compared to the correct solution.
special template class to simplify handling of ITX/PEAR
resumePlayerCmd()
Resume a test at the last position.
endingTimeReached()
handle endingTimeReached
static formatDate(ilDateTime $date)
Format a date public.
Output class for assessment test evaluation.
populateTestNavigationToolbar(ilTestNavigationToolbarGUI $toolbarGUI)
lookupExamId($active_id, $pass)
const IL_COMP_MODULE
Date and time handling
redirection script todo: (a better solution should control the processing via a xml file) ...
static _getCurrentGUI(ilObjTest $a_test_obj=null)
Get the GUI that is used for the currently available selector (other GUI instances may exist for conf...
Test session handler.
$results
Basic GUI class for assessment questions.
_getUsePreviousAnswers($active_id, $user_active_user_setting=false)
Returns if the previous results should be hidden for a learner.
static getIdleValue($fixedMode=false)
Returns the idle time in seconds.
const REDIRECT_KIOSK
static _instantiateQuestion($question_id)
outQuestionForTest( $formaction, $active_id, $pass, $is_question_postponed=FALSE, $user_post_solutions=FALSE, $show_specific_inline_feedback=FALSE)
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
$filename
Definition: buildRTE.php:89
isShowingPostponeStatusReguired($questionId)
outQuestionSummaryCmd($fullpage=true, $contextFinishTest=false, $obligationsNotAnswered=false, $obligationsFilter=false)
Output of a summary of all test questions for test participants.
unmarkQuestionCmd()
Set a question unsolved.
const IL_CAL_FKT_GETDATE
Class ilTestArchiver.
static getInstance()
Get instance.
isOptionalQuestionAnsweringConfirmationRequired($sequenceElement)
showQuestionViewable(assQuestionGUI $questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse)
isTestAccessible()
test accessible returns true if the user can perform the test
global $ilUser
Definition: imgupload.php:15
getContentStylePath($a_style_id)
get content style path
global $ilSetting
Definition: privfeed.php:40
populateQuestionNavigation($sequenceElement, $disabled)
$path
Definition: index.php:22
static generatePDF($pdf_output, $output_mode, $filename=null)
global $ilDB
getQuestionGuiInstance($questionId, $fromCache=true)
static buildExamId($active_id, $pass, $test_obj_id=null)
getQuestionInstance($questionId, $fromCache=true)
getQuestionsDefaultPresentationMode($isQuestionWorkedThrough)
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="")
output question page
Service GUI class for tests.
getAdditionalUsrDataHtmlAndPopulateWindowTitle($testSession, $active_id, $overwrite_anonymity=FALSE)
Returns the user data for a test results output.
$GLOBALS['PHPCAS_CLIENT']
This global variable is used by the interface class phpCAS.
Definition: CAS.php:276
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.
static redirect($a_script)
http redirect to other script
static getWebspaceDir($mode="filesystem")
get webspace directory
populateIntermediateSolutionSaver(assQuestionGUI $questionGui)
populateScoreBlock($reachedPoints, $maxPoints)
Output class for assessment test execution.
Confirmation screen class.
setNavigationGUI($navigationGUI)