1<?php 2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */ 3 4require_once 'Modules/Test/classes/class.ilTestPlayerAbstractGUI.php'; 5 6/** 7 * Output class for assessment test execution 8 * 9 * The ilTestOutputGUI class creates the output for the ilObjTestGUI 10 * class when learners execute a test. This saves some heap space because 11 * the ilObjTestGUI class will be much smaller then 12 * 13 * @extends ilTestPlayerAbstractGUI 14 * 15 * @author Björn Heyser <bheyser@databay.de> 16 * @version $Id$ 17 * 18 * @package Modules/Test 19 * 20 * @ilCtrl_Calls ilTestPlayerDynamicQuestionSetGUI: ilAssGenFeedbackPageGUI 21 * @ilCtrl_Calls ilTestPlayerDynamicQuestionSetGUI: ilAssSpecFeedbackPageGUI 22 * @ilCtrl_Calls ilTestPlayerDynamicQuestionSetGUI: ilAssQuestionHintRequestGUI 23 * @ilCtrl_Calls ilTestPlayerDynamicQuestionSetGUI: ilAssQuestionPageGUI 24 * @ilCtrl_Calls ilTestPlayerDynamicQuestionSetGUI: ilTestDynamicQuestionSetStatisticTableGUI 25 * @ilCtrl_Calls ilTestPlayerDynamicQuestionSetGUI: ilToolbarGUI 26 * @ilCtrl_Calls ilTestPlayerDynamicQuestionSetGUI: ilTestSubmissionReviewGUI 27 * @ilCtrl_Calls ilTestPlayerDynamicQuestionSetGUI: ilTestPasswordProtectionGUI 28 */ 29class ilTestPlayerDynamicQuestionSetGUI extends ilTestPlayerAbstractGUI 30{ 31 /** 32 * @var ilObjTestDynamicQuestionSetConfig 33 */ 34 private $dynamicQuestionSetConfig = null; 35 36 /** 37 * @var ilTestSequenceDynamicQuestionSet 38 */ 39 protected $testSequence; 40 41 /** 42 * @var ilTestSessionDynamicQuestionSet 43 */ 44 protected $testSession; 45 46 /** 47 * execute command 48 */ 49 public function executeCommand() 50 { 51 global $DIC; 52 $ilDB = $DIC['ilDB']; 53 $lng = $DIC['lng']; 54 $ilPluginAdmin = $DIC['ilPluginAdmin']; 55 $ilTabs = $DIC['ilTabs']; 56 $tree = $DIC['tree']; 57 58 $ilTabs->clearTargets(); 59 60 $this->ctrl->saveParameter($this, "sequence"); 61 $this->ctrl->saveParameter($this, "active_id"); 62 63 $this->initAssessmentSettings(); 64 65 $DIC->globalScreen()->tool()->context()->current()->addAdditionalData( 66 ilTestPlayerLayoutProvider::TEST_PLAYER_KIOSK_MODE_ENABLED, 67 $this->object->getKioskMode() 68 ); 69 70 require_once 'Modules/Test/classes/class.ilObjTestDynamicQuestionSetConfig.php'; 71 $this->dynamicQuestionSetConfig = new ilObjTestDynamicQuestionSetConfig($tree, $ilDB, $ilPluginAdmin, $this->object); 72 $this->dynamicQuestionSetConfig->loadFromDb(); 73 74 $testSessionFactory = new ilTestSessionFactory($this->object); 75 $this->testSession = $testSessionFactory->getSession($_GET['active_id']); 76 77 $this->ensureExistingTestSession($this->testSession); 78 $this->checkTestSessionUser($this->testSession); 79 $this->initProcessLocker($this->testSession->getActiveId()); 80 81 $testSequenceFactory = new ilTestSequenceFactory($ilDB, $lng, $ilPluginAdmin, $this->object); 82 $this->testSequence = $testSequenceFactory->getSequenceByTestSession($this->testSession); 83 $this->testSequence->loadFromDb(); 84 85 if ($this->object->isInstantFeedbackAnswerFixationEnabled()) { 86 $this->testSequence->setPreventCheckedQuestionsFromComingUpEnabled(true); 87 } 88 89 include_once 'Services/jQuery/classes/class.iljQueryUtil.php'; 90 iljQueryUtil::initjQuery(); 91 include_once "./Services/YUI/classes/class.ilYuiUtil.php"; 92 ilYuiUtil::initConnectionWithAnimation(); 93 if ($this->object->getKioskMode()) { 94 include_once 'Services/UIComponent/Overlay/classes/class.ilOverlayGUI.php'; 95 ilOverlayGUI::initJavascript(); 96 } 97 98 $this->handlePasswordProtectionRedirect(); 99 100 $cmd = $this->ctrl->getCmd(); 101 $nextClass = $this->ctrl->getNextClass($this); 102 103 switch ($nextClass) { 104 case 'ilassquestionpagegui': 105 106 $questionId = $this->testSession->getCurrentQuestionId(); 107 108 require_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPageGUI.php"; 109 $page_gui = new ilAssQuestionPageGUI($questionId); 110 $ret = $this->ctrl->forwardCommand($page_gui); 111 break; 112 113 case 'ilassquestionhintrequestgui': 114 115 $this->ctrl->saveParameter($this, 'pmode'); 116 117 $questionGUI = $this->object->createQuestionGUI( 118 "", 119 $this->testSession->getCurrentQuestionId() 120 ); 121 122 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php'; 123 $questionHintTracking = new ilAssQuestionHintTracking( 124 $questionGUI->object->getId(), 125 $this->testSession->getActiveId(), 126 $this->testSession->getPass() 127 ); 128 129 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintRequestGUI.php'; 130 $gui = new ilAssQuestionHintRequestGUI( 131 $this, 132 ilTestPlayerCommands::SHOW_QUESTION, 133 $questionGUI, 134 $questionHintTracking 135 ); 136 137// fau: testNav - save the 'answer changed status' for viewing hint requests 138 $this->setAnswerChangedParameter($this->getAnswerChangedParameter()); 139// fau. 140 $this->ctrl->forwardCommand($gui); 141 142 break; 143 144 case 'ildynamicquestionsetstatistictablegui': 145 146 $this->ctrl->forwardCommand($this->buildQuestionSetFilteredStatisticTableGUI()); 147 148 break; 149 150 case 'iltestpasswordprotectiongui': 151 require_once 'Modules/Test/classes/class.ilTestPasswordProtectionGUI.php'; 152 $gui = new ilTestPasswordProtectionGUI($this->ctrl, $this->tpl, $this->lng, $this, $this->passwordChecker); 153 $ret = $this->ctrl->forwardCommand($gui); 154 break; 155 156 default: 157 158 $cmd .= 'Cmd'; 159 $ret = &$this->$cmd(); 160 break; 161 } 162 163 return $ret; 164 } 165 166 /** 167 * @return integer 168 */ 169 protected function getCurrentQuestionId() 170 { 171 return $this->testSession->getCurrentQuestionId(); 172 } 173 174 /** 175 * Resume a test at the last position 176 */ 177 protected function resumePlayerCmd() 178 { 179 if ($this->object->checkMaximumAllowedUsers() == false) { 180 return $this->showMaximumAllowedUsersReachedMessage(); 181 } 182 183 $this->handleUserSettings(); 184 185 if ($this->dynamicQuestionSetConfig->isAnyQuestionFilterEnabled()) { 186 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION_SELECTION); 187 } 188 189 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 190 } 191 192 protected function startTestCmd() 193 { 194 $this->testSession->setCurrentQuestionId(null); // no question "came up" yet 195 196 $this->testSession->saveToDb(); 197 198 $this->ctrl->setParameter($this, 'active_id', $this->testSession->getActiveId()); 199 200 assQuestion::_updateTestPassResults($this->testSession->getActiveId(), $this->testSession->getPass(), false, null, $this->object->id); 201 202 $_SESSION['active_time_id'] = $this->object->startWorkingTime( 203 $this->testSession->getActiveId(), 204 $this->testSession->getPass() 205 ); 206 207 $this->ctrl->saveParameter($this, 'tst_javascript'); 208 209 if ($this->dynamicQuestionSetConfig->isAnyQuestionFilterEnabled()) { 210 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION_SELECTION); 211 } 212 213 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 214 } 215 216 protected function showQuestionSelectionCmd() 217 { 218 $this->prepareSummaryPage(); 219 220 $this->testSequence->loadQuestions( 221 $this->dynamicQuestionSetConfig, 222 $this->testSession->getQuestionSetFilterSelection() 223 ); 224 225 $this->testSequence->cleanupQuestions($this->testSession); 226 227 $this->testSequence->saveToDb(); 228 229 require_once 'Services/UIComponent/Toolbar/classes/class.ilToolbarGUI.php'; 230 $toolbarGUI = new ilToolbarGUI(); 231 232 require_once 'Services/UIComponent/Button/classes/class.ilLinkButton.php'; 233 $button = ilLinkButton::getInstance(); 234 $button->setUrl($this->getStartTestFromQuestionSelectionLink()); 235 $button->setCaption($this->getEnterTestButtonLangVar()); 236 $button->setPrimary(true); 237 $toolbarGUI->addButtonInstance($button); 238 239 if ($this->object->getShowCancel()) { 240 $button = ilLinkButton::getInstance(); 241 $button->setUrl($this->ctrl->getLinkTarget( 242 $this, 243 ilTestPlayerCommands::SUSPEND_TEST 244 )); 245 $button->setCaption('cancel_test'); 246 $toolbarGUI->addButtonInstance($button); 247 } 248 249 if ($this->object->isPassDeletionAllowed()) { 250 require_once 'Modules/Test/classes/confirmations/class.ilTestPassDeletionConfirmationGUI.php'; 251 252 $toolbarGUI->addButton( 253 $this->lng->txt('tst_dyn_test_pass_deletion_button'), 254 $this->getPassDeletionTarget(ilTestPassDeletionConfirmationGUI::CONTEXT_DYN_TEST_PLAYER) 255 ); 256 } 257 258 $filteredData = array($this->buildQuestionSetAnswerStatisticRowArray( 259 $this->testSequence->getFilteredQuestionsData(), 260 $this->testSequence->getTrackedQuestionList() 261 )); #vd($filteredData); 262 $filteredTableGUI = $this->buildQuestionSetFilteredStatisticTableGUI(); 263 $filteredTableGUI->setData($filteredData); 264 265 $completeData = array($this->buildQuestionSetAnswerStatisticRowArray( 266 $this->testSequence->getCompleteQuestionsData(), 267 $this->testSequence->getTrackedQuestionList() 268 )); #vd($completeData); 269 $completeTableGUI = $this->buildQuestionSetCompleteStatisticTableGUI(); 270 $completeTableGUI->setData($completeData); 271 272 $content = $this->ctrl->getHTML($toolbarGUI); 273 $content .= $this->ctrl->getHTML($filteredTableGUI); 274 $content .= $this->ctrl->getHTML($completeTableGUI); 275 276 $this->tpl->setVariable('TABLE_LIST_OF_QUESTIONS', $content); 277 278 if ($this->object->getEnableProcessingTime()) { 279 $this->outProcessingTime($this->testSession->getActiveId()); 280 } 281 } 282 283 protected function filterQuestionSelectionCmd() 284 { 285 $tableGUI = $this->buildQuestionSetFilteredStatisticTableGUI(); 286 $tableGUI->writeFilterToSession(); 287 288 $taxFilterSelection = array(); 289 $answerStatusFilterSelection = ilAssQuestionList::ANSWER_STATUS_FILTER_ALL_NON_CORRECT; 290 291 foreach ($tableGUI->getFilterItems() as $item) { 292 if (strpos($item->getPostVar(), 'tax_') !== false) { 293 $taxId = substr($item->getPostVar(), strlen('tax_')); 294 $taxFilterSelection[$taxId] = $item->getValue(); 295 } elseif ($item->getPostVar() == 'question_answer_status') { 296 $answerStatusFilterSelection = $item->getValue(); 297 } 298 } 299 300 $this->testSession->getQuestionSetFilterSelection()->setTaxonomySelection($taxFilterSelection); 301 $this->testSession->getQuestionSetFilterSelection()->setAnswerStatusSelection($answerStatusFilterSelection); 302 $this->testSession->saveToDb(); 303 304 $this->testSequence->resetTrackedQuestionList(); 305 $this->testSequence->saveToDb(); 306 307 308 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION_SELECTION); 309 } 310 311 protected function resetQuestionSelectionCmd() 312 { 313 $tableGUI = $this->buildQuestionSetFilteredStatisticTableGUI(); 314 $tableGUI->resetFilter(); 315 316 $this->testSession->getQuestionSetFilterSelection()->setTaxonomySelection(array()); 317 $this->testSession->getQuestionSetFilterSelection()->setAnswerStatusSelection(null); 318 $this->testSession->saveToDb(); 319 320 $this->testSequence->resetTrackedQuestionList(); 321 $this->testSequence->saveToDb(); 322 323 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION_SELECTION); 324 } 325 326 protected function previousQuestionCmd() 327 { 328 // nothing to do, won't be called 329 } 330 331 protected function fromPassDeletionCmd() 332 { 333 $this->resetCurrentQuestion(); 334 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 335 } 336 337 protected function nextQuestionCmd() 338 { 339 $isWorkedThrough = assQuestion::_isWorkedThrough( 340 $this->testSession->getActiveId(), 341 $this->testSession->getCurrentQuestionId(), 342 $this->testSession->getPass() 343 ); 344 345 if (!$isWorkedThrough) { 346 $this->testSequence->setQuestionPostponed($this->testSession->getCurrentQuestionId()); 347 $this->testSequence->saveToDb(); 348 } 349 350 $this->resetCurrentQuestion(); 351 352 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 353 } 354 355 protected function markQuestionCmd() 356 { 357 global $DIC; 358 $ilUser = $DIC['ilUser']; 359 $this->object->setQuestionSetSolved(1, $this->testSession->getCurrentQuestionId(), $ilUser->getId()); 360 361 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 362 } 363 364 protected function unmarkQuestionCmd() 365 { 366 global $DIC; 367 $ilUser = $DIC['ilUser']; 368 $this->object->setQuestionSetSolved(0, $this->testSession->getCurrentQuestionId(), $ilUser->getId()); 369 370 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 371 } 372 373 protected function editSolutionCmd() 374 { 375 $this->ctrl->setParameter($this, 'pmode', ilTestPlayerAbstractGUI::PRESENTATION_MODE_EDIT); 376 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 377 } 378 379 protected function submitSolutionAndNextCmd() 380 { 381 if ($this->object->isForceInstantFeedbackEnabled()) { 382 return $this->submitSolutionCmd(); 383 } 384 385 if ($this->saveQuestionSolution(true, false)) { 386 $questionId = $this->testSession->getCurrentQuestionId(); 387 388 $this->getQuestionInstance($questionId)->removeIntermediateSolution( 389 $this->testSession->getActiveId(), 390 $this->testSession->getPass() 391 ); 392 393 $this->persistQuestionAnswerStatus(); 394 395 $this->ctrl->setParameter($this, 'pmode', ''); 396 397 $this->resetCurrentQuestion(); 398 } 399 400 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 401 } 402 403 protected function submitSolutionCmd() 404 { 405 if ($this->saveQuestionSolution(true, false)) { 406 $questionId = $this->testSession->getCurrentQuestionId(); 407 408 $this->getQuestionInstance($questionId)->removeIntermediateSolution( 409 $this->testSession->getActiveId(), 410 $this->testSession->getPass() 411 ); 412 413 $this->persistQuestionAnswerStatus(); 414 415 if ($this->object->isForceInstantFeedbackEnabled()) { 416 $this->ctrl->setParameter($this, 'instresp', 1); 417 418 $this->testSequence->unsetQuestionPostponed($questionId); 419 $this->testSequence->setQuestionChecked($questionId); 420 $this->testSequence->saveToDb(); 421 } 422 423 if ($this->getNextCommandParameter()) { 424 if ($this->getNextSequenceParameter()) { 425 $this->ctrl->setParameter($this, 'sequence', $this->getNextSequenceParameter()); 426 $this->ctrl->setParameter($this, 'pmode', ''); 427 } 428 429 $this->ctrl->redirect($this, $this->getNextCommandParameter()); 430 } 431 432 $this->ctrl->setParameter($this, 'pmode', ilTestPlayerAbstractGUI::PRESENTATION_MODE_VIEW); 433 } else { 434 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 435 } 436 437 // fau: testNav - remember to prevent the navigation confirmation 438 $this->saveNavigationPreventConfirmation(); 439 // fau. 440 441 // fau: testNav - handle navigation after saving 442 if ($this->getNavigationUrlParameter()) { 443 ilUtil::redirect($this->getNavigationUrlParameter()); 444 } else { 445 $this->ctrl->saveParameter($this, 'sequence'); 446 } 447 // fau. 448 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 449 } 450 451 protected function discardSolutionCmd() 452 { 453 $questionId = $this->testSession->getCurrentQuestionId(); 454 455 $currentQuestionOBJ = $this->getQuestionInstance($questionId); 456 457 $currentQuestionOBJ->resetUsersAnswer( 458 $this->testSession->getActiveId(), 459 $this->testSession->getPass() 460 ); 461 462 $this->ctrl->setParameter($this, 'pmode', ilTestPlayerAbstractGUI::PRESENTATION_MODE_VIEW); 463 464 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 465 } 466 467 protected function skipQuestionCmd() 468 { 469 $this->nextQuestionCmd(); 470 } 471 472 protected function isCheckedQuestionResettingConfirmationRequired() 473 { 474 if (!$this->getResetCheckedParameter()) { 475 return false; 476 } 477 478 if ($this->testSession->getQuestionSetFilterSelection()->isAnswerStatusSelectionWrongAnswered()) { 479 $this->testSequence->loadQuestions( 480 $this->dynamicQuestionSetConfig, 481 $this->testSession->getQuestionSetFilterSelection() 482 ); 483 484 if ($this->testSequence->hasFilteredQuestionListCheckedQuestions()) { 485 return true; 486 } 487 } 488 489 return false; 490 } 491 492 protected function showQuestionCmd() 493 { 494 $this->updateWorkingTime(); 495 496 $this->testSequence->loadQuestions( 497 $this->dynamicQuestionSetConfig, 498 $this->testSession->getQuestionSetFilterSelection() 499 ); 500 501 $this->testSequence->cleanupQuestions($this->testSession); 502 503 if ($this->isCheckedQuestionResettingConfirmationRequired()) { 504 $this->showCheckedQuestionResettingConfirmation(); 505 return; 506 } 507 508 if ($this->testSequence->getQuestionSet()->getSelectionQuestionList()->isInList($this->getQuestionIdParameter())) { 509 $this->testSession->setCurrentQuestionId($this->getQuestionIdParameter()); 510 } else { 511 $this->resetQuestionIdParameter(); 512 } 513 514 if (!$this->testSession->getCurrentQuestionId()) { 515 $upComingQuestionId = $this->testSequence->getUpcomingQuestionId(); 516 517 $this->testSession->setCurrentQuestionId($upComingQuestionId); 518 519 // seems to be a first try of freezing answers not too hard 520 /*if( $this->testSequence->isQuestionChecked($upComingQuestionId) ) 521 { 522 $this->testSequence->setQuestionUnchecked($upComingQuestionId); 523 }*/ 524 } 525 526 $navigationToolbarGUI = $this->getTestNavigationToolbarGUI(); 527 $navigationToolbarGUI->setQuestionSelectionButtonEnabled(true); 528 529 if ($this->testSession->getCurrentQuestionId()) { 530 $questionGui = $this->getQuestionGuiInstance($this->testSession->getCurrentQuestionId()); 531 $this->testSequence->setCurrentQuestionId($this->testSession->getCurrentQuestionId()); 532 533 $questionGui->setQuestionCount( 534 $this->testSequence->getLastPositionIndex() 535 ); 536 $questionGui->setSequenceNumber( 537 $this->testSequence->getCurrentPositionIndex($this->testSession->getCurrentQuestionId()) 538 ); 539 540 if (!($questionGui instanceof assQuestionGUI)) { 541 $this->handleTearsAndAngerQuestionIsNull( 542 $this->testSession->getCurrentQuestionId(), 543 $this->testSession->getCurrentQuestionId() 544 ); 545 } 546 547 $isQuestionWorkedThrough = assQuestion::_isWorkedThrough( 548 $this->testSession->getActiveId(), 549 $this->testSession->getCurrentQuestionId(), 550 $this->testSession->getPass() 551 ); 552 553 require_once 'Modules/Test/classes/class.ilTestQuestionHeaderBlockBuilder.php'; 554 $headerBlockBuilder = new ilTestQuestionHeaderBlockBuilder($this->lng); 555 $headerBlockBuilder->setHeaderMode( 556 // avoid legacy setting combination: ctm without question titles 557 $this->object->getTitleOutput() == 2 ? 1 : $this->object->getTitleOutput() 558 ); 559 $headerBlockBuilder->setQuestionTitle($questionGui->object->getTitle()); 560 $headerBlockBuilder->setQuestionPoints($questionGui->object->getPoints()); 561 /* avoid showing Qst X of Y within CTMs 562 $headerBlockBuilder->setQuestionPosition( 563 $this->testSequence->getCurrentPositionIndex($this->testSession->getCurrentQuestionId()) 564 ); 565 $headerBlockBuilder->setQuestionCount($this->testSequence->getLastPositionIndex()); 566 */ 567 $headerBlockBuilder->setQuestionPostponed( 568 $this->testSequence->isPostponedQuestion( 569 $this->testSession->getCurrentQuestionId() 570 ) 571 ); 572 $headerBlockBuilder->setQuestionObligatory( 573 $this->object->areObligationsEnabled() && ilObjTest::isQuestionObligatory($this->object->getId()) 574 ); 575 $questionGui->setQuestionHeaderBlockBuilder($headerBlockBuilder); 576 577 // fau: testNav - always use edit mode, except for fixed answer 578 if ($this->isParticipantsAnswerFixed($this->testSession->getCurrentQuestionId())) { 579 $instantResponse = true; 580 $presentationMode = ilTestPlayerAbstractGUI::PRESENTATION_MODE_VIEW; 581 } else { 582 $instantResponse = $this->getInstantResponseParameter(); 583 $presentationMode = ilTestPlayerAbstractGUI::PRESENTATION_MODE_EDIT; 584 } 585 // fau. 586 587 $this->prepareTestPage( 588 $presentationMode, 589 $this->testSession->getCurrentQuestionId(), 590 $this->testSession->getCurrentQuestionId() 591 ); 592 593 $this->ctrl->setParameter($this, 'sequence', $this->testSession->getCurrentQuestionId()); 594 $this->ctrl->setParameter($this, 'pmode', $presentationMode); 595 $formAction = $this->ctrl->getFormAction($this, ilTestPlayerCommands::SUBMIT_INTERMEDIATE_SOLUTION); 596 597 switch ($presentationMode) { 598 case ilTestPlayerAbstractGUI::PRESENTATION_MODE_EDIT: 599 600// fau: testNav - enable navigation toolbar in edit mode 601 $navigationToolbarGUI->setDisabledStateEnabled(false); 602// fau. 603 $this->showQuestionEditable($questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse); 604 605 break; 606 607 case ilTestPlayerAbstractGUI::PRESENTATION_MODE_VIEW: 608 609 $this->showQuestionViewable($questionGui, $formAction, $isQuestionWorkedThrough, $instantResponse); 610 611 break; 612 613 default: 614 615 require_once 'Modules/Test/exceptions/class.ilTestException.php'; 616 throw new ilTestException('no presentation mode given'); 617 } 618 619 $navigationToolbarGUI->build(); 620 $this->populateTestNavigationToolbar($navigationToolbarGUI); 621 622 // fau: testNav - enable the question navigation in edit mode 623 $this->populateQuestionNavigation( 624 $this->testSession->getCurrentQuestionId(), 625 false, 626 $this->object->isForceInstantFeedbackEnabled() 627 ); 628 // fau. 629 630 if ($instantResponse) { 631 // fau: testNav - always use authorized solution for instant feedback 632 $this->populateInstantResponseBlocks( 633 $questionGui, 634 true 635 ); 636 // fau. 637 $this->testSession->getQuestionSetFilterSelection()->setForcedQuestionIds(array()); 638 } 639 640 // fau: testNav - add feedback modal 641 if ($this->isForcedFeedbackNavUrlRegistered()) { 642 $this->populateInstantResponseModal($questionGui, $this->getRegisteredForcedFeedbackNavUrl()); 643 $this->unregisterForcedFeedbackNavUrl(); 644 } 645 // fau. 646 } else { 647 $this->prepareTestPage(ilTestPlayerAbstractGUI::PRESENTATION_MODE_VIEW, null, null); 648 649 $navigationToolbarGUI->build(); 650 $this->populateTestNavigationToolbar($navigationToolbarGUI); 651 652 $this->outCurrentlyFinishedPage(); 653 } 654 655 $this->testSequence->saveToDb(); 656 $this->testSession->saveToDb(); 657 } 658 659 protected function showInstantResponseCmd() 660 { 661 $questionId = $this->testSession->getCurrentQuestionId(); 662 663 $filterSelection = $this->testSession->getQuestionSetFilterSelection(); 664 665 $filterSelection->setForcedQuestionIds(array($this->testSession->getCurrentQuestionId())); 666 667 $this->testSequence->loadQuestions($this->dynamicQuestionSetConfig, $filterSelection); 668 $this->testSequence->cleanupQuestions($this->testSession); 669 $this->testSequence->saveToDb(); 670 671 if (!$this->isParticipantsAnswerFixed($questionId)) { 672 if ($this->saveQuestionSolution(true)) { 673 $this->removeIntermediateSolution(); 674 $this->persistQuestionAnswerStatus(); 675 $this->setAnswerChangedParameter(false); 676 } else { 677 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 678 } 679 $this->testSequence->unsetQuestionPostponed($questionId); 680 $this->testSequence->setQuestionChecked($questionId); 681 $this->testSequence->saveToDb(); 682 } 683 684 $this->ctrl->setParameter( 685 $this, 686 'sequence', 687 $this->testSession->getCurrentQuestionId() 688 ); 689 690 $this->ctrl->setParameter($this, 'instresp', 1); 691 692 // fau: testNav - handle navigation after feedback 693 if ($this->getNavigationUrlParameter()) { 694 $this->saveNavigationPreventConfirmation(); 695 $this->registerForcedFeedbackNavUrl($this->getNavigationUrlParameter()); 696 } 697 // fau. 698 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 699 } 700 701 protected function handleQuestionActionCmd() 702 { 703 $questionId = $this->testSession->getCurrentQuestionId(); 704 705 if ($questionId && !$this->isParticipantsAnswerFixed($questionId)) { 706 $this->saveQuestionSolution(false); 707 // fau: testNav - add changed status of the question 708 $this->setAnswerChangedParameter(true); 709 // fau. 710 } 711 712 $this->ctrl->setParameter( 713 $this, 714 'sequence', 715 $this->testSession->getCurrentQuestionId() 716 ); 717 718 $this->ctrl->saveParameter($this, 'pmode'); 719 720 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 721 } 722 723 private function outCurrentlyFinishedPage() 724 { 725 if ($this->testSequence->openQuestionExists()) { 726 $message = $this->lng->txt('tst_dyn_test_msg_currently_finished_selection'); 727 } else { 728 $message = $this->lng->txt('tst_dyn_test_msg_currently_finished_completely'); 729 $message .= "<br /><br />{$this->buildFinishPagePassDeletionLink()}"; 730 } 731 732 $msgHtml = ilUtil::getSystemMessageHTML($message); 733 734 $tpl = new ilTemplate('tpl.test_currently_finished_msg.html', true, true, 'Modules/Test'); 735 $tpl->setVariable('TEST_CURRENTLY_FINISHED_MSG', $msgHtml); 736 737 $this->tpl->setVariable('QUESTION_OUTPUT', $tpl->get()); 738 } 739 740 protected function isFirstQuestionInSequence($sequenceElement) 741 { 742 return !$this->testSequence->trackedQuestionExists(); 743 } 744 745 protected function isLastQuestionInSequence($sequenceElement) 746 { 747 return false; // always 748 } 749 750 /** 751 * Returns TRUE if the answers of the current user could be saved 752 * 753 * @return boolean TRUE if the answers could be saved, FALSE otherwise 754 */ 755 protected function canSaveResult() 756 { 757 return !$this->object->endingTimeReached(); 758 } 759 760 /** 761 * saves the user input of a question 762 */ 763 public function saveQuestionSolution($authorized = true, $force = false) 764 { 765 // what is this formtimestamp ?? 766 if (!$force) { 767 $formtimestamp = $_POST["formtimestamp"]; 768 if (strlen($formtimestamp) == 0) { 769 $formtimestamp = $_GET["formtimestamp"]; 770 } 771 if ($formtimestamp != $_SESSION["formtimestamp"]) { 772 $_SESSION["formtimestamp"] = $formtimestamp; 773 } else { 774 return false; 775 } 776 } 777 778 // determine current question 779 780 $qId = $this->testSession->getCurrentQuestionId(); 781 782 if (!$qId || $qId != $_GET["sequence"]) { 783 return false; 784 } 785 786 // save question solution 787 788 $this->saveResult = false; 789 790 if ($this->canSaveResult($qId) || $force) { 791 $questionGUI = $this->object->createQuestionGUI("", $qId); 792 793 if ($this->object->getJavaScriptOutput()) { 794 $questionGUI->object->setOutputType(OUTPUT_JAVASCRIPT); 795 } 796 797 $activeId = $this->testSession->getActiveId(); 798 799 $this->saveResult = $questionGUI->object->persistWorkingState( 800 $activeId, 801 $pass = null, 802 $this->object->areObligationsEnabled(), 803 $authorized 804 ); 805 806 if ($authorized && $this->object->isSkillServiceToBeConsidered()) { 807 $this->handleSkillTriggering($this->testSession); 808 } 809 } 810 811 if ($this->saveResult == false) { 812 $this->ctrl->setParameter($this, "save_error", "1"); 813 $_SESSION["previouspost"] = $_POST; 814 } 815 816 return $this->saveResult; 817 } 818 819 private function isQuestionAnsweredCorrect($questionId, $activeId, $pass) 820 { 821 $questionGUI = $this->object->createQuestionGUI("", $questionId); 822 823 $reachedPoints = assQuestion::_getReachedPoints($activeId, $questionId, $pass); 824 $maxPoints = $questionGUI->object->getMaximumPoints(); 825 826 if ($reachedPoints < $maxPoints) { 827 return false; 828 } 829 830 return true; 831 } 832 833 protected function buildQuestionsTableDataArray($questions, $marked_questions) 834 { 835 $data = array(); 836 837 foreach ($questions as $key => $value) { 838 $this->ctrl->setParameter($this, 'sequence', $value['question_id']); 839 $href = $this->ctrl->getLinkTarget($this, 'gotoQuestion'); 840 $this->ctrl->setParameter($this, 'sequence', ''); 841 842 $description = ""; 843 if ($this->object->getListOfQuestionsDescription()) { 844 $description = $value["description"]; 845 } 846 847 $marked = false; 848 if (count($marked_questions)) { 849 if (isset($marked_questions[$value["question_id"]])) { 850 if ($marked_questions[$value["question_id"]]["solved"] == 1) { 851 $marked = true; 852 } 853 } 854 } 855 856 array_push($data, array( 857 'href' => $href, 858 'title' => $this->object->getQuestionTitle($value["title"]), 859 'description' => $description, 860 'worked_through' => $this->testSequence->isAnsweredQuestion($value["question_id"]), 861 'postponed' => $this->testSequence->isPostponedQuestion($value["question_id"]), 862 'marked' => $marked 863 )); 864 } 865 866 return $data; 867 } 868 869 protected function buildQuestionSetAnswerStatisticRowArray($questions, $trackedQuestions) 870 { 871 $questionAnswerStats = array( 872 'total_all' => count($questions), 873 'total_open' => 0, 874 'non_answered_notseen' => 0, 875 'non_answered_skipped' => 0, 876 'wrong_answered' => 0, 877 'correct_answered' => 0 878 ); 879 880 foreach ($questions as $key => $value) { 881 switch ($value['question_answer_status']) { 882 case ilAssQuestionList::QUESTION_ANSWER_STATUS_NON_ANSWERED: 883 if (isset($trackedQuestions[$key])) { 884 $questionAnswerStats['non_answered_skipped']++; 885 } else { 886 $questionAnswerStats['non_answered_notseen']++; 887 } 888 $questionAnswerStats['total_open']++; 889 break; 890 case ilAssQuestionList::QUESTION_ANSWER_STATUS_WRONG_ANSWERED: 891 $questionAnswerStats['wrong_answered']++; 892 $questionAnswerStats['total_open']++; 893 break; 894 case ilAssQuestionList::QUESTION_ANSWER_STATUS_CORRECT_ANSWERED: 895 $questionAnswerStats['correct_answered']++; 896 break; 897 } 898 } 899 900 return $questionAnswerStats; 901 } 902 903 private function buildQuestionSetCompleteStatisticTableGUI() 904 { 905 require_once 'Modules/Test/classes/tables/class.ilTestDynamicQuestionSetStatisticTableGUI.php'; 906 $gui = $this->buildQuestionSetStatisticTableGUI( 907 ilTestDynamicQuestionSetStatisticTableGUI::COMPLETE_TABLE_ID 908 ); 909 910 $gui->initTitle('tst_dynamic_question_set_complete'); 911 $gui->initColumns('tst_num_all_questions'); 912 913 return $gui; 914 } 915 916 private function buildQuestionSetFilteredStatisticTableGUI() 917 { 918 require_once 'Modules/Test/classes/tables/class.ilTestDynamicQuestionSetStatisticTableGUI.php'; 919 $gui = $this->buildQuestionSetStatisticTableGUI( 920 ilTestDynamicQuestionSetStatisticTableGUI::FILTERED_TABLE_ID 921 ); 922 923 $gui->initTitle('tst_dynamic_question_set_selection'); 924 $gui->initColumns('tst_num_selected_questions'); 925 926 require_once 'Services/Taxonomy/classes/class.ilObjTaxonomy.php'; 927 $gui->setTaxIds(ilObjTaxonomy::getUsageOfObject( 928 $this->dynamicQuestionSetConfig->getSourceQuestionPoolId() 929 )); 930 931 $gui->setTaxonomyFilterEnabled($this->dynamicQuestionSetConfig->isTaxonomyFilterEnabled()); 932 $gui->setAnswerStatusFilterEnabled($this->dynamicQuestionSetConfig->isAnswerStatusFilterEnabled()); 933 934 $gui->setFilterSelection($this->testSession->getQuestionSetFilterSelection()); 935 $gui->initFilter(); 936 $gui->setFilterCommand('filterQuestionSelection'); 937 $gui->setResetCommand('resetQuestionSelection'); 938 939 return $gui; 940 } 941 942 private function buildQuestionSetStatisticTableGUI($tableId) 943 { 944 require_once 'Modules/Test/classes/tables/class.ilTestDynamicQuestionSetStatisticTableGUI.php'; 945 $gui = new ilTestDynamicQuestionSetStatisticTableGUI( 946 $this->ctrl, 947 $this->lng, 948 $this, 949 ilTestPlayerCommands::SHOW_QUESTION_SELECTION, 950 $tableId 951 ); 952 953 return $gui; 954 } 955 956 private function getEnterTestButtonLangVar() 957 { 958 if ($this->testSequence->trackedQuestionExists()) { 959 return 'tst_resume_dyn_test_with_cur_quest_sel'; 960 } 961 962 return 'tst_start_dyn_test_with_cur_quest_sel'; 963 } 964 965 protected function persistQuestionAnswerStatus() 966 { 967 $questionId = $this->testSession->getCurrentQuestionId(); 968 $activeId = $this->testSession->getActiveId(); 969 $pass = $this->testSession->getPass(); 970 971 if ($this->isQuestionAnsweredCorrect($questionId, $activeId, $pass)) { 972 $this->testSequence->setQuestionAnsweredCorrect($questionId); 973 } else { 974 $this->testSequence->setQuestionAnsweredWrong($questionId); 975 } 976 977 $this->testSequence->saveToDb(); 978 } 979 980 private function resetCurrentQuestion() 981 { 982 $this->testSession->setCurrentQuestionId(null); 983 $this->testSession->saveToDb(); 984 985 $this->ctrl->setParameter($this, 'sequence', $this->testSession->getCurrentQuestionId()); 986 $this->ctrl->setParameter($this, 'pmode', ''); 987 } 988 989 /** 990 * @return string 991 */ 992 private function buildFinishPagePassDeletionLink() 993 { 994 $href = $this->getPassDeletionTarget(); 995 996 $label = $this->lng->txt('tst_dyn_test_msg_pass_deletion_link'); 997 998 return "<a href=\"{$href}\">{$label}</a>"; 999 } 1000 1001 /** 1002 * @return string 1003 */ 1004 private function getPassDeletionTarget() 1005 { 1006 require_once 'Modules/Test/classes/confirmations/class.ilTestPassDeletionConfirmationGUI.php'; 1007 1008 $this->ctrl->setParameterByClass('ilTestEvaluationGUI', 'context', ilTestPassDeletionConfirmationGUI::CONTEXT_DYN_TEST_PLAYER); 1009 $this->ctrl->setParameterByClass('ilTestEvaluationGUI', 'active_id', $this->testSession->getActiveId()); 1010 $this->ctrl->setParameterByClass('ilTestEvaluationGUI', 'pass', $this->testSession->getPass()); 1011 1012 return $this->ctrl->getLinkTargetByClass('ilTestEvaluationGUI', 'confirmDeletePass'); 1013 } 1014 1015 protected function resetQuestionIdParameter() 1016 { 1017 $this->resetSequenceElementParameter(); 1018 } 1019 1020 protected function getQuestionIdParameter() 1021 { 1022 return $this->getSequenceElementParameter(); 1023 } 1024 1025 protected function getResetCheckedParameter() 1026 { 1027 if (isset($_GET['reset_checked'])) { 1028 return $_GET['reset_checked']; 1029 } 1030 1031 return null; 1032 } 1033 1034 public function outQuestionSummaryCmd($fullpage = true, $contextFinishTest = false, $obligationsNotAnswered = false, $obligationsFilter = false) 1035 { 1036 $this->testSequence->loadQuestions( 1037 $this->dynamicQuestionSetConfig, 1038 $this->testSession->getQuestionSetFilterSelection() 1039 ); 1040 1041 $this->testSequence->setCurrentQuestionId($this->testSession->getCurrentQuestionId()); 1042 1043 parent::outQuestionSummaryCmd($fullpage, $contextFinishTest, $obligationsNotAnswered, $obligationsFilter); 1044 } 1045 1046 protected function showCheckedQuestionResettingConfirmation() 1047 { 1048 require_once 'Services/Utilities/classes/class.ilConfirmationGUI.php'; 1049 $confirmation = new ilConfirmationGUI(); 1050 $confirmation->setFormAction($this->ctrl->getFormAction($this)); 1051 $confirmation->setHeaderText($this->lng->txt('tst_dyn_unfreeze_answers_confirmation')); 1052 $confirmation->setConfirm($this->lng->txt('tst_dyn_unfreeze_answers'), ilTestPlayerCommands::UNFREEZE_ANSWERS); 1053 $confirmation->setCancel($this->lng->txt('tst_dyn_keep_answ_freeze'), ilTestPlayerCommands::SHOW_QUESTION); 1054 1055 $this->populateMessageContent($confirmation->getHtml()); 1056 } 1057 1058 protected function unfreezeCheckedQuestionsAnswersCmd() 1059 { 1060 $this->testSequence->loadQuestions( 1061 $this->dynamicQuestionSetConfig, 1062 $this->testSession->getQuestionSetFilterSelection() 1063 ); 1064 1065 $this->testSequence->resetFilteredQuestionListsCheckedStatus(); 1066 $this->testSequence->saveToDb(); 1067 1068 $this->ctrl->redirect($this, ilTestPlayerCommands::SHOW_QUESTION); 1069 } 1070 1071 protected function populateQuestionNavigation($sequenceElement, $disabled, $primaryNext) 1072 { 1073 if (!$this->isLastQuestionInSequence($sequenceElement)) { 1074 $this->populateNextButtons($disabled, $primaryNext); 1075 } 1076 } 1077 1078 protected function getStartTestFromQuestionSelectionLink() 1079 { 1080 $this->ctrl->setParameter($this, 'reset_checked', 1); 1081 $link = $this->ctrl->getLinkTarget($this, ilTestPlayerCommands::SHOW_QUESTION); 1082 $this->ctrl->setParameter($this, 'reset_checked', ''); 1083 1084 return $link; 1085 } 1086 1087 protected function isShowingPostponeStatusReguired($questionId) 1088 { 1089 return false; 1090 } 1091 1092 protected function buildTestPassQuestionList() 1093 { 1094 global $DIC; 1095 $ilPluginAdmin = $DIC['ilPluginAdmin']; 1096 1097 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionList.php'; 1098 $questionList = new ilAssQuestionList($this->db, $this->lng, $ilPluginAdmin); 1099 $questionList->setParentObjId($this->dynamicQuestionSetConfig->getSourceQuestionPoolId()); 1100 $questionList->setQuestionInstanceTypeFilter(ilAssQuestionList::QUESTION_INSTANCE_TYPE_ORIGINALS); 1101 1102 return $questionList; 1103 } 1104 1105 protected function isQuestionSummaryFinishTestButtonRequired() 1106 { 1107 return false; 1108 } 1109 1110 protected function isOptionalQuestionAnsweringConfirmationRequired($sequenceKey) 1111 { 1112 return false; 1113 } 1114} 1115