ILIAS  Release_5_0_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilTestOutputGUI.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/class.ilTestPlayerAbstractGUI.php';
5 
21 {
25  public function executeCommand()
26  {
27  global $ilUser, $ilDB, $ilPluginAdmin, $lng, $ilTabs;
28 
29  $ilTabs->clearTargets();
30 
31  $cmd = $this->ctrl->getCmd();
32  $next_class = $this->ctrl->getNextClass($this);
33 
34  $this->ctrl->saveParameter($this, "sequence");
35  $this->ctrl->saveParameter($this, "active_id");
36 
37  if (preg_match("/^gotoquestion_(\\d+)$/", $cmd, $matches))
38  {
39  $cmd = "gotoquestion";
40  if (strlen($matches[1]))
41  {
42  $this->ctrl->setParameter($this, 'gotosequence', $matches[1]);
43  }
44  }
45 
46  $testSessionFactory = new ilTestSessionFactory($this->object);
47  $this->testSession = $testSessionFactory->getSession($_GET['active_id']);
48 
49  $this->ensureExistingTestSession($this->testSession);
50 
51  $this->initProcessLocker($this->testSession->getActiveId());
52 
53  $testSequenceFactory = new ilTestSequenceFactory($ilDB, $lng, $ilPluginAdmin, $this->object);
54  $this->testSequence = $testSequenceFactory->getSequence($this->testSession);
55  $this->testSequence->loadFromDb();
56  $this->testSequence->loadQuestions();
57 
58  include_once 'Services/jQuery/classes/class.iljQueryUtil.php';
60  include_once "./Services/YUI/classes/class.ilYuiUtil.php";
62 
64 
65  $cmd = $this->getCommand($cmd);
66 
67  switch($next_class)
68  {
69  case 'ilassquestionpagegui':
70 
71  $questionId = $this->testSequence->getQuestionForSequence( $this->calculateSequence() );
72 
73  require_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPageGUI.php";
74  $page_gui = new ilAssQuestionPageGUI($questionId);
75  $ret = $this->ctrl->forwardCommand($page_gui);
76  break;
77 
78  case 'iltestsubmissionreviewgui':
79  require_once './Modules/Test/classes/class.ilTestSubmissionReviewGUI.php';
80  $gui = new ilTestSubmissionReviewGUI($this, $this->object, $this->testSession);
81  $ret = $this->ctrl->forwardCommand($gui);
82  break;
83 
84  case 'ilassquestionhintrequestgui':
85 
86  $questionGUI = $this->object->createQuestionGUI(
87  "", $this->testSequence->getQuestionForSequence( $this->calculateSequence() )
88  );
89 
90  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
91  $questionHintTracking = new ilAssQuestionHintTracking(
92  $questionGUI->object->getId(), $this->testSession->getActiveId(), $this->testSession->getPass()
93  );
94 
95  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintRequestGUI.php';
96  $gui = new ilAssQuestionHintRequestGUI($this, 'redirectQuestion', $questionGUI, $questionHintTracking);
97 
98  $ret = $this->ctrl->forwardCommand($gui);
99 
100  break;
101 
102  case 'iltestsignaturegui':
103  require_once './Modules/Test/classes/class.ilTestSignatureGUI.php';
104  $gui = new ilTestSignatureGUI($this);
105  $ret = $this->ctrl->forwardCommand($gui);
106  break;
107 
108  case 'iltestpasswordprotectiongui':
109  require_once 'Modules/Test/classes/class.ilTestPasswordProtectionGUI.php';
110  $gui = new ilTestPasswordProtectionGUI($this->ctrl, $this->tpl, $this->lng, $this, $this->passwordChecker);
111  $ret = $this->ctrl->forwardCommand($gui);
112  break;
113 
114  default:
115 
116  $cmd .= 'Cmd';
117  $ret =& $this->$cmd();
118  break;
119  }
120  return $ret;
121  }
122 
123  protected function startTestCmd()
124  {
125  $_GET['activecommand'] = 'start';
126  $this->redirectQuestionCmd();
127  }
128 
132  protected function nextQuestionCmd()
133  {
134  $questionId = $this->testSequence->getQuestionForSequence($_GET["sequence"]);
135 
136  if( !$this->isParticipantsAnswerFixed($questionId) )
137  {
138  $this->saveQuestionSolution();
139  }
140 
141  $this->ctrl->setParameter($this, "activecommand", "next");
142  $this->ctrl->redirect($this, "redirectQuestion");
143  }
144 
148  protected function previousQuestionCmd()
149  {
150  $questionId = $this->testSequence->getQuestionForSequence($_GET["sequence"]);
151 
152  if( !$this->isParticipantsAnswerFixed($questionId) )
153  {
154  $this->saveQuestionSolution();
155  }
156 
157  $this->ctrl->setParameter($this, "activecommand", "previous");
158  $this->ctrl->redirect($this, "redirectQuestion");
159  }
160 
168  protected function redirectQuestionCmd()
169  {
170  global $ilUser;
171 
172  // check the test restrictions to access the test in case one
173  // of the test navigation commands was called by an external script
174  // e.g. $ilNavigationHistory
175  $executable = $this->object->isExecutable($this->testSession, $ilUser->getId());
176  if (!$executable["executable"])
177  {
178  ilUtil::sendInfo($executable["errormessage"], TRUE);
179  $this->ctrl->redirectByClass("ilobjtestgui", "infoScreen");
180  }
181  switch ($_GET["activecommand"])
182  {
183  case "next":
184  $this->sequence = $this->calculateSequence();
185  if ($this->sequence === FALSE)
186  {
187  if ($this->object->getListOfQuestionsEnd())
188  {
189 
190  $allObligationsAnswered = ilObjTest::allObligationsAnswered(
191  $this->testSession->getTestId(),
192  $this->testSession->getActiveId(),
193  $this->testSession->getPass()
194  );
195 
196  if( $this->object->areObligationsEnabled() && !$allObligationsAnswered )
197  {
198  $this->ctrl->redirect($this, "outQuestionSummaryWithObligationsInfo");
199  }
200 
201  $this->outQuestionSummaryCmd();
202  }
203  else
204  {
205  $this->ctrl->redirect($this, "finishTest");
206  }
207  }
208  else
209  {
210  $this->testSession->setLastSequence($this->sequence);
211  $this->testSession->saveToDb();
212  $this->outTestPage(false);
213  }
214  break;
215  case "previous":
216  $this->sequence = $this->calculateSequence();
217  $this->testSession->setLastSequence($this->sequence);
218  $this->testSession->saveToDb();
219  if ($this->sequence === FALSE)
220  {
221  $this->ctrl->redirect($this, "outIntroductionPage");
222  }
223  else
224  {
225  $this->outTestPage(false);
226  }
227  break;
228  case "postpone":
229  $this->sequence = $this->calculateSequence();
230  $nextSequence = $this->testSequence->getNextSequence($this->sequence);
231  $this->testSequence->postponeSequence($this->sequence);
232  $this->testSequence->saveToDb();
233  $this->testSession->setLastSequence($nextSequence);
234  $this->testSession->saveToDb();
235  $this->sequence = $nextSequence;
236  $this->outTestPage(false);
237  break;
238  case "setmarked":
239  $this->sequence = $this->calculateSequence();
240  $this->testSession->setLastSequence($this->sequence);
241  $this->testSession->saveToDb();
242  $q_id = $this->testSequence->getQuestionForSequence($_GET["sequence"]);
243  $this->object->setQuestionSetSolved(1, $q_id, $ilUser->getId());
244  $this->outTestPage(false);
245  break;
246  case "resetmarked":
247  $this->sequence = $this->calculateSequence();
248  $this->testSession->setLastSequence($this->sequence);
249  $this->testSession->saveToDb();
250  $q_id = $this->testSequence->getQuestionForSequence($_GET["sequence"]);
251  $this->object->setQuestionSetSolved(0, $q_id, $ilUser->getId());
252  $this->outTestPage(false);
253  break;
254  case "directfeedback":
255  $this->sequence = $this->calculateSequence();
256  $this->testSession->setLastSequence($this->sequence);
257  $this->testSession->saveToDb();
258  $this->outTestPage(true);
259  break;
260  case "handleQuestionAction":
261  $this->sequence = $this->calculateSequence();
262  $this->testSession->setLastSequence($this->sequence);
263  $this->testSession->saveToDb();
264  $this->outTestPage(false);
265  break;
266  case "summary":
267  $this->ctrl->redirect($this, "outQuestionSummary");
268  break;
269  case "summary_obligations":
270  $this->ctrl->redirect($this, "outQuestionSummaryWithObligationsInfo");
271  break;
272  case "summary_obligations_only":
273  $this->ctrl->redirect($this, "outObligationsOnlySummary");
274  break;
275  case "start":
276  $_SESSION['tst_pass_finish'] = 0;
277 
278  // ensure existing test session
279  $this->testSession->setUserId($ilUser->getId());
280  $this->testSession->setAnonymousId($_SESSION["tst_access_code"][$this->object->getTestId()]);
281  $this->testSession->setObjectiveOrientedContainerId($this->getObjectiveOrientedContainerId());
282  $this->testSession->saveToDb();
283 
284  $active_id = $this->testSession->getActiveId();
285  $this->ctrl->setParameter($this, "active_id", $active_id);
286  $shuffle = $this->object->getShuffleQuestions();
287  if ($this->object->isRandomTest())
288  {
290 
291  $this->object->loadQuestions();
292  $shuffle = FALSE; // shuffle is already done during the creation of the random questions
293  }
294 
295  assQuestion::_updateTestPassResults(
296  $active_id, $this->testSession->getPass(), $this->object->areObligationsEnabled(), null, $this->object->id
297  );
298 
299  // ensure existing test sequence
300  if( !$this->testSequence->hasSequence() )
301  {
302  $this->testSequence->createNewSequence($this->object->getQuestionCount(), $shuffle);
303  $this->testSequence->saveToDb();
304  }
305 
306  if( $this->testSession->isObjectiveOriented() )
307  {
308  $this->testSequence->loadFromDb();
309  $this->testSequence->loadQuestions();
310 
312  $this->testSession, $this->testSequence
313  );
314  }
315 
316  $active_time_id = $this->object->startWorkingTime($this->testSession->getActiveId(), $this->testSession->getPass());
317  $_SESSION["active_time_id"] = $active_time_id;
318  if ($this->object->getListOfQuestionsStart())
319  {
320  $this->ctrl->setParameter($this, "activecommand", "summary");
321  $this->ctrl->redirect($this, "redirectQuestion");
322  }
323  else
324  {
325  $this->ctrl->setParameter($this, "sequence", $this->sequence);
326  $this->ctrl->setParameter($this, "activecommand", "gotoquestion");
327  $this->ctrl->saveParameter($this, "tst_javascript");
328  $this->ctrl->redirect($this, "redirectQuestion");
329  }
330  break;
331  case "resume":
332  $_SESSION['tst_pass_finish'] = 0;
333  $active_id = $this->testSession->getActiveId();
334  $this->ctrl->setParameter($this, "active_id", $active_id);
335 
336  if ($this->object->isRandomTest())
337  {
338  if (!$this->testSequence->hasRandomQuestionsForPass($active_id, $this->testSession->getPass()))
339  {
340  // create a new set of random questions
342  }
343  }
344  $shuffle = $this->object->getShuffleQuestions();
345  if ($this->object->isRandomTest())
346  {
347  $shuffle = FALSE;
348  }
349 
350  assQuestion::_updateTestPassResults(
351  $active_id, $this->testSession->getPass(), $this->object->areObligationsEnabled(), null, $this->object->id
352  );
353 
354  // ensure existing test sequence
355  if( !$this->testSequence->hasSequence() )
356  {
357  $this->testSequence->createNewSequence($this->object->getQuestionCount(), $shuffle);
358  $this->testSequence->saveToDb();
359  }
360 
361  $this->sequence = $this->testSession->getLastSequence();
362  $active_time_id = $this->object->startWorkingTime($active_id, $this->testSession->getPass());
363  $_SESSION["active_time_id"] = $active_time_id;
364  if ($this->object->getListOfQuestionsStart())
365  {
366  $this->ctrl->setParameter($this, "activecommand", "summary");
367  $this->ctrl->redirect($this, "redirectQuestion");
368  }
369  else
370  {
371  $this->ctrl->setParameter($this, "sequence", $this->sequence);
372  $this->ctrl->setParameter($this, "activecommand", "gotoquestion");
373  $this->ctrl->saveParameter($this, "tst_javascript");
374  $this->ctrl->redirect($this, "redirectQuestion");
375  }
376  break;
377 
378  case 'test_submission_overview':
379  require_once './Modules/Test/classes/class.ilTestSubmissionReviewGUI.php';
380  $this->ctrl->redirectByClass('ilTestSubmissionReviewGUI', "show");
381  break;
382 
383  case "back":
384  case "gotoquestion":
385  default:
386  $_SESSION['tst_pass_finish'] = 0;
387  if (array_key_exists("tst_javascript", $_GET))
388  {
389  $ilUser->writePref("tst_javascript", $_GET["tst_javascript"]);
390  }
391  $this->sequence = $this->calculateSequence();
392  if (strlen($_GET['gotosequence'])) $this->sequence = $_GET['gotosequence'];
393  $this->testSession->setLastSequence($this->sequence);
394  $this->testSession->saveToDb();
395  $this->outTestPage(false);
396  break;
397  }
398  }
399 
400  private function isValidSequenceElement($sequenceElement)
401  {
402  if( $sequenceElement < 1 )
403  {
404  return false;
405  }
406 
407  if( !$this->testSequence->getPositionOfSequence($sequenceElement) )
408  {
409  return false;
410  }
411 
412  return true;
413  }
414 
418  protected function outWorkingForm($sequence = "", $test_id, $postpone_allowed, $directfeedback = false)
419  {
420  global $ilUser;
421 
422  if( !$this->isValidSequenceElement($sequence) )
423  {
424  $sequence = $this->testSequence->getFirstSequence();
425  }
426 
427  $_SESSION["active_time_id"]= $this->object->startWorkingTime($this->testSession->getActiveId(),
428  $this->testSession->getPass()
429  );
430 
431  $this->populateContentStyleBlock();
432  $this->populateSyntaxStyleBlock();
433 
434  if ($this->object->getListOfQuestions())
435  {
436  $this->showSideList();
437  }
438 
439  $questionId = $this->testSequence->getQuestionForSequence($sequence);
440 
441  if( !(int)$questionId && $this->testSession->isObjectiveOriented() )
442  {
444  sprintf($this->lng->txt('tst_objective_oriented_test_pass_without_questions'), $this->object->getTitle()), true
445  );
446  $this->performCustomRedirect();
447  }
448 
449  $question_gui = $this->object->createQuestionGUI("", $questionId);
450 
451  if( !is_object($question_gui) )
452  {
453  global $ilLog;
454 
455  $ilLog->write(
456  "INV SEQ: active={$this->testSession->getActiveId()} qId=$questionId seq=$sequence "
457  .serialize($this->testSequence)
458  );
459 
460  $ilLog->logStack('INV SEQ');
461 
462  $this->ctrl->setParameter($this, 'gotosequence', $this->testSequence->getFirstSequence());
463  $this->ctrl->setParameter($this, 'activecommand', 'gotoquestion');
464  $this->ctrl->redirect($this, 'redirectQuestion');
465  }
466 
467  $question_gui->setTargetGui($this);
468 
469  if ($this->object->getJavaScriptOutput())
470  {
471  $question_gui->object->setOutputType(OUTPUT_JAVASCRIPT);
472  }
473 
474  $is_postponed = $this->testSequence->isPostponedQuestion($question_gui->object->getId());
475  $this->ctrl->setParameter($this, "sequence", "$sequence");
476  $formaction = $this->ctrl->getFormAction($this, "gotoQuestion");
477 
478  $question_gui->setSequenceNumber($this->testSequence->getPositionOfSequence($sequence));
479  $question_gui->setQuestionCount($this->testSequence->getUserQuestionCount());
480 
481 
482  // output question
483  $user_post_solution = FALSE;
484  if (array_key_exists("previouspost", $_SESSION))
485  {
486  $user_post_solution = $_SESSION["previouspost"];
487  unset($_SESSION["previouspost"]);
488  }
489 
490  // Determine $answer_feedback: It should hold a boolean stating if answer-specific-feedback is to be given.
491  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Answer-Specific Feedback"
492  // $directfeedback holds a boolean stating if the instant feedback was requested using the "Check" button.
493  $answer_feedback = FALSE;
494  if (($directfeedback) && ($this->object->getSpecificAnswerFeedback()))
495  {
496  $answer_feedback = TRUE;
497  }
498 
499  if( $this->isParticipantsAnswerFixed($questionId) )
500  {
501  $solutionoutput = $question_gui->getSolutionOutput(
502  $this->testSession->getActiveId(), #active_id
503  NULL, #pass
504  FALSE, #graphical_output
505  false, #result_output
506  true, #show_question_only
507  $answer_feedback, #show_feedback
508  false, #show_correct_solution
509  FALSE, #show_manual_scoring
510  true #show_question_text
511  );
512 
513  $pageoutput = $question_gui->outQuestionPage(
514  "", $this->testSequence->isPostponedQuestion($questionId),
515  $this->testSession->getActiveId(),
516  $solutionoutput
517  );
518 
519  $this->tpl->setVariable("QUESTION_OUTPUT", $pageoutput);
520  $this->tpl->setVariable("FORMACTION", $formaction);
521 
522  $directfeedback = true;
523  }
524  else
525  {
526  // Answer specific feedback is rendered into the display of the test question with in the concrete question types outQuestionForTest-method.
527  // Notation of the params prior to getting rid of this crap in favor of a class
528  $question_gui->outQuestionForTest(
529  $formaction, #form_action
530  $this->testSession->getActiveId(), #active_id
531  NULL, #pass
532  $is_postponed, #is_postponed
533  $user_post_solution, #user_post_solution
534  $answer_feedback #answer_feedback == inline_specific_feedback
535  );
536  // The display of specific inline feedback and specific feedback in an own block is to honor questions, which
537  // have the possibility to embed the specific feedback into their output while maintaining compatibility to
538  // questions, which do not have such facilities. E.g. there can be no "specific inline feedback" for essay
539  // questions, while the multiple-choice questions do well.
540 
541  $this->fillQuestionRelatedNavigation($question_gui);
542  }
543 
544  if ($directfeedback)
545  {
546  // This controls if the solution should be shown.
547  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Solutions"
548  if ($this->object->getInstantFeedbackSolution())
549  {
550  $show_question_inline_score = $this->determineInlineScoreDisplay();
551 
552  // Notation of the params prior to getting rid of this crap in favor of a class
553  $solutionoutput = $question_gui->getSolutionOutput(
554  $this->testSession->getActiveId(), #active_id
555  NULL, #pass
556  FALSE, #graphical_output
557  $show_question_inline_score, #result_output
558  FALSE, #show_question_only
559  FALSE, #show_feedback
560  TRUE, #show_correct_solution
561  FALSE, #show_manual_scoring
562  FALSE #show_question_text
563  );
564  $this->populateSolutionBlock( $solutionoutput );
565  }
566 
567  // This controls if the score should be shown.
568  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Results (Only Points)"
569  if ($this->object->getAnswerFeedbackPoints())
570  {
571  $reachedPoints = $question_gui->object->getAdjustedReachedPoints($this->testSession->getActiveId(), NULL);
572  $maxPoints = $question_gui->object->getMaximumPoints();
573 
574  $this->populateScoreBlock( $reachedPoints, $maxPoints );
575  }
576 
577  // This controls if the generic feedback should be shown.
578  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Solutions"
579  if ($this->object->getGenericAnswerFeedback())
580  {
581  $this->populateGenericFeedbackBlock( $question_gui );
582  }
583 
584  // This controls if the specific feedback should be shown.
585  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Answer-Specific Feedback"
586  if ($this->object->getSpecificAnswerFeedback())
587  {
588  $this->populateSpecificFeedbackBlock( $question_gui );
589  }
590  }
591 
593 
594  if ($postpone_allowed && !$is_postponed)
595  {
596  $this->populatePostponeButtons();
597  }
598 
599  if ($this->object->getListOfQuestions())
600  {
601  $this->populateSummaryButtons();
602  }
603 
604  if ($this->object->getShowCancel())
605  {
606  $this->populateCancelButtonBlock();
607  }
608 
609  if ($this->isLastQuestionInSequence( $question_gui ))
610  {
611  if ($this->object->getListOfQuestionsEnd())
612  {
614  }
615  else
616  {
618  }
619  }
620  else
621  {
623  }
624 
625  if ($this->object->getShowMarker())
626  {
627  include_once "./Modules/Test/classes/class.ilObjTest.php";
628  $solved_array = ilObjTest::_getSolvedQuestions($this->testSession->getActiveId(), $question_gui->object->getId());
629  $solved = 0;
630 
631  if (count ($solved_array) > 0)
632  {
633  $solved = array_pop($solved_array);
634  $solved = $solved["solved"];
635  }
636 
637  if ($solved==1)
638  {
640  }
641  else
642  {
644  }
645  }
646 
647  $this->populateCharSelector();
648 
649  if ($this->object->getJavaScriptOutput())
650  {
651  $this->tpl->setVariable("JAVASCRIPT_TITLE", $this->lng->txt("disable_javascript"));
652  $this->ctrl->setParameter($this, "tst_javascript", "0");
653  $this->tpl->setVariable("JAVASCRIPT_URL", $this->ctrl->getLinkTarget($this, "gotoQuestion"));
654  }
655  else
656  {
657  $this->tpl->setVariable("JAVASCRIPT_TITLE", $this->lng->txt("enable_javascript"));
658  $this->ctrl->setParameter($this, "tst_javascript", "1");
659  $this->tpl->setVariable("JAVASCRIPT_URL", $this->ctrl->getLinkTarget($this, "gotoQuestion"));
660  }
661 
662  if ($question_gui->object->requiresJsSwitch() && !$this->object->getForceJS())
663  {
664  $this->tpl->touchBlock("jsswitch");
665  }
666 
667  $this->tpl->addJavaScript(ilUtil::getJSLocation("autosave.js", "Modules/Test"));
668 
669  $this->tpl->setVariable("AUTOSAVE_URL", $this->ctrl->getFormAction($this, "autosave", "", true));
670 
671  if ($question_gui->isAutosaveable()&& $this->object->getAutosave())
672  {
673  $this->tpl->touchBlock('autosave');
674  //$this->tpl->setVariable("BTN_SAVE", "Zwischenspeichern");
675  //$this->tpl->setVariable("CMD_SAVE", "gotoquestion_{$sequence}");
676  //$this->tpl->setVariable("AUTOSAVEFORMACTION", str_replace("&amp;", "&", $this->ctrl->getFormAction($this)));
677  $this->tpl->setVariable("AUTOSAVEFORMACTION", str_replace("&amp;", "&", $this->ctrl->getLinkTarget($this, "autosave")));
678  $this->tpl->setVariable("AUTOSAVEINTERVAL", $this->object->getAutosaveIval());
679  }
680 
681  if( $this->object->areObligationsEnabled() && ilObjTest::isQuestionObligatory($question_gui->object->getId()) )
682  {
683  $this->tpl->touchBlock('question_obligatory');
684  $this->tpl->setVariable('QUESTION_OBLIGATORY', $this->lng->txt('required_field'));
685  }
686  }
687 
688  protected function isFirstPageInSequence($sequence)
689  {
690  return $sequence == $this->testSequence->getFirstSequence();
691  }
692 
693  protected function isLastQuestionInSequence(assQuestionGUI $question_gui)
694  {
695  return $this->testSequence->getQuestionForSequence($this->testSequence->getLastSequence()) == $question_gui->object->getId();
696  }
697 
703  protected function canSaveResult()
704  {
705  return !$this->object->endingTimeReached() && !$this->isMaxProcessingTimeReached() && !$this->isNrOfTriesReached();
706  }
707 
711  public function saveQuestionSolution($force = FALSE)
712  {
713  $this->updateWorkingTime();
714  $this->saveResult = FALSE;
715  if (!$force)
716  {
717  $formtimestamp = $_POST["formtimestamp"];
718  if (strlen($formtimestamp) == 0) $formtimestamp = $_GET["formtimestamp"];
719  if ($formtimestamp != $_SESSION["formtimestamp"])
720  {
721  $_SESSION["formtimestamp"] = $formtimestamp;
722  }
723  else
724  {
725  return FALSE;
726  }
727  }
728  // save question solution
729  if ($this->canSaveResult() || $force)
730  {
731  // but only if the ending time is not reached
732  $q_id = $this->testSequence->getQuestionForSequence($_GET["sequence"]);
733  if (is_numeric($q_id) && (int)$q_id)
734  {
735  global $ilUser;
736 
737  $question_gui = $this->object->createQuestionGUI("", $q_id);
738  if ($this->object->getJavaScriptOutput())
739  {
740  $question_gui->object->setOutputType(OUTPUT_JAVASCRIPT);
741  }
742  $pass = NULL;
743  $active_id = $this->testSession->getActiveId();
744  if ($this->object->isRandomTest())
745  {
746  $pass = $this->object->_getPass($active_id);
747  }
748  $this->saveResult = $question_gui->object->persistWorkingState(
749  $active_id, $pass, $this->object->areObligationsEnabled()
750  );
751 
752  // update learning progress (is done in ilTestSession)
753  //include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
754  //ilLPStatusWrapper::_updateStatus($this->object->getId(), $ilUser->getId());
755 
756  if( $this->testSession->isObjectiveOriented() )
757  {
759  $this->testSession, $this->testSequence, $question_gui->object
760  );
761  }
762  }
763  }
764  if ($this->saveResult == FALSE)
765  {
766  $this->ctrl->setParameter($this, "save_error", "1");
767  $_SESSION["previouspost"] = $_POST;
768  }
769  return $this->saveResult;
770  }
771 
772  protected function showInstantResponseCmd()
773  {
774  $questionId = $this->testSequence->getQuestionForSequence($_GET["sequence"]);
775 
776  if( !$this->isParticipantsAnswerFixed($questionId) )
777  {
778  $this->saveQuestionSolution();
779 
780  $this->testSequence->setQuestionChecked($questionId);
781  $this->testSequence->saveToDb();
782  }
783 
784  $this->ctrl->setParameter($this, "activecommand", "directfeedback");
785  $this->ctrl->redirect($this, "redirectQuestion");
786  }
787 
788  protected function showQuestionListCmd()
789  {
790  $questionId = $this->testSequence->getQuestionForSequence($_GET["sequence"]);
791 
792  if( !$this->isParticipantsAnswerFixed($questionId) )
793  {
794  $this->saveQuestionSolution();
795  }
796 
797  if ($this->saveResult == FALSE)
798  {
799  $this->ctrl->setParameter($this, "activecommand", "");
800  $this->ctrl->redirect($this, "redirectQuestion");
801  }
802  else
803  {
804  $this->ctrl->setParameter($this, "activecommand", "summary");
805  $this->ctrl->redirect($this, "redirectQuestion");
806  }
807  }
808 
809  protected function showQuestionListWithoutSavingCmd()
810  {
811  $this->ctrl->setParameter($this, "activecommand", "summary");
812  $this->ctrl->redirect($this, "redirectQuestion");
813  }
814 
820  protected function postponeQuestionCmd()
821  {
822  $questionId = $this->testSequence->getQuestionForSequence($_GET["sequence"]);
823 
824  if( !$this->isParticipantsAnswerFixed($questionId) )
825  {
826  $this->saveQuestionSolution();
827  }
828 
829  $this->ctrl->setParameter($this, "activecommand", "postpone");
830  $this->ctrl->redirect($this, "redirectQuestion");
831  }
832 
833  protected function handleQuestionActionCmd()
834  {
835  $questionId = $this->testSequence->getQuestionForSequence($_GET["sequence"]);
836 
837  if( !$this->isParticipantsAnswerFixed($questionId) )
838  {
839  $this->updateWorkingTime();
840  $this->saveQuestionSolution();
841  }
842 
843  $this->ctrl->setParameter($this, 'activecommand', 'handleQuestionAction');
844  $this->ctrl->redirect($this, 'redirectQuestion');
845  }
846 
848  {
849  if ($this->testSession->getActiveId() > 0)
850  {
851  if ($this->testSequence->hasRandomQuestionsForPass($this->testSession->getActiveId(), $this->testSession->getPass()) > 0)
852  {
853  // Something went wrong. Maybe the user pressed the start button twice
854  // Questions already exist so there is no need to create new questions
855 
856  global $ilLog, $ilUser;
857 
858  $ilLog->write(
859  __METHOD__.' Random Questions allready exists for user '.
860  $ilUser->getId().' in test '.$this->object->getTestId()
861  );
862 
863  return true;
864  }
865  }
866  else
867  {
868  // This may not happen! If it happens, raise a fatal error...
869 
870  global $ilLog, $ilUser;
871 
872  $ilLog->write(__METHOD__.' '.sprintf(
873  $this->lng->txt("error_random_question_generation"), $ilUser->getId(), $this->object->getTestId()
874  ));
875 
876  return true;
877  };
878 
879  return false;
880  }
881 
883  {
884  global $tree, $ilDB, $ilPluginAdmin;
885 
886  require_once 'Modules/Test/classes/class.ilTestRandomQuestionSetConfig.php';
887  $questionSetConfig = new ilTestRandomQuestionSetConfig($tree, $ilDB, $ilPluginAdmin, $this->object);
888  $questionSetConfig->loadFromDb();
889 
890  require_once 'Modules/Test/classes/class.ilTestRandomQuestionSetSourcePoolDefinitionFactory.php';
891  $sourcePoolDefinitionFactory = new ilTestRandomQuestionSetSourcePoolDefinitionFactory($ilDB, $this->object);
892 
893  require_once 'Modules/Test/classes/class.ilTestRandomQuestionSetSourcePoolDefinitionList.php';
894  $sourcePoolDefinitionList = new ilTestRandomQuestionSetSourcePoolDefinitionList($ilDB, $this->object, $sourcePoolDefinitionFactory);
895  $sourcePoolDefinitionList->loadDefinitions();
896 
897  $this->processLocker->requestRandomPassBuildLock($sourcePoolDefinitionList->hasTaxonomyFilters());
898 
900  {
901  require_once 'Modules/Test/classes/class.ilTestRandomQuestionSetStagingPoolQuestionList.php';
902  $stagingPoolQuestionList = new ilTestRandomQuestionSetStagingPoolQuestionList($ilDB, $ilPluginAdmin);
903 
904  require_once 'Modules/Test/classes/class.ilTestRandomQuestionSetBuilder.php';
905  $questionSetBuilder = ilTestRandomQuestionSetBuilder::getInstance($ilDB, $this->object, $questionSetConfig, $sourcePoolDefinitionList, $stagingPoolQuestionList);
906 
907  $questionSetBuilder->performBuild($this->testSession);
908  }
909 
910  $this->processLocker->releaseRandomPassBuildLock();
911  }
912 
913  protected function getObjectiveOrientedContainerId()
914  {
915  require_once 'Modules/Course/classes/Objectives/class.ilLOSettings.php';
916 
917  return (int)ilLOSettings::isObjectiveTest($this->testSession->getRefId());
918  }
919 
921  {
922  require_once 'Modules/Course/classes/Objectives/class.ilLOTestQuestionAdapter.php';
923 
924  ilLOTestQuestionAdapter::filterQuestions($testSession, $testSequence);
925  }
926 
928  {
929  require_once 'Modules/Course/classes/Objectives/class.ilLOTestQuestionAdapter.php';
930 
931  ilLOTestQuestionAdapter::updateObjectiveStatus($testSession, $testSequence, $question);
932 
933  $testSequence->saveToDb();
934  }
935 
936  protected function customRedirectRequired()
937  {
938  return $this->testSession->isObjectiveOriented();
939  }
940 
941  protected function performCustomRedirect()
942  {
943  $containerRefId = current(ilObject::_getAllReferences($this->testSession->getObjectiveOrientedContainerId()));
944 
945  require_once 'Services/Link/classes/class.ilLink.php';
946  $redirectTarget = ilLink::_getLink($containerRefId);
947 
948  ilUtil::redirect($redirectTarget);
949  }
950 }