ILIAS  Release_4_4_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilTestPlayerDynamicQuestionSetGUI.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 
27 {
28  const CMD_SHOW_QUESTION_SELECTION = 'showQuestionSelection';
29  const CMD_SHOW_QUESTION = 'showQuestion';
30  const CMD_FROM_PASS_DELETION = 'fromPassDeletion';
31 
35  private $dynamicQuestionSetConfig = null;
36 
40  protected $testSequence;
41 
45  protected $testSession;
46 
50  function executeCommand()
51  {
52  global $ilDB, $lng, $ilPluginAdmin, $ilTabs, $tree;
53 
54  $ilTabs->clearTargets();
55 
56  $this->ctrl->saveParameter($this, "sequence");
57  $this->ctrl->saveParameter($this, "active_id");
58 
59  require_once 'Modules/Test/classes/class.ilObjTestDynamicQuestionSetConfig.php';
60  $this->dynamicQuestionSetConfig = new ilObjTestDynamicQuestionSetConfig($tree, $ilDB, $ilPluginAdmin, $this->object);
61  $this->dynamicQuestionSetConfig->loadFromDb();
62 
63  $testSessionFactory = new ilTestSessionFactory($this->object);
64  $this->testSession = $testSessionFactory->getSession($_GET['active_id']);
65 
66  $this->ensureExistingTestSession($this->testSession);
67  $this->initProcessLocker($this->testSession->getActiveId());
68 
69  $testSequenceFactory = new ilTestSequenceFactory($ilDB, $lng, $ilPluginAdmin, $this->object);
70  $this->testSequence = $testSequenceFactory->getSequence($this->testSession);
71  $this->testSequence->loadFromDb();
72 
73  include_once 'Services/jQuery/classes/class.iljQueryUtil.php';
75  include_once "./Services/YUI/classes/class.ilYuiUtil.php";
77  if( $this->object->getKioskMode() )
78  {
79  include_once 'Services/UIComponent/Overlay/classes/class.ilOverlayGUI.php';
81  }
82 
83  $cmd = $this->ctrl->getCmd();
84  $nextClass = $this->ctrl->getNextClass($this);
85 
86  switch($nextClass)
87  {
88  case 'ilassquestionpagegui':
89 
90  $questionId = $this->testSequence->getQuestionForSequence( $this->calculateSequence() );
91 
92  require_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPageGUI.php";
93  $page_gui = new ilAssQuestionPageGUI($questionId);
94  $ret = $this->ctrl->forwardCommand($page_gui);
95  break;
96 
97  case 'ilassquestionhintrequestgui':
98 
99  $questionGUI = $this->object->createQuestionGUI(
100  "", $this->testSequenceFactory->getSequence()->getQuestionForSequence( $this->calculateSequence() )
101  );
102 
103  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintRequestGUI.php';
104  $gui = new ilAssQuestionHintRequestGUI($this, self::CMD_SHOW_QUESTION, $this->testSession, $questionGUI);
105 
106  $this->ctrl->forwardCommand($gui);
107 
108  break;
109 
110  case 'ildynamicquestionsetstatistictablegui':
111 
112  $this->ctrl->forwardCommand( $this->buildQuestionSetFilteredStatisticTableGUI() );
113 
114  break;
115 
116  default:
117 
118  $cmd .= 'Cmd';
119  $ret =& $this->$cmd();
120  break;
121  }
122 
123  return $ret;
124  }
125 
129  protected function resumePlayerCmd()
130  {
131  if ($this->object->checkMaximumAllowedUsers() == FALSE)
132  {
134  }
135 
136  $this->handleUserSettings();
137 
138  if( $this->dynamicQuestionSetConfig->isTaxonomyFilterEnabled() )
139  {
140  $this->ctrl->redirect($this, self::CMD_SHOW_QUESTION_SELECTION);
141  }
142 
143  $this->ctrl->redirect($this, self::CMD_SHOW_QUESTION);
144  }
145 
146  protected function startTestCmd()
147  {
148  global $ilUser;
149 
150  $this->testSession->setCurrentQuestionId(null); // no question "came up" yet
151 
152  $this->testSession->saveToDb();
153 
154  $this->ctrl->setParameter($this, 'active_id', $this->testSession->getActiveId());
155 
156  assQuestion::_updateTestPassResults($this->testSession->getActiveId(), $this->testSession->getPass());
157 
158  $_SESSION['active_time_id'] = $this->object->startWorkingTime(
159  $this->testSession->getActiveId(), $this->testSession->getPass()
160  );
161 
162  $this->ctrl->saveParameter($this, 'tst_javascript');
163 
164  if( $this->dynamicQuestionSetConfig->isTaxonomyFilterEnabled() )
165  {
166  $this->ctrl->redirect($this, self::CMD_SHOW_QUESTION_SELECTION);
167  }
168 
169  $this->ctrl->redirect($this, self::CMD_SHOW_QUESTION);
170  }
171 
172  protected function showQuestionSelectionCmd()
173  {
174  $this->prepareSummaryPage();
175 
176  if( $this->testSession->getCurrentQuestionId() )
177  {
178  $this->updateWorkingTime();
179  $this->saveQuestionSolution();
181 
182  $this->testSequence->unsetQuestionPostponed($questionId);
183  }
184 
185  $this->testSequence->loadQuestions(
186  $this->dynamicQuestionSetConfig, $this->testSession->getQuestionSetFilterSelection()
187  );
188 
189  $this->testSequence->cleanupQuestions($this->testSession);
190 
191  $this->testSequence->saveToDb();
192 
193  require_once 'Services/UIComponent/Toolbar/classes/class.ilToolbarGUI.php';
194  $toolbarGUI = new ilToolbarGUI();
195 
196  $toolbarGUI->addButton(
197  $this->getEnterTestButtonLangVar(), $this->ctrl->getLinkTarget($this, self::CMD_SHOW_QUESTION),
198  '', '', '', '', 'submit emphsubmit'
199  );
200 
201  if( $this->object->isPassDeletionAllowed() )
202  {
203  require_once 'Modules/Test/classes/confirmations/class.ilTestPassDeletionConfirmationGUI.php';
204 
205  $toolbarGUI->addButton(
206  $this->lng->txt('tst_dyn_test_pass_deletion_button'),
208  );
209  }
210 
211  $filteredData = array($this->buildQuestionSetAnswerStatisticRowArray(
212  $this->testSequence->getFilteredQuestionsData(), $this->getMarkedQuestions()
213  )); #vd($filteredData);
214  $filteredTableGUI = $this->buildQuestionSetFilteredStatisticTableGUI();
215  $filteredTableGUI->setData($filteredData);
216 
217  $completeData = array($this->buildQuestionSetAnswerStatisticRowArray(
218  $this->testSequence->getCompleteQuestionsData(), $this->getMarkedQuestions()
219  )); #vd($completeData);
220  $completeTableGUI = $this->buildQuestionSetCompleteStatisticTableGUI();
221  $completeTableGUI->setData($completeData);
222 
223  $content = $this->ctrl->getHTML($toolbarGUI);
224  $content .= $this->ctrl->getHTML($filteredTableGUI);
225  $content .= $this->ctrl->getHTML($completeTableGUI);
226 
227  $this->tpl->setVariable('TABLE_LIST_OF_QUESTIONS', $content);
228 
229  if( $this->object->getEnableProcessingTime() )
230  {
231  $this->outProcessingTime($this->testSession->getActiveId());
232  }
233  }
234 
235  protected function filterQuestionSelectionCmd()
236  {
237  $tableGUI = $this->buildQuestionSetFilteredStatisticTableGUI();
238  $tableGUI->writeFilterToSession();
239 
240  $taxFilterSelection = array();
241  $answerStatusFilterSelection = ilAssQuestionList::ANSWER_STATUS_FILTER_ALL_NON_CORRECT;
242 
243  foreach( $tableGUI->getFilterItems() as $item )
244  {
245  if( strpos($item->getPostVar(), 'tax_') !== false )
246  {
247  $taxId = substr( $item->getPostVar(), strlen('tax_') );
248  $taxFilterSelection[$taxId] = $item->getValue();
249  }
250  elseif( $item->getPostVar() == 'question_answer_status' )
251  {
252  $answerStatusFilterSelection = $item->getValue();
253  }
254  }
255 
256  $this->testSession->getQuestionSetFilterSelection()->setTaxonomySelection($taxFilterSelection);
257  $this->testSession->getQuestionSetFilterSelection()->setAnswerStatusSelection($answerStatusFilterSelection);
258  $this->testSession->saveToDb();
259 
260  $this->testSequence->resetTrackedQuestionList();
261  $this->testSequence->saveToDb();
262 
263  $this->ctrl->redirect($this, 'showQuestionSelection');
264  }
265 
266  protected function resetQuestionSelectionCmd()
267  {
268  $tableGUI = $this->buildQuestionSetFilteredStatisticTableGUI();
269  $tableGUI->resetFilter();
270 
271  $this->testSession->getQuestionSetFilterSelection()->setTaxonomySelection( array() );
272  $this->testSession->getQuestionSetFilterSelection()->setAnswerStatusSelection( null );
273  $this->testSession->saveToDb();
274 
275  $this->testSequence->resetTrackedQuestionList();
276  $this->testSequence->saveToDb();
277 
278  $this->ctrl->redirect($this, 'showQuestionSelection');
279  }
280 
281  protected function showTrackedQuestionListCmd()
282  {
283  if( !$this->dynamicQuestionSetConfig->isPreviousQuestionsListEnabled() )
284  {
285  $this->ctrl->redirect($this, self::CMD_SHOW_QUESTION);
286  }
287 
288  $this->prepareSummaryPage();
289 
290  $questionId = $this->testSession->getCurrentQuestionId();
291 
292  if( $questionId )
293  {
294  $this->updateWorkingTime();
295  $this->saveQuestionSolution();
297 
298  $this->testSequence->unsetQuestionPostponed($questionId);
299  }
300 
301  $this->testSequence->loadQuestions(
302  $this->dynamicQuestionSetConfig, $this->testSession->getQuestionSetFilterSelection()
303  );
304 
305  $this->testSequence->cleanupQuestions($this->testSession);
306 
307  $this->testSequence->saveToDb();
308 
309  $data = $this->buildQuestionsTableDataArray(
310  $this->testSequence->getTrackedQuestionList( $this->testSession->getCurrentQuestionId() ),
311  $this->getMarkedQuestions()
312  );
313 
314  include_once "./Modules/Test/classes/tables/class.ilTrackedQuestionsTableGUI.php";
315  $table_gui = new ilTrackedQuestionsTableGUI(
316  $this, 'showTrackedQuestionList', $this->object->getSequenceSettings(), $this->object->getShowMarker()
317  );
318 
319  $table_gui->setData($data);
320 
321  $this->tpl->setVariable('TABLE_LIST_OF_QUESTIONS', $table_gui->getHTML());
322 
323  if( $this->object->getEnableProcessingTime() )
324  {
325  $this->outProcessingTime($this->testSession->getActiveId());
326  }
327  }
328 
329  protected function previousQuestionCmd()
330  {
331 
332  }
333 
334  protected function fromPassDeletionCmd()
335  {
336  $this->resetCurrentQuestion();
337  $this->ctrl->redirect($this, 'showQuestion');
338  }
339 
340  protected function nextQuestionCmd()
341  {
342  $questionId = $this->testSession->getCurrentQuestionId();
343 
344  if( $questionId )
345  {
346  $this->updateWorkingTime();
347  $this->saveQuestionSolution();
349 
350  $this->testSequence->unsetQuestionPostponed($questionId);
351  $this->testSequence->saveToDb();
352  }
353 
354  $this->resetCurrentQuestion();
355 
356  $this->ctrl->redirect($this, 'showQuestion');
357  }
358 
359  protected function postponeQuestionCmd()
360  {
361  $questionId = $this->testSession->getCurrentQuestionId();
362 
363  if( $questionId )
364  {
365  $this->updateWorkingTime();
366  $this->saveQuestionSolution();
368 
369  $this->testSequence->setQuestionPostponed($questionId);
370  }
371 
372  $this->testSession->setCurrentQuestionId(null);
373 
374  $this->testSequence->saveToDb();
375  $this->testSession->saveToDb();
376 
377  $this->ctrl->redirect($this, 'showQuestion');
378  }
379 
380  protected function markQuestionCmd()
381  {
382  $questionId = $this->testSession->getCurrentQuestionId();
383 
384  if( $questionId )
385  {
386  $this->updateWorkingTime();
387  $this->saveQuestionSolution();
389 
390  $this->testSequence->unsetQuestionPostponed($questionId);
391  $this->testSequence->saveToDb();
392  }
393 
394  global $ilUser;
395  $this->object->setQuestionSetSolved(1, $this->testSession->getCurrentQuestionId(), $ilUser->getId());
396 
397  $this->ctrl->redirect($this, 'showQuestion');
398  }
399 
400  protected function unmarkQuestionCmd()
401  {
402  $questionId = $this->testSession->getCurrentQuestionId();
403 
404  if( $questionId )
405  {
406  $this->updateWorkingTime();
407  $this->saveQuestionSolution();
409 
410  $this->testSequence->unsetQuestionPostponed($questionId);
411  $this->testSequence->saveToDb();
412  }
413 
414  global $ilUser;
415  $this->object->setQuestionSetSolved(0, $this->testSession->getCurrentQuestionId(), $ilUser->getId());
416 
417  $this->ctrl->redirect($this, 'showQuestion');
418  }
419 
420  protected function gotoQuestionCmd()
421  {
422  $this->testSequence->loadQuestions(
423  $this->dynamicQuestionSetConfig, $this->testSession->getQuestionSetFilterSelection()
424  );
425 
426  $this->testSequence->cleanupQuestions($this->testSession);
427 
428  if( isset($_GET['sequence']) && (int)$_GET['sequence'] )
429  {
430  $this->testSession->setCurrentQuestionId( (int)$_GET['sequence'] );
431  $this->testSession->saveToDb();
432 
433  $this->ctrl->setParameter(
434  $this, 'sequence', $this->testSession->getCurrentQuestionId()
435  );
436  }
437 
438  $this->ctrl->redirect($this, 'showQuestion');
439  }
440 
441  protected function showQuestionCmd()
442  {
444 
445  $this->testSequence->loadQuestions(
446  $this->dynamicQuestionSetConfig, $this->testSession->getQuestionSetFilterSelection()
447  );
448 
449  $this->testSequence->cleanupQuestions($this->testSession);
450 
451  if( !$this->testSession->getCurrentQuestionId() )
452  {
453  $this->testSession->setCurrentQuestionId(
454  $this->testSequence->getUpcomingQuestionId()
455  );
456  }
457 
458  if( $this->testSession->getCurrentQuestionId() )
459  {
460  $this->ctrl->setParameter(
461  $this, 'sequence', $this->testSession->getCurrentQuestionId()
462  );
463 
464  $this->outTestPage(false);
465  }
466  else
467  {
468  $this->outCurrentlyFinishedPage();
469  }
470 
471  $this->testSequence->saveToDb();
472  $this->testSession->saveToDb();
473  }
474 
475  protected function showInstantResponseCmd()
476  {
477  $questionId = $this->testSession->getCurrentQuestionId();
478 
479  if( $questionId )
480  {
481  $this->updateWorkingTime();
482  $this->saveQuestionSolution();
484 
485  $this->testSequence->unsetQuestionPostponed($questionId);
486  }
487 
489 
490  $filterSelection = $this->testSession->getQuestionSetFilterSelection();
491 
492  $filterSelection->setForcedQuestionIds(array($this->testSession->getCurrentQuestionId()));
493 
494  $this->testSequence->loadQuestions($this->dynamicQuestionSetConfig, $filterSelection);
495 
496  $this->testSequence->cleanupQuestions($this->testSession);
497 
498  $this->ctrl->setParameter(
499  $this, 'sequence', $this->testSession->getCurrentQuestionId()
500  );
501 
502  $this->outTestPage(true);
503 
504  $this->testSequence->saveToDb();
505  $this->testSession->saveToDb();
506  }
507 
508  protected function handleQuestionActionCmd()
509  {
510  $questionId = $this->testSession->getCurrentQuestionId();
511 
512  if( $questionId )
513  {
514  $this->updateWorkingTime();
515  $this->saveQuestionSolution();
517 
518  $this->testSequence->unsetQuestionPostponed($questionId);
519  $this->testSequence->saveToDb();
520  }
521 
522  $this->ctrl->setParameter(
523  $this, 'sequence', $this->testSession->getCurrentQuestionId()
524  );
525 
526  $this->ctrl->redirect($this, 'showQuestion');
527  }
528 
532  protected function outWorkingForm($sequence = "", $test_id, $postpone_allowed, $directfeedback = false)
533  {
534  global $ilUser;
535 
536  $_SESSION["active_time_id"] = $this->object->startWorkingTime(
537  $this->testSession->getActiveId(), $this->testSession->getPass()
538  );
539 
540  $this->populateContentStyleBlock();
541  $this->populateSyntaxStyleBlock();
542 
543  $question_gui = $this->object->createQuestionGUI(
544  "", $this->testSession->getCurrentQuestionId()
545  );
546 
547  if( !is_object($question_gui) )
548  {
549  global $ilLog;
550 
551  $ilLog->write(
552  "INV SEQ: active={$this->testSession->getActiveId()} qId={$this->testSession->getCurrentQuestionId()} "
553  .serialize($this->testSequence)
554  );
555 
556  $ilLog->logStack('INV SEQ');
557 
558  $this->resetCurrentQuestion();
559  $this->ctrl->redirect($this, 'showQuestion');
560  }
561 
562  $question_gui->setTargetGui($this);
563 
564  $question_gui->setQuestionCount(
565  $this->testSequence->getLastPositionIndex()
566  );
567  $question_gui->setSequenceNumber( $this->testSequence->getCurrentPositionIndex(
568  $this->testSession->getCurrentQuestionId()
569  ));
570 
571  $this->ctrl->setParameter($this, 'sequence', $this->testSession->getCurrentQuestionId());
572 
573  if ($this->object->getJavaScriptOutput())
574  {
575  $question_gui->object->setOutputType(OUTPUT_JAVASCRIPT);
576  }
577 
578  $is_postponed = $this->testSequence->isPostponedQuestion($question_gui->object->getId());
579  $formaction = $this->ctrl->getFormAction($this);
580 
581  // output question
582  $user_post_solution = FALSE;
583  if( isset($_SESSION['previouspost']) )
584  {
585  $user_post_solution = $_SESSION['previouspost'];
586  unset($_SESSION['previouspost']);
587  }
588 
589  global $ilNavigationHistory;
590  $ilNavigationHistory->addItem($_GET["ref_id"], $this->ctrl->getLinkTarget($this, "resumePlayer"), "tst");
591 
592  // Determine $answer_feedback: It should hold a boolean stating if answer-specific-feedback is to be given.
593  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Answer-Specific Feedback"
594  // $directfeedback holds a boolean stating if the instant feedback was requested using the "Check" button.
595  $answer_feedback = FALSE;
596  if (($directfeedback) && ($this->object->getSpecificAnswerFeedback()))
597  {
598  $answer_feedback = TRUE;
599  }
600 
601  // Answer specific feedback is rendered into the display of the test question with in the concrete question types outQuestionForTest-method.
602  // Notation of the params prior to getting rid of this crap in favor of a class
603  $question_gui->outQuestionForTest(
604  $formaction, #form_action
605  $this->testSession->getActiveId(), #active_id
606  NULL, #pass
607  $is_postponed, #is_postponed
608  $user_post_solution, #user_post_solution
609  $answer_feedback #answer_feedback == inline_specific_feedback
610  );
611  // The display of specific inline feedback and specific feedback in an own block is to honor questions, which
612  // have the possibility to embed the specific feedback into their output while maintaining compatibility to
613  // questions, which do not have such facilities. E.g. there can be no "specific inline feedback" for essay
614  // questions, while the multiple-choice questions do well.
615 
616  $this->fillQuestionRelatedNavigation($question_gui);
617 
618  if ($directfeedback)
619  {
620  // This controls if the solution should be shown.
621  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Solutions"
622  if ($this->object->getInstantFeedbackSolution())
623  {
624  $show_question_inline_score = $this->determineInlineScoreDisplay();
625 
626  // Notation of the params prior to getting rid of this crap in favor of a class
627  $solutionoutput = $question_gui->getSolutionOutput(
628  $this->testSession->getActiveId(), #active_id
629  NULL, #pass
630  TRUE, #graphical_output
631  $show_question_inline_score, #result_output
632  FALSE, #show_question_only
633  FALSE, #show_feedback
634  TRUE, #show_correct_solution
635  FALSE, #show_manual_scoring
636  FALSE #show_question_text
637  );
638  $this->populateSolutionBlock( $solutionoutput );
639  }
640 
641  // This controls if the score should be shown.
642  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Results (Only Points)"
643  if ($this->object->getAnswerFeedbackPoints())
644  {
645  $reachedPoints = $question_gui->object->getAdjustedReachedPoints($this->testSession->getActiveId(), NULL);
646  $maxPoints = $question_gui->object->getMaximumPoints();
647 
648  $this->populateScoreBlock( $reachedPoints, $maxPoints );
649  }
650 
651  // This controls if the generic feedback should be shown.
652  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Solutions"
653  if ($this->object->getGenericAnswerFeedback())
654  {
655  $this->populateGenericFeedbackBlock( $question_gui );
656  }
657 
658  // This controls if the specific feedback should be shown.
659  // It gets the parameter "Scoring and Results" -> "Instant Feedback" -> "Show Answer-Specific Feedback"
660  if ($this->object->getSpecificAnswerFeedback())
661  {
662  $this->populateSpecificFeedbackBlock( $question_gui );
663  }
664  }
665 
666  $this->populatePreviousButtons( $this->testSession->getCurrentQuestionId() );
667 
668  if( $postpone_allowed )
669  {
670  $this->populatePostponeButtons();
671  }
672 
673  if ($this->object->getShowCancel())
674  {
675  $this->populateCancelButtonBlock();
676  }
677 
678  if ($this->isLastQuestionInSequence( $question_gui ))
679  {
680  if ($this->object->getListOfQuestionsEnd())
681  {
683  }
684  else
685  {
687  }
688  }
689  else
690  {
692  }
693 
694  if( $this->dynamicQuestionSetConfig->isTaxonomyFilterEnabled() )
695  {
697  }
698 
699  if ($this->object->getShowMarker())
700  {
701  include_once "./Modules/Test/classes/class.ilObjTest.php";
702  $solved_array = ilObjTest::_getSolvedQuestions($this->testSession->getActiveId(), $question_gui->object->getId());
703  $solved = 0;
704 
705  if (count ($solved_array) > 0)
706  {
707  $solved = array_pop($solved_array);
708  $solved = $solved["solved"];
709  }
710 
711  if ($solved==1)
712  {
714  }
715  else
716  {
718  }
719  }
720 
721  $this->populateCharSelector();
722 
723  if ($this->object->getJavaScriptOutput())
724  {
725  $this->tpl->setVariable("JAVASCRIPT_IMAGE", ilUtil::getImagePath("javascript_disable.png"));
726  $this->tpl->setVariable("JAVASCRIPT_IMAGE_ALT", $this->lng->txt("disable_javascript"));
727  $this->tpl->setVariable("JAVASCRIPT_IMAGE_TITLE", $this->lng->txt("disable_javascript"));
728  $this->ctrl->setParameter($this, "tst_javascript", "0");
729  $this->tpl->setVariable("JAVASCRIPT_URL", $this->ctrl->getLinkTarget($this, "gotoQuestion"));
730  }
731  else
732  {
733  $this->tpl->setVariable("JAVASCRIPT_IMAGE", ilUtil::getImagePath("javascript.png"));
734  $this->tpl->setVariable("JAVASCRIPT_IMAGE_ALT", $this->lng->txt("enable_javascript"));
735  $this->tpl->setVariable("JAVASCRIPT_IMAGE_TITLE", $this->lng->txt("enable_javascript"));
736  $this->ctrl->setParameter($this, "tst_javascript", "1");
737  $this->tpl->setVariable("JAVASCRIPT_URL", $this->ctrl->getLinkTarget($this, "gotoQuestion"));
738  }
739 
740  if ($question_gui->object->supportsJavascriptOutput())
741  {
742  $this->tpl->touchBlock("jsswitch");
743  }
744 
745  $this->tpl->addJavaScript(ilUtil::getJSLocation("autosave.js", "Modules/Test"));
746 
747  $this->tpl->setVariable("AUTOSAVE_URL", $this->ctrl->getFormAction($this, "autosave", "", true));
748 
749  if ($question_gui->isAutosaveable()&& $this->object->getAutosave())
750  {
751  $this->tpl->touchBlock('autosave');
752  //$this->tpl->setVariable("BTN_SAVE", "Zwischenspeichern");
753  //$this->tpl->setVariable("CMD_SAVE", "gotoquestion_{$sequence}");
754  //$this->tpl->setVariable("AUTOSAVEFORMACTION", str_replace("&amp;", "&", $this->ctrl->getFormAction($this)));
755  $this->tpl->setVariable("AUTOSAVEFORMACTION", str_replace("&amp;", "&", $this->ctrl->getLinkTarget($this, "autosave")));
756  $this->tpl->setVariable("AUTOSAVEINTERVAL", $this->object->getAutosaveIval());
757  }
758 
759  if( $this->object->areObligationsEnabled() && ilObjTest::isQuestionObligatory($question_gui->object->getId()) )
760  {
761  $this->tpl->touchBlock('question_obligatory');
762  $this->tpl->setVariable('QUESTION_OBLIGATORY', $this->lng->txt('required_field'));
763  }
764  }
765 
766  private function outCurrentlyFinishedPage()
767  {
768  $this->prepareTestPageOutput();
769 
770  $this->populatePreviousButtons( $this->testSession->getCurrentQuestionId() );
771 
772  if ($this->object->getKioskMode())
773  {
774  $this->populateKioskHead();
775  }
776 
777  if ($this->object->getEnableProcessingTime())
778  {
779  $this->outProcessingTime($this->testSession->getActiveId());
780  }
781 
782  $this->tpl->setVariable("FORMACTION", $this->ctrl->getFormAction($this));
783  $this->tpl->setVariable("FORM_TIMESTAMP", time());
784 
785  $this->tpl->setVariable("PAGETITLE", "- " . $this->object->getTitle());
786 
787  if ($this->object->isShowExamIdInTestPassEnabled() && !$this->object->getKioskMode())
788  {
789  $this->tpl->setCurrentBlock('exam_id');
790  $this->tpl->setVariable('EXAM_ID', ilObjTest::lookupExamId(
791  $this->testSession->getActiveId(), $this->testSession->getPass(), $this->object->getId()
792  ));
793  $this->tpl->setVariable('EXAM_ID_TXT', $this->lng->txt('exam_id'));
794  $this->tpl->parseCurrentBlock();
795  }
796 
797  if ($this->object->getShowCancel())
798  {
799  $this->populateCancelButtonBlock();
800  }
801 
802  if( $this->dynamicQuestionSetConfig->isTaxonomyFilterEnabled() )
803  {
805  }
806 
807  if( $this->testSequence->openQuestionExists() )
808  {
809  $message = $this->lng->txt('tst_dyn_test_msg_currently_finished_selection');
810  }
811  else
812  {
813  $message = $this->lng->txt('tst_dyn_test_msg_currently_finished_completely');
814  $message .= "<br /><br />{$this->buildFinishPagePassDeletionLink()}";
815  }
816 
817  $msgHtml = $this->tpl->getMessageHTML($message);
818 
819  $this->tpl->addBlockFile(
820  'QUESTION_OUTPUT', 'test_currently_finished_msg_block',
821  'tpl.test_currently_finished_msg.html', 'Modules/Test'
822  );
823 
824  $this->tpl->setCurrentBlock('test_currently_finished_msg_block');
825  $this->tpl->setVariable('TEST_CURRENTLY_FINISHED_MSG', $msgHtml);
826  $this->tpl->parseCurrentBlock();
827 
828  }
829 
830  protected function isFirstPageInSequence($sequence)
831  {
832  return !$this->testSequence->trackedQuestionExists();
833  }
834 
835  protected function isLastQuestionInSequence(assQuestionGUI $questionGUI)
836  {
837  return false; // always
838  }
839 
840  protected function handleJavascriptActivationStatus()
841  {
842  global $ilUser;
843 
844  if( isset($_GET['tst_javascript']) )
845  {
846  $ilUser->writePref('tst_javascript', $_GET['tst_javascript']);
847  }
848  }
849 
855  protected function canSaveResult()
856  {
857  return !$this->object->endingTimeReached();
858  }
859 
863  public function saveQuestionSolution($force = FALSE)
864  {
865  // what is this formtimestamp ??
866  if (!$force)
867  {
868  $formtimestamp = $_POST["formtimestamp"];
869  if (strlen($formtimestamp) == 0) $formtimestamp = $_GET["formtimestamp"];
870  if ($formtimestamp != $_SESSION["formtimestamp"])
871  {
872  $_SESSION["formtimestamp"] = $formtimestamp;
873  }
874  else
875  {
876  return FALSE;
877  }
878  }
879 
880  // determine current question
881 
882  $qId = $this->testSession->getCurrentQuestionId();
883 
884  if( !$qId || $qId != $_GET["sequence"])
885  {
886  return false;
887  }
888 
889  // save question solution
890 
891  $this->saveResult = FALSE;
892 
893  if ($this->canSaveResult() || $force)
894  {
895  global $ilUser;
896 
897  $questionGUI = $this->object->createQuestionGUI("", $qId);
898 
899  if( $this->object->getJavaScriptOutput() )
900  {
901  $questionGUI->object->setOutputType(OUTPUT_JAVASCRIPT);
902  }
903 
904  $activeId = $this->testSession->getActiveId();
905 
906  $this->saveResult = $questionGUI->object->persistWorkingState(
907  $activeId, $pass = null, $this->object->areObligationsEnabled()
908  );
909  }
910 
911  if ($this->saveResult == FALSE)
912  {
913  $this->ctrl->setParameter($this, "save_error", "1");
914  $_SESSION["previouspost"] = $_POST;
915  }
916 
917  return $this->saveResult;
918  }
919 
920  private function isQuestionAnsweredCorrect($questionId, $activeId, $pass)
921  {
922  $questionGUI = $this->object->createQuestionGUI("", $questionId);
923 
924  $reachedPoints = assQuestion::_getReachedPoints($activeId, $questionId, $pass);
925  $maxPoints = $questionGUI->object->getMaximumPoints();
926 
927  if($reachedPoints < $maxPoints)
928  {
929  return false;
930  }
931 
932  return true;
933  }
934 
935 
936  protected function populatePreviousButtons($sequence)
937  {
938  if( !$this->dynamicQuestionSetConfig->isPreviousQuestionsListEnabled() )
939  {
940  return;
941  }
942 
943  if( $this->isFirstPageInSequence($sequence) )
944  {
945  return;
946  }
947 
949  'showTrackedQuestionList', "&lt;&lt; " . $this->lng->txt( "save_previous" )
950  );
952  'showTrackedQuestionList', "&lt;&lt; " . $this->lng->txt( "save_previous" )
953  );
954  }
955 
956  protected function buildQuestionsTableDataArray($questions, $marked_questions)
957  {
958  $data = array();
959 
960  foreach($questions as $key => $value )
961  {
962  $this->ctrl->setParameter($this, 'sequence', $value['question_id']);
963  $href = $this->ctrl->getLinkTarget($this, 'gotoQuestion');
964  $this->ctrl->setParameter($this, 'sequence', '');
965 
966  $description = "";
967  if( $this->object->getListOfQuestionsDescription() )
968  {
969  $description = $value["description"];
970  }
971 
972  $marked = false;
973  if( count($marked_questions) )
974  {
975  if( isset($marked_questions[$value["question_id"]]) )
976  {
977  if( $marked_questions[$value["question_id"]]["solved"] == 1 )
978  {
979  $marked = true;
980  }
981  }
982  }
983 
984  array_push($data, array(
985  'href' => $href,
986  'title' => $this->object->getQuestionTitle($value["title"]),
987  'description' => $description,
988  'worked_through' => $this->testSequence->isAnsweredQuestion($value["question_id"]),
989  'postponed' => $this->testSequence->isPostponedQuestion($value["question_id"]),
990  'marked' => $marked
991  ));
992  }
993 
994  return $data;
995  }
996 
997  protected function buildQuestionSetAnswerStatisticRowArray($questions, $marked_questions)
998  {
999  $questionAnswerStats = array(
1000  'total_all' => count($questions),
1001  'total_open' => 0,
1002  'non_answered' => 0,
1003  'wrong_answered' => 0,
1004  'correct_answered' => 0,
1005  'postponed' => 0,
1006  'marked' => 0
1007  );
1008 
1009  foreach($questions as $key => $value )
1010  {
1011  switch( $value['question_answer_status'] )
1012  {
1014  $questionAnswerStats['non_answered']++;
1015  $questionAnswerStats['total_open']++;
1016  break;
1018  $questionAnswerStats['wrong_answered']++;
1019  $questionAnswerStats['total_open']++;
1020  break;
1022  $questionAnswerStats['correct_answered']++;
1023  break;
1024  }
1025 
1026  if( $this->testSequence->isPostponedQuestion($value["question_id"]) )
1027  {
1028  $questionAnswerStats['postponed']++;
1029  }
1030 
1031  if( isset($marked_questions[$value["question_id"]]) )
1032  {
1033  if( $marked_questions[$value["question_id"]]["solved"] == 1 )
1034  {
1035  $questionAnswerStats['marked']++;
1036  }
1037  }
1038  }
1039 
1040  return $questionAnswerStats;
1041  }
1042 
1044  {
1045  require_once 'Modules/Test/classes/tables/class.ilTestDynamicQuestionSetStatisticTableGUI.php';
1046  $gui = $this->buildQuestionSetStatisticTableGUI(
1048  );
1049 
1050  $gui->initTitle('tst_dynamic_question_set_complete');
1051  $gui->initColumns('tst_num_all_questions');
1052 
1053  return $gui;
1054  }
1055 
1057  {
1058  require_once 'Modules/Test/classes/tables/class.ilTestDynamicQuestionSetStatisticTableGUI.php';
1059  $gui = $this->buildQuestionSetStatisticTableGUI(
1061  );
1062 
1063  $gui->initTitle('tst_dynamic_question_set_selection');
1064  $gui->initColumns('tst_num_selected_questions');
1065 
1066  require_once 'Services/Taxonomy/classes/class.ilObjTaxonomy.php';
1067  $gui->setTaxIds(ilObjTaxonomy::getUsageOfObject(
1068  $this->dynamicQuestionSetConfig->getSourceQuestionPoolId()
1069  ));
1070 
1071  $gui->initFilter();
1072  $gui->setFilterCommand('filterQuestionSelection');
1073  $gui->setResetCommand('resetQuestionSelection');
1074 
1075  return $gui;
1076  }
1077 
1078  private function buildQuestionSetStatisticTableGUI($tableId)
1079  {
1080  require_once 'Modules/Test/classes/tables/class.ilTestDynamicQuestionSetStatisticTableGUI.php';
1082  $this->ctrl, $this->lng, $this, 'showQuestionSelection', $tableId
1083  );
1084 
1085  $gui->setShowNumMarkedQuestionsEnabled($this->object->getShowMarker());
1086  $gui->setShowNumPostponedQuestionsEnabled($this->object->getSequenceSettings());
1087 
1088  return $gui;
1089  }
1090 
1091  private function getEnterTestButtonLangVar()
1092  {
1093  if( $this->testSequence->trackedQuestionExists() )
1094  {
1095  return $this->lng->txt('tst_resume_dyn_test_with_cur_quest_sel');
1096  }
1097 
1098  return $this->lng->txt('tst_start_dyn_test_with_cur_quest_sel');
1099  }
1100 
1101  protected function persistQuestionAnswerStatus()
1102  {
1103  $questionId = $this->testSession->getCurrentQuestionId();
1104  $activeId = $this->testSession->getActiveId();
1105  $pass = $this->testSession->getPass();
1106 
1107  if($this->isQuestionAnsweredCorrect($questionId, $activeId, $pass))
1108  {
1109  $this->testSequence->setQuestionAnsweredCorrect($questionId);
1110  }
1111  else
1112  {
1113  $this->testSequence->setQuestionAnsweredWrong($questionId);
1114  }
1115  }
1116 
1117  private function resetCurrentQuestion()
1118  {
1119  $this->testSession->setCurrentQuestionId(null);
1120 
1121  $this->testSequence->saveToDb();
1122  $this->testSession->saveToDb();
1123 
1124  $this->ctrl->setParameter($this, 'sequence', $this->testSession->getCurrentQuestionId());
1125  }
1126 
1131  {
1132  $href = $this->getPassDeletionTarget();
1133 
1134  $label = $this->lng->txt('tst_dyn_test_msg_pass_deletion_link');
1135 
1136  return "<a href=\"{$href}\">{$label}</a>";
1137  }
1138 
1142  private function getPassDeletionTarget()
1143  {
1144  require_once 'Modules/Test/classes/confirmations/class.ilTestPassDeletionConfirmationGUI.php';
1145 
1146  $this->ctrl->setParameterByClass('ilTestEvaluationGUI', 'context', ilTestPassDeletionConfirmationGUI::CONTEXT_DYN_TEST_PLAYER);
1147  $this->ctrl->setParameterByClass('ilTestEvaluationGUI', 'active_id', $this->testSession->getActiveId());
1148  $this->ctrl->setParameterByClass('ilTestEvaluationGUI', 'pass', $this->testSession->getPass());
1149 
1150  return $this->ctrl->getLinkTargetByClass('ilTestEvaluationGUI', 'confirmDeletePass');
1151  }
1152 }