1<?php 2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */ 3 4require_once './Modules/Test/classes/inc.AssessmentConstants.php'; 5require_once 'Modules/Test/classes/class.ilTestExpressPage.php'; 6require_once 'Modules/TestQuestionPool/exceptions/class.ilTestQuestionPoolException.php'; 7require_once 'Modules/Test/classes/class.ilTestExpressPage.php'; 8require_once 'Modules/Test/classes/class.ilObjAssessmentFolder.php'; 9 10/** 11* Basic GUI class for assessment questions 12* 13* The assQuestionGUI class encapsulates basic GUI functions for assessment questions. 14* 15* @author Helmut Schottmüller <helmut.schottmueller@mac.com> 16* @author Björn Heyser <bheyser@databay.de> 17* @version $Id$ 18* @ingroup ModulesTestQuestionPool 19*/ 20abstract class assQuestionGUI 21{ 22 const FORM_MODE_EDIT = 'edit'; 23 const FORM_MODE_ADJUST = 'adjust'; 24 25 const FORM_ENCODING_URLENCODE = 'application/x-www-form-urlencoded'; 26 const FORM_ENCODING_MULTIPART = 'multipart/form-data'; 27 28 const SESSION_PREVIEW_DATA_BASE_INDEX = 'ilAssQuestionPreviewAnswers'; 29 30 /** 31 * Question object 32 * 33 * A reference to the matching question object 34 * 35 * @var assQuestion 36 */ 37 public $object; 38 39 /** @var ilGlobalTemplateInterface */ 40 public $tpl; 41 public $lng; 42 public $error; 43 public $errormessage; 44 45 /** 46 * sequence number in test 47 */ 48 public $sequence_no; 49 /** 50 * question count in test 51 */ 52 public $question_count; 53 54 private $taxonomyIds = array(); 55 56 private $targetGuiClass = null; 57 58 private $questionActionCmd = 'handleQuestionAction'; 59 60 /** 61 * @var ilQuestionHeaderBlockBuilder 62 */ 63 private $questionHeaderBlockBuilder; 64 65 /** 66 * @var ilTestQuestionNavigationGUI 67 */ 68 private $navigationGUI; 69 70 const PRESENTATION_CONTEXT_TEST = 'pContextTest'; 71 const PRESENTATION_CONTEXT_RESULTS = 'pContextResults'; 72 73 /** 74 * @var string 75 */ 76 private $presentationContext = null; 77 78 const RENDER_PURPOSE_PLAYBACK = 'renderPurposePlayback'; 79 const RENDER_PURPOSE_DEMOPLAY = 'renderPurposeDemoplay'; 80 const RENDER_PURPOSE_PREVIEW = 'renderPurposePreview'; 81 const RENDER_PURPOSE_PRINT_PDF = 'renderPurposePrintPdf'; 82 const RENDER_PURPOSE_INPUT_VALUE = 'renderPurposeInputValue'; 83 84 /** 85 * @var string 86 */ 87 private $renderPurpose = self::RENDER_PURPOSE_PLAYBACK; 88 89 const EDIT_CONTEXT_AUTHORING = 'authoring'; 90 const EDIT_CONTEXT_ADJUSTMENT = 'adjustment'; 91 92 /** 93 * @var string 94 */ 95 private $editContext = self::EDIT_CONTEXT_AUTHORING; 96 97 // hey: prevPassSolutions - flag to indicate that a previous answer is shown 98 /** 99 * @var bool 100 */ 101 private $previousSolutionPrefilled = false; 102 // hey. 103 104 /** 105 * @var \ilPropertyFormGUI 106 */ 107 protected $editForm; 108 109 /** 110 * Prefer the intermediate solution for solution output 111 * @var bool 112 */ 113 protected $use_intermediate_solution = false; 114 115 /** 116 * assQuestionGUI constructor 117 */ 118 public function __construct() 119 { 120 global $DIC; 121 $lng = $DIC['lng']; 122 $tpl = $DIC['tpl']; 123 $ilCtrl = $DIC['ilCtrl']; 124 125 $this->lng = &$lng; 126 $this->tpl = &$tpl; 127 $this->ctrl = &$ilCtrl; 128 $this->ctrl->saveParameter($this, "q_id"); 129 $this->ctrl->saveParameter($this, "prev_qid"); 130 $this->ctrl->saveParameter($this, "calling_test"); 131 $this->ctrl->saveParameter($this, "calling_consumer"); 132 $this->ctrl->saveParameter($this, "consumer_context"); 133 $this->ctrl->saveParameterByClass('ilAssQuestionPageGUI', 'test_express_mode'); 134 $this->ctrl->saveParameterByClass('ilAssQuestionPageGUI', 'calling_consumer'); 135 $this->ctrl->saveParameterByClass('ilAssQuestionPageGUI', 'consumer_context'); 136 $this->ctrl->saveParameterByClass('ilobjquestionpoolgui', 'test_express_mode'); 137 $this->ctrl->saveParameterByClass('ilobjquestionpoolgui', 'calling_consumer'); 138 $this->ctrl->saveParameterByClass('ilobjquestionpoolgui', 'consumer_context'); 139 140 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 141 $this->errormessage = $this->lng->txt("fill_out_all_required_fields"); 142 143 $this->selfassessmenteditingmode = false; 144 $this->new_id_listeners = array(); 145 $this->new_id_listener_cnt = 0; 146 147 $this->navigationGUI = null; 148 } 149 150 /** 151 * this method can be overwritten per question type 152 * 153 * @return bool 154 */ 155 public function hasInlineFeedback() 156 { 157 return false; 158 } 159 160 public function addHeaderAction() 161 { 162 global $DIC; /* @var ILIAS\DI\Container $DIC */ 163 164 $DIC->ui()->mainTemplate()->setVariable( 165 "HEAD_ACTION", 166 $this->getHeaderAction() 167 ); 168 169 $notesUrl = $this->ctrl->getLinkTargetByClass( 170 array("ilcommonactiondispatchergui", "ilnotegui"), 171 "", 172 "", 173 true, 174 false 175 ); 176 177 ilNoteGUI::initJavascript($notesUrl, IL_NOTE_PUBLIC, $DIC->ui()->mainTemplate()); 178 179 $redrawActionsUrl = $DIC->ctrl()->getLinkTarget($this, 'redrawHeaderAction', '', true); 180 $DIC->ui()->mainTemplate()->addOnLoadCode("il.Object.setRedrawAHUrl('$redrawActionsUrl');"); 181 } 182 183 public function redrawHeaderAction() 184 { 185 global $DIC; /* @var ILIAS\DI\Container $DIC */ 186 echo $this->getHeaderAction() . $DIC->ui()->mainTemplate()->getOnLoadCodeForAsynch(); 187 exit; 188 } 189 190 public function getHeaderAction() 191 { 192 global $DIC; /* @var ILIAS\DI\Container $DIC */ 193 /* @var ilObjectDataCache $ilObjDataCache */ 194 $ilObjDataCache = $DIC['ilObjDataCache']; 195 196 $parentObjType = $ilObjDataCache->lookupType($this->object->getObjId()); 197 198 $dispatcher = new ilCommonActionDispatcherGUI( 199 ilCommonActionDispatcherGUI::TYPE_REPOSITORY, 200 $DIC->access(), 201 $parentObjType, 202 $_GET["ref_id"], 203 $this->object->getObjId() 204 ); 205 206 $dispatcher->setSubObject("quest", $this->object->getId()); 207 208 $ha = $dispatcher->initHeaderAction(); 209 $ha->enableComments(true, false); 210 211 return $ha->getHeaderAction($DIC->ui()->mainTemplate()); 212 } 213 214 public function getNotesHTML() 215 { 216 $notesGUI = new ilNoteGUI($this->object->getObjId(), $this->object->getId(), 'quest'); 217 $notesGUI->enablePublicNotes(true); 218 $notesGUI->enablePublicNotesDeletion(true); 219 220 return $notesGUI->getNotesHTML(); 221 } 222 223 /** 224 * execute command 225 */ 226 public function executeCommand() 227 { 228 global $DIC; /* @var \ILIAS\DI\Container $DIC */ 229 $ilHelp = $DIC['ilHelp']; /* @var ilHelpGUI $ilHelp */ 230 $ilHelp->setScreenIdComponent('qpl'); 231 232 $cmd = $this->ctrl->getCmd("editQuestion"); 233 $next_class = $this->ctrl->getNextClass($this); 234 235 $cmd = $this->getCommand($cmd); 236 237 switch ($next_class) { 238 case 'ilformpropertydispatchgui': 239 $form = $this->buildEditForm(); 240 241 require_once 'Services/Form/classes/class.ilFormPropertyDispatchGUI.php'; 242 $form_prop_dispatch = new ilFormPropertyDispatchGUI(); 243 $form_prop_dispatch->setItem($form->getItemByPostVar(ilUtil::stripSlashes($_GET['postvar']))); 244 return $this->ctrl->forwardCommand($form_prop_dispatch); 245 break; 246 247 default: 248 $ret = $this->$cmd(); 249 break; 250 } 251 return $ret; 252 } 253 254 public function getCommand($cmd) 255 { 256 return $cmd; 257 } 258 259 /** 260 * needed for page editor compliance 261 */ 262 public function getType() 263 { 264 return $this->getQuestionType(); 265 } 266 267 /** 268 * @return string 269 */ 270 public function getPresentationContext() 271 { 272 return $this->presentationContext; 273 } 274 275 /** 276 * @param string $presentationContext 277 */ 278 public function setPresentationContext($presentationContext) 279 { 280 $this->presentationContext = $presentationContext; 281 } 282 283 public function isTestPresentationContext() 284 { 285 return $this->getPresentationContext() == self::PRESENTATION_CONTEXT_TEST; 286 } 287 288 // hey: previousPassSolutions - setter/getter for Previous Solution Prefilled flag 289 /** 290 * @return boolean 291 */ 292 public function isPreviousSolutionPrefilled() 293 { 294 return $this->previousSolutionPrefilled; 295 } 296 297 /** 298 * @param boolean $previousSolutionPrefilled 299 */ 300 public function setPreviousSolutionPrefilled($previousSolutionPrefilled) 301 { 302 $this->previousSolutionPrefilled = $previousSolutionPrefilled; 303 } 304 // hey. 305 306 /** 307 * @return string 308 */ 309 public function getRenderPurpose() 310 { 311 return $this->renderPurpose; 312 } 313 314 /** 315 * @param string $renderPurpose 316 */ 317 public function setRenderPurpose($renderPurpose) 318 { 319 $this->renderPurpose = $renderPurpose; 320 } 321 322 public function isRenderPurposePrintPdf() 323 { 324 return $this->getRenderPurpose() == self::RENDER_PURPOSE_PRINT_PDF; 325 } 326 327 public function isRenderPurposePreview() 328 { 329 return $this->getRenderPurpose() == self::RENDER_PURPOSE_PREVIEW; 330 } 331 332 public function isRenderPurposeInputValue() 333 { 334 return $this->getRenderPurpose() == self::RENDER_PURPOSE_INPUT_VALUE; 335 } 336 337 public function isRenderPurposePlayback() 338 { 339 return $this->getRenderPurpose() == self::RENDER_PURPOSE_PLAYBACK; 340 } 341 342 public function isRenderPurposeDemoplay() 343 { 344 return $this->getRenderPurpose() == self::RENDER_PURPOSE_DEMOPLAY; 345 } 346 347 public function renderPurposeSupportsFormHtml() 348 { 349 if ($this->isRenderPurposePrintPdf()) { 350 return false; 351 } 352 353 if ($this->isRenderPurposeInputValue()) { 354 return false; 355 } 356 357 return true; 358 } 359 360 /** 361 * @return string 362 */ 363 public function getEditContext() 364 { 365 return $this->editContext; 366 } 367 368 /** 369 * @param string $editContext 370 */ 371 public function setEditContext($editContext) 372 { 373 $this->editContext = $editContext; 374 } 375 376 /** 377 * @param bool $isAuthoringEditContext 378 */ 379 public function isAuthoringEditContext() 380 { 381 return $this->getEditContext() == self::EDIT_CONTEXT_AUTHORING; 382 } 383 384 /** 385 * @param bool $isAdjustmentEditContext 386 */ 387 public function isAdjustmentEditContext() 388 { 389 return $this->getEditContext() == self::EDIT_CONTEXT_ADJUSTMENT; 390 } 391 392 public function setAdjustmentEditContext() 393 { 394 return $this->setEditContext(self::EDIT_CONTEXT_ADJUSTMENT); 395 } 396 397 /** 398 * @return ilTestQuestionNavigationGUI 399 */ 400 public function getNavigationGUI() 401 { 402 return $this->navigationGUI; 403 } 404 405 /** 406 * @param ilTestQuestionNavigationGUI $navigationGUI 407 */ 408 public function setNavigationGUI($navigationGUI) 409 { 410 $this->navigationGUI = $navigationGUI; 411 } 412 413 public function setTaxonomyIds($taxonomyIds) 414 { 415 $this->taxonomyIds = $taxonomyIds; 416 } 417 418 public function getTaxonomyIds() 419 { 420 return $this->taxonomyIds; 421 } 422 423 public function setTargetGui($linkTargetGui) 424 { 425 $this->setTargetGuiClass(get_class($linkTargetGui)); 426 } 427 428 public function setTargetGuiClass($targetGuiClass) 429 { 430 $this->targetGuiClass = $targetGuiClass; 431 } 432 433 public function getTargetGuiClass() 434 { 435 return $this->targetGuiClass; 436 } 437 438 /** 439 * @param \ilQuestionHeaderBlockBuilder $questionHeaderBlockBuilder 440 */ 441 public function setQuestionHeaderBlockBuilder($questionHeaderBlockBuilder) 442 { 443 $this->questionHeaderBlockBuilder = $questionHeaderBlockBuilder; 444 } 445 446 // fau: testNav - get the question header block bulder (for tweaking) 447 /** 448 * @return \ilQuestionHeaderBlockBuilder $questionHeaderBlockBuilder 449 */ 450 public function getQuestionHeaderBlockBuilder() 451 { 452 return $this->questionHeaderBlockBuilder; 453 } 454 // fau. 455 456 public function setQuestionActionCmd($questionActionCmd) 457 { 458 $this->questionActionCmd = $questionActionCmd; 459 460 if (is_object($this->object)) { 461 $this->object->questionActionCmd = $questionActionCmd; 462 } 463 } 464 465 public function getQuestionActionCmd() 466 { 467 return $this->questionActionCmd; 468 } 469 470 /** 471 * Evaluates a posted edit form and writes the form data in the question object 472 * @return integer A positive value, if one of the required fields wasn't set, else 0 473 */ 474 protected function writePostData($always = false) 475 { 476 } 477 478 /** 479 * output assessment 480 */ 481 public function assessment() 482 { 483 /** 484 * @var $tpl ilGlobalTemplate 485 */ 486 global $DIC; 487 $tpl = $DIC['tpl']; 488 489 require_once 'Modules/TestQuestionPool/classes/tables/class.ilQuestionCumulatedStatisticsTableGUI.php'; 490 $stats_table = new ilQuestionCumulatedStatisticsTableGUI($this, 'assessment', '', $this->object); 491 492 require_once 'Modules/TestQuestionPool/classes/tables/class.ilQuestionUsagesTableGUI.php'; 493 $usage_table = new ilQuestionUsagesTableGUI($this, 'assessment', '', $this->object); 494 495 $tpl->setContent(implode('<br />', array( 496 $stats_table->getHTML(), 497 $usage_table->getHTML() 498 ))); 499 } 500 501 /** 502 * Creates a question gui representation and returns the alias to the question gui 503 * note: please do not use $this inside this method to allow static calls 504 * 505 * @param string $question_type The question type as it is used in the language database 506 * @param integer $question_id The database ID of an existing question to load it into assQuestionGUI 507 * 508 * @return assQuestionGUI The alias to the question object 509 */ 510 public static function _getQuestionGUI($question_type, $question_id = -1) 511 { 512 global $DIC; 513 $ilCtrl = $DIC['ilCtrl']; 514 $ilDB = $DIC['ilDB']; 515 $lng = $DIC['lng']; 516 517 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 518 519 if ((!$question_type) and ($question_id > 0)) { 520 $question_type = assQuestion::getQuestionTypeFromDb($question_id); 521 } 522 523 if (strlen($question_type) == 0) { 524 return null; 525 } 526 527 assQuestion::_includeClass($question_type, 1); 528 529 $question_type_gui = assQuestion::getGuiClassNameByQuestionType($question_type); 530 $question = new $question_type_gui(); 531 532 $feedbackObjectClassname = assQuestion::getFeedbackClassNameByQuestionType($question_type); 533 $question->object->feedbackOBJ = new $feedbackObjectClassname($question->object, $ilCtrl, $ilDB, $lng); 534 535 if ($question_id > 0) { 536 $question->object->loadFromDb($question_id); 537 } 538 539 return $question; 540 } 541 542 /** 543 * @deprecated 544 */ 545 public static function _getGUIClassNameForId($a_q_id) 546 { 547 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 548 include_once "./Modules/TestQuestionPool/classes/class.assQuestionGUI.php"; 549 $q_type = assQuestion::getQuestionTypeFromDb($a_q_id); 550 $class_name = assQuestionGUI::_getClassNameForQType($q_type); 551 return $class_name; 552 } 553 554 /** 555 * @deprecated 556 */ 557 public static function _getClassNameForQType($q_type) 558 { 559 return $q_type . "GUI"; 560 } 561 562 /** 563 * Creates a question gui representation 564 * 565 * Creates a question gui representation and returns the alias to the question gui 566 * 567 * @param string $question_type The question type as it is used in the language database 568 * @param integer $question_id The database ID of an existing question to load it into assQuestionGUI 569 * @return object The alias to the question object 570 * @access public 571 * 572 * @deprecated: WTF is this? GUIobject::question should be a GUIobject !? WTF is a question alias !? 573 */ 574 public function &createQuestionGUI($question_type, $question_id = -1) 575 { 576 include_once "./Modules/TestQuestionPool/classes/class.assQuestionGUI.php"; 577 $this->question = &assQuestionGUI::_getQuestionGUI($question_type, $question_id); 578 } 579 580 public function populateJavascriptFilesRequiredForWorkForm(ilGlobalTemplateInterface $tpl) 581 { 582 foreach ($this->getPresentationJavascripts() as $jsFile) { 583 $tpl->addJavaScript($jsFile); 584 } 585 } 586 587 public function getPresentationJavascripts() 588 { 589 return array(); 590 } 591 592 /** 593 * get question template 594 */ 595 public function getQuestionTemplate() 596 { 597 // @todo Björn: Maybe this has to be changed for PHP 7/ILIAS 5.2.x (ilObjTestGUI::executeCommand, switch -> default case -> $this->prepareOutput(); already added a template to the CONTENT variable wrapped in a block named content) 598 if (!$this->tpl->blockExists('content')) { 599 $this->tpl->addBlockFile("CONTENT", "content", "tpl.il_as_qpl_content.html", "Modules/TestQuestionPool"); 600 } 601 // @todo Björn: Maybe this has to be changed for PHP 7/ILIAS 5.2.x (ilObjTestGUI::executeCommand, switch -> default case -> $this->prepareOutput(); already added a template to the STATUSLINE variable wrapped in a block named statusline) 602 if (!$this->tpl->blockExists('statusline')) { 603 $this->tpl->addBlockFile("STATUSLINE", "statusline", "tpl.statusline.html"); 604 } 605 // @todo Björn: Maybe this has to be changed for PHP 7/ILIAS 5.2.x because ass[XYZ]QuestionGUI::editQuestion is called multiple times 606 if (!$this->tpl->blockExists('adm_content')) { 607 $this->tpl->addBlockFile("ADM_CONTENT", "adm_content", "tpl.il_as_question.html", "Modules/TestQuestionPool"); 608 } 609 } 610 611 /** 612 * @param $form 613 */ 614 protected function renderEditForm($form) 615 { 616 $this->getQuestionTemplate(); 617 $this->tpl->setVariable("QUESTION_DATA", $form->getHTML()); 618 } 619 620 /** 621 * Returns the ILIAS Page around a question 622 * 623 * @return string The ILIAS page content 624 * @access public 625 */ 626 public function getILIASPage($html = "") 627 { 628 include_once("./Modules/TestQuestionPool/classes/class.ilAssQuestionPageGUI.php"); 629 $page_gui = new ilAssQuestionPageGUI($this->object->getId()); 630 $page_gui->setQuestionHTML(array($this->object->getId() => $html)); 631 $presentation = $page_gui->presentation(); 632 $presentation = preg_replace("/src=\"\\.\\//ims", "src=\"" . ILIAS_HTTP_PATH . "/", $presentation); 633 return $presentation; 634 } 635 636 /** 637 * output question page 638 */ 639 public function outQuestionPage($a_temp_var, $a_postponed = false, $active_id = "", $html = "", $inlineFeedbackEnabled = false) 640 { 641 // hey: prevPassSolutions - add the "use previous answer" 642 // hey: prevPassSolutions - refactored identifiers 643 if ($this->object->getTestPresentationConfig()->isSolutionInitiallyPrefilled()) { 644 // hey 645 ilUtil::sendInfo($this->getPreviousSolutionProvidedMessage()); 646 $html .= $this->getPreviousSolutionConfirmationCheckboxHtml(); 647 } elseif // (!) --> if ($this->object->getTestQuestionConfig()->isUnchangedAnswerPossible()) 648 // hey. 649// fau: testNav - add the "use unchanged answer checkbox" 650 // hey: prevPassSolutions - refactored identifiers 651 ($this->object->getTestPresentationConfig()->isUnchangedAnswerPossible()) { 652 // hey. 653 $html .= $this->getUseUnchangedAnswerCheckboxHtml(); 654 } 655 // fau. 656 657 $this->lng->loadLanguageModule("content"); 658 659 // fau: testNav - add question buttons below question, add actions menu 660 include_once("./Modules/TestQuestionPool/classes/class.ilAssQuestionPageGUI.php"); 661 $page_gui = new ilAssQuestionPageGUI($this->object->getId()); 662 $page_gui->setOutputMode("presentation"); 663 $page_gui->setTemplateTargetVar($a_temp_var); 664 665 if ($this->getNavigationGUI()) { 666 $html .= $this->getNavigationGUI()->getHTML(); 667 $page_gui->setQuestionActionsHTML($this->getNavigationGUI()->getActionsHTML()); 668 } 669 // fau. 670 671 if (strlen($html)) { 672 if ($inlineFeedbackEnabled && $this->hasInlineFeedback()) { 673 $html = $this->buildFocusAnchorHtml() . $html; 674 } 675 676 $page_gui->setQuestionHTML(array($this->object->getId() => $html)); 677 } 678 679 // fau: testNav - fill the header with subtitle blocks for question info an actions 680 $page_gui->setPresentationTitle($this->questionHeaderBlockBuilder->getPresentationTitle()); 681 $page_gui->setQuestionInfoHTML($this->questionHeaderBlockBuilder->getQuestionInfoHTML()); 682 // fau. 683 684 return $page_gui->presentation(); 685 } 686 687 // fau: testNav - get the html of the "use unchanged answer checkbox" 688 protected function getUseUnchangedAnswerCheckboxHtml() 689 { 690 // hey: prevPassSolutions - use abstracted template to share with other purposes of this kind 691 $tpl = new ilTemplate('tpl.tst_question_additional_behaviour_checkbox.html', true, true, 'Modules/TestQuestionPool'); 692 $tpl->setVariable('TXT_FORCE_FORM_DIFF_LABEL', $this->object->getTestPresentationConfig()->getUseUnchangedAnswerLabel()); 693 // hey. 694 return $tpl->get(); 695 } 696 // fau. 697 698 // hey: prevPassSolutions - build prev solution message / build "use previous answer checkbox" html 699 protected function getPreviousSolutionProvidedMessage() 700 { 701 return $this->lng->txt('use_previous_solution_advice'); 702 } 703 704 protected function getPreviousSolutionConfirmationCheckboxHtml() 705 { 706 $tpl = new ilTemplate('tpl.tst_question_additional_behaviour_checkbox.html', true, true, 'Modules/TestQuestionPool'); 707 // hey: prevPassSolutions - use abtract template 708 $tpl->setVariable('TXT_FORCE_FORM_DIFF_LABEL', $this->lng->txt('use_previous_solution')); 709 // hey. 710 return $tpl->get(); 711 } 712 // hey. 713 714 /** 715 * cancel action 716 */ 717 public function cancel() 718 { 719 if ($_GET["calling_test"]) { 720 $_GET["ref_id"] = $_GET["calling_test"]; 721 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $_GET["calling_test"]); 722 } elseif ($_GET["test_ref_id"]) { 723 $_GET["ref_id"] = $_GET["test_ref_id"]; 724 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $_GET["test_ref_id"]); 725 } else { 726 if ($_GET["q_id"] > 0) { 727 $this->ctrl->setParameterByClass("ilAssQuestionPageGUI", "q_id", $_GET["q_id"]); 728 $this->ctrl->redirectByClass("ilAssQuestionPageGUI", "edit"); 729 } else { 730 $this->ctrl->redirectByClass("ilobjquestionpoolgui", "questions"); 731 } 732 } 733 } 734 735 /** 736 * @param string $return_to 737 * @param string $return_to_feedback ilAssQuestionFeedbackEditingGUI 738 */ 739 public function originalSyncForm($return_to = "", $return_to_feedback = '') 740 { 741 if (strlen($return_to)) { 742 $this->ctrl->setParameter($this, "return_to", $return_to); 743 } elseif ($_REQUEST['return_to']) { 744 $this->ctrl->setParameter($this, "return_to", $_REQUEST['return_to']); 745 } 746 if (strlen($return_to_feedback)) { 747 $this->ctrl->setParameter($this, 'return_to_fb', 'true'); 748 } 749 750 $this->ctrl->saveParameter($this, 'test_express_mode'); 751 752 $template = new ilTemplate("tpl.il_as_qpl_sync_original.html", true, true, "Modules/TestQuestionPool"); 753 $template->setVariable("BUTTON_YES", $this->lng->txt("yes")); 754 $template->setVariable("BUTTON_NO", $this->lng->txt("no")); 755 $template->setVariable("FORM_ACTION", $this->ctrl->getFormAction($this)); 756 $template->setVariable("TEXT_SYNC", $this->lng->txt("confirm_sync_questions")); 757 $this->tpl->setVariable("ADM_CONTENT", $template->get()); 758 } 759 760 public function sync() 761 { 762 $original_id = $this->object->original_id; 763 if ($original_id) { 764 $this->object->syncWithOriginal(); 765 } 766 if (strlen($_GET["return_to"])) { 767 $this->ctrl->redirect($this, $_GET["return_to"]); 768 } 769 if (strlen($_REQUEST["return_to_fb"])) { 770 $this->ctrl->redirectByClass('ilAssQuestionFeedbackEditingGUI', 'showFeedbackForm'); 771 } else { 772 if (isset($_GET['calling_consumer']) && (int) $_GET['calling_consumer']) { 773 $ref_id = (int) $_GET['calling_consumer']; 774 $consumer = ilObjectFactory::getInstanceByRefId($ref_id); 775 if ($consumer instanceof ilQuestionEditingFormConsumer) { 776 ilUtil::redirect($consumer->getQuestionEditingFormBackTarget($_GET['consumer_context'])); 777 } 778 require_once 'Services/Link/classes/class.ilLink.php'; 779 ilUtil::redirect(ilLink::_getLink($ref_id)); 780 } 781 $_GET["ref_id"] = $_GET["calling_test"]; 782 783 if ($_REQUEST['test_express_mode']) { 784 ilUtil::redirect(ilTestExpressPage::getReturnToPageLink($this->object->getId())); 785 } else { 786 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $_GET["calling_test"]); 787 } 788 } 789 } 790 791 public function cancelSync() 792 { 793 if (strlen($_GET["return_to"])) { 794 $this->ctrl->redirect($this, $_GET["return_to"]); 795 } 796 if (strlen($_REQUEST['return_to_fb'])) { 797 $this->ctrl->redirectByClass('ilAssQuestionFeedbackEditingGUI', 'showFeedbackForm'); 798 } else { 799 if (isset($_GET['calling_consumer']) && (int) $_GET['calling_consumer']) { 800 $ref_id = (int) $_GET['calling_consumer']; 801 $consumer = ilObjectFactory::getInstanceByRefId($ref_id); 802 if ($consumer instanceof ilQuestionEditingFormConsumer) { 803 ilUtil::redirect($consumer->getQuestionEditingFormBackTarget($_GET['consumer_context'])); 804 } 805 require_once 'Services/Link/classes/class.ilLink.php'; 806 ilUtil::redirect(ilLink::_getLink($ref_id)); 807 } 808 $_GET["ref_id"] = $_GET["calling_test"]; 809 810 if ($_REQUEST['test_express_mode']) { 811 ilUtil::redirect(ilTestExpressPage::getReturnToPageLink($this->object->getId())); 812 } else { 813 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $_GET["calling_test"]); 814 } 815 } 816 } 817 818 /** 819 * save question 820 */ 821 public function saveEdit() 822 { 823 global $DIC; 824 $ilUser = $DIC['ilUser']; 825 826 $result = $this->writePostData(); 827 if ($result == 0) { 828 $ilUser->setPref("tst_lastquestiontype", $this->object->getQuestionType()); 829 $ilUser->writePref("tst_lastquestiontype", $this->object->getQuestionType()); 830 $this->object->saveToDb(); 831 $originalexists = $this->object->_questionExists($this->object->original_id); 832 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 833 if ($_GET["calling_test"] && $originalexists && assQuestion::_isWriteable($this->object->original_id, $ilUser->getId())) { 834 $this->ctrl->redirect($this, "originalSyncForm"); 835 } elseif ($_GET["calling_test"]) { 836 $_GET["ref_id"] = $_GET["calling_test"]; 837 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $_GET["calling_test"]); 838 return; 839 } elseif ($_GET["test_ref_id"]) { 840 global $DIC; 841 $tree = $DIC['tree']; 842 $ilDB = $DIC['ilDB']; 843 $ilPluginAdmin = $DIC['ilPluginAdmin']; 844 845 include_once("./Modules/Test/classes/class.ilObjTest.php"); 846 $_GET["ref_id"] = $_GET["test_ref_id"]; 847 $test = new ilObjTest($_GET["test_ref_id"], true); 848 849 require_once 'Modules/Test/classes/class.ilTestQuestionSetConfigFactory.php'; 850 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $ilPluginAdmin, $test); 851 852 $test->insertQuestion($testQuestionSetConfigFactory->getQuestionSetConfig(), $this->object->getId()); 853 854 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $_GET["test_ref_id"]); 855 } else { 856 $this->ctrl->setParameter($this, "q_id", $this->object->getId()); 857 $this->editQuestion(); 858 if (strcmp($_SESSION["info"], "") != 0) { 859 ilUtil::sendSuccess($_SESSION["info"] . "<br />" . $this->lng->txt("msg_obj_modified"), false); 860 } else { 861 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), false); 862 } 863 $this->ctrl->setParameterByClass("ilAssQuestionPageGUI", "q_id", $this->object->getId()); 864 $this->ctrl->redirectByClass("ilAssQuestionPageGUI", "edit"); 865 } 866 } 867 } 868 869 /** 870 * save question 871 */ 872 public function save() 873 { 874 global $DIC; 875 $ilUser = $DIC['ilUser']; 876 $old_id = $_GET["q_id"]; 877 $result = $this->writePostData(); 878 879 if ($result == 0) { 880 $ilUser->setPref("tst_lastquestiontype", $this->object->getQuestionType()); 881 $ilUser->writePref("tst_lastquestiontype", $this->object->getQuestionType()); 882 $this->object->saveToDb(); 883 $originalexists = $this->object->_questionExistsInPool($this->object->original_id); 884 885 886 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 887 if (($_GET["calling_test"] || (isset($_GET['calling_consumer']) && (int) $_GET['calling_consumer'])) && $originalexists && assQuestion::_isWriteable($this->object->original_id, $ilUser->getId())) { 888 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), true); 889 $this->ctrl->setParameter($this, 'return_to', 'editQuestion'); 890 $this->ctrl->redirect($this, "originalSyncForm"); 891 return; 892 } elseif ($_GET["calling_test"]) { 893 require_once 'Modules/Test/classes/class.ilObjTest.php'; 894 $test = new ilObjTest($_GET["calling_test"]); 895 if (!assQuestion::_questionExistsInTest($this->object->getId(), $test->getTestId())) { 896 global $DIC; 897 $tree = $DIC['tree']; 898 $ilDB = $DIC['ilDB']; 899 $ilPluginAdmin = $DIC['ilPluginAdmin']; 900 901 include_once("./Modules/Test/classes/class.ilObjTest.php"); 902 $_GET["ref_id"] = $_GET["calling_test"]; 903 $test = new ilObjTest($_GET["calling_test"], true); 904 905 require_once 'Modules/Test/classes/class.ilTestQuestionSetConfigFactory.php'; 906 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $ilPluginAdmin, $test); 907 908 $new_id = $test->insertQuestion( 909 $testQuestionSetConfigFactory->getQuestionSetConfig(), 910 $this->object->getId() 911 ); 912 913 if (isset($_REQUEST['prev_qid'])) { 914 $test->moveQuestionAfter($this->object->getId() + 1, $_REQUEST['prev_qid']); 915 } 916 917 $this->ctrl->setParameter($this, 'q_id', $new_id); 918 $this->ctrl->setParameter($this, 'calling_test', $_GET['calling_test']); 919 #$this->ctrl->setParameter($this, 'test_ref_id', false); 920 } 921 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), true); 922 $this->ctrl->redirect($this, 'editQuestion'); 923 } else { 924 $this->callNewIdListeners($this->object->getId()); 925 926 if ($this->object->getId() != $old_id) { 927 // first save 928 $this->ctrl->setParameterByClass($_GET["cmdClass"], "q_id", $this->object->getId()); 929 $this->ctrl->setParameterByClass($_GET["cmdClass"], "sel_question_types", $_GET["sel_question_types"]); 930 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), true); 931 932 //global $___test_express_mode; 933 /** 934 * in express mode, so add question to test directly 935 */ 936 if ($_REQUEST['prev_qid']) { 937 // @todo: bheyser/mbecker wtf? ..... thx@jposselt .... 938 // mbecker: Possible fix: Just instantiate the obj? 939 include_once("./Modules/Test/classes/class.ilObjTest.php"); 940 $test = new ilObjTest($_GET["ref_id"], true); 941 $test->moveQuestionAfter($_REQUEST['prev_qid'], $this->object->getId()); 942 } 943 if ( /*$___test_express_mode || */ $_REQUEST['express_mode']) { 944 global $DIC; 945 $tree = $DIC['tree']; 946 $ilDB = $DIC['ilDB']; 947 $ilPluginAdmin = $DIC['ilPluginAdmin']; 948 949 include_once("./Modules/Test/classes/class.ilObjTest.php"); 950 $test = new ilObjTest($_GET["ref_id"], true); 951 952 require_once 'Modules/Test/classes/class.ilTestQuestionSetConfigFactory.php'; 953 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $ilPluginAdmin, $test); 954 955 $test->insertQuestion( 956 $testQuestionSetConfigFactory->getQuestionSetConfig(), 957 $this->object->getId() 958 ); 959 960 require_once 'Modules/Test/classes/class.ilTestExpressPage.php'; 961 $_REQUEST['q_id'] = $this->object->getId(); 962 ilUtil::redirect(ilTestExpressPage::getReturnToPageLink()); 963 } 964 965 $this->ctrl->redirectByClass($_GET["cmdClass"], "editQuestion"); 966 } 967 if (strcmp($_SESSION["info"], "") != 0) { 968 ilUtil::sendSuccess($_SESSION["info"] . "<br />" . $this->lng->txt("msg_obj_modified"), true); 969 } else { 970 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), true); 971 } 972 $this->ctrl->redirect($this, 'editQuestion'); 973 } 974 } 975 } 976 977 /** 978 * save question 979 */ 980 public function saveReturn() 981 { 982 global $DIC; 983 $ilUser = $DIC['ilUser']; 984 $old_id = $_GET["q_id"]; 985 $result = $this->writePostData(); 986 if ($result == 0) { 987 $ilUser->setPref("tst_lastquestiontype", $this->object->getQuestionType()); 988 $ilUser->writePref("tst_lastquestiontype", $this->object->getQuestionType()); 989 $this->object->saveToDb(); 990 $originalexists = $this->object->_questionExistsInPool($this->object->original_id); 991 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 992 if (($_GET["calling_test"] || (isset($_GET['calling_consumer']) && (int) $_GET['calling_consumer'])) && $originalexists && assQuestion::_isWriteable($this->object->original_id, $ilUser->getId())) { 993 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), true); 994 $this->ctrl->setParameter($this, 'test_express_mode', $_REQUEST['test_express_mode']); 995 $this->ctrl->redirect($this, "originalSyncForm"); 996 return; 997 } elseif ($_GET["calling_test"]) { 998 require_once 'Modules/Test/classes/class.ilObjTest.php'; 999 $test = new ilObjTest($_GET["calling_test"]); 1000 #var_dump(assQuestion::_questionExistsInTest($this->object->getId(), $test->getTestId())); 1001 $q_id = $this->object->getId(); 1002 if (!assQuestion::_questionExistsInTest($this->object->getId(), $test->getTestId())) { 1003 global $DIC; 1004 $tree = $DIC['tree']; 1005 $ilDB = $DIC['ilDB']; 1006 $ilPluginAdmin = $DIC['ilPluginAdmin']; 1007 1008 include_once("./Modules/Test/classes/class.ilObjTest.php"); 1009 $_GET["ref_id"] = $_GET["calling_test"]; 1010 $test = new ilObjTest($_GET["calling_test"], true); 1011 1012 require_once 'Modules/Test/classes/class.ilTestQuestionSetConfigFactory.php'; 1013 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $ilPluginAdmin, $test); 1014 1015 $new_id = $test->insertQuestion( 1016 $testQuestionSetConfigFactory->getQuestionSetConfig(), 1017 $this->object->getId() 1018 ); 1019 1020 $q_id = $new_id; 1021 if (isset($_REQUEST['prev_qid'])) { 1022 $test->moveQuestionAfter($this->object->getId() + 1, $_REQUEST['prev_qid']); 1023 } 1024 1025 $this->ctrl->setParameter($this, 'q_id', $new_id); 1026 $this->ctrl->setParameter($this, 'calling_test', $_GET['calling_test']); 1027 #$this->ctrl->setParameter($this, 'test_ref_id', false); 1028 } 1029 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), true); 1030 if ( /*$___test_express_mode || */ 1031 $_REQUEST['test_express_mode'] 1032 ) { 1033 ilUtil::redirect(ilTestExpressPage::getReturnToPageLink($q_id)); 1034 } else { 1035 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $_GET["calling_test"]); 1036 } 1037 } else { 1038 if ($this->object->getId() != $old_id) { 1039 $this->callNewIdListeners($this->object->getId()); 1040 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), true); 1041 $this->ctrl->redirectByClass("ilobjquestionpoolgui", "questions"); 1042 } 1043 if (strcmp($_SESSION["info"], "") != 0) { 1044 ilUtil::sendSuccess($_SESSION["info"] . "<br />" . $this->lng->txt("msg_obj_modified"), true); 1045 } else { 1046 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), true); 1047 } 1048 $this->ctrl->redirectByClass("ilobjquestionpoolgui", "questions"); 1049 } 1050 } 1051 } 1052 1053 /** 1054 * apply changes 1055 */ 1056 public function apply() 1057 { 1058 $this->writePostData(); 1059 $this->object->saveToDb(); 1060 $this->ctrl->setParameter($this, "q_id", $this->object->getId()); 1061 $this->editQuestion(); 1062 } 1063 1064 /** 1065 * get context path in content object tree 1066 * 1067 * @param int $a_endnode_id id of endnode 1068 * @param int $a_startnode_id id of startnode 1069 */ 1070 public function getContextPath($cont_obj, $a_endnode_id, $a_startnode_id = 1) 1071 { 1072 $path = ""; 1073 1074 $tmpPath = $cont_obj->getLMTree()->getPathFull($a_endnode_id, $a_startnode_id); 1075 1076 // count -1, to exclude the learning module itself 1077 for ($i = 1; $i < (count($tmpPath) - 1); $i++) { 1078 if ($path != "") { 1079 $path .= " > "; 1080 } 1081 1082 $path .= $tmpPath[$i]["title"]; 1083 } 1084 1085 return $path; 1086 } 1087 1088 public function setSequenceNumber($nr) 1089 { 1090 $this->sequence_no = $nr; 1091 } 1092 1093 public function getSequenceNumber() 1094 { 1095 return $this->sequence_no; 1096 } 1097 1098 public function setQuestionCount($a_question_count) 1099 { 1100 $this->question_count = $a_question_count; 1101 } 1102 1103 public function getQuestionCount() 1104 { 1105 return $this->question_count; 1106 } 1107 1108 public function getErrorMessage() 1109 { 1110 return $this->errormessage; 1111 } 1112 1113 public function setErrorMessage($errormessage) 1114 { 1115 $this->errormessage = $errormessage; 1116 } 1117 1118 public function addErrorMessage($errormessage) 1119 { 1120 $this->errormessage .= ((strlen($this->errormessage)) ? "<br />" : "") . $errormessage; 1121 } 1122 1123 public function outAdditionalOutput() 1124 { 1125 } 1126 1127 /** 1128 * Returns the question type string 1129 * 1130 * Returns the question type string 1131 * 1132 * @result string The question type string 1133 * @access public 1134 */ 1135 public function getQuestionType() 1136 { 1137 return $this->object->getQuestionType(); 1138 } 1139 1140 /** 1141 * Returns a HTML value attribute 1142 * 1143 * @param mixed $a_value A given text or value 1144 * @result string The value as HTML value attribute 1145 * @access public 1146 */ 1147 public function getAsValueAttribute($a_value) 1148 { 1149 $result = ""; 1150 if (strlen($a_value)) { 1151 $result = " value=\"$a_value\" "; 1152 } 1153 return $result; 1154 } 1155 1156 // scorm2004-start 1157 /** 1158 * Add a listener that is notified with the new question ID, when 1159 * a new question is saved 1160 */ 1161 public function addNewIdListener(&$a_object, $a_method, $a_parameters = "") 1162 { 1163 $cnt = $this->new_id_listener_cnt; 1164 $this->new_id_listeners[$cnt]["object"] = &$a_object; 1165 $this->new_id_listeners[$cnt]["method"] = $a_method; 1166 $this->new_id_listeners[$cnt]["parameters"] = $a_parameters; 1167 $this->new_id_listener_cnt++; 1168 } 1169 1170 /** 1171 * Call the new id listeners 1172 */ 1173 public function callNewIdListeners($a_new_id) 1174 { 1175 for ($i = 0; $i < $this->new_id_listener_cnt; $i++) { 1176 $this->new_id_listeners[$i]["parameters"]["new_id"] = $a_new_id; 1177 $object = &$this->new_id_listeners[$i]["object"]; 1178 $method = $this->new_id_listeners[$i]["method"]; 1179 $parameters = $this->new_id_listeners[$i]["parameters"]; 1180 //var_dump($object); 1181 //var_dump($method); 1182 //var_dump($parameters); 1183 1184 $object->$method($parameters); 1185 } 1186 } 1187 1188 /** 1189 * Add the command buttons of a question properties form 1190 */ 1191 public function addQuestionFormCommandButtons($form) 1192 { 1193 //if (!$this->object->getSelfAssessmentEditingMode() && !$_GET["calling_test"]) $form->addCommandButton("saveEdit", $this->lng->txt("save_edit")); 1194 if (!$this->object->getSelfAssessmentEditingMode()) { 1195 $form->addCommandButton("saveReturn", $this->lng->txt("save_return")); 1196 } 1197 $form->addCommandButton("save", $this->lng->txt("save")); 1198 } 1199 1200 /** 1201 * Add basic question form properties: 1202 * assessment: title, author, description, question, working time 1203 * 1204 * @return int Default Nr of Tries 1205 */ 1206 public function addBasicQuestionFormProperties($form) 1207 { 1208 // title 1209 $title = new ilTextInputGUI($this->lng->txt("title"), "title"); 1210 $title->setMaxLength(100); 1211 $title->setValue($this->object->getTitle()); 1212 $title->setRequired(true); 1213 $form->addItem($title); 1214 1215 if (!$this->object->getSelfAssessmentEditingMode()) { 1216 // author 1217 $author = new ilTextInputGUI($this->lng->txt("author"), "author"); 1218 $author->setValue($this->object->getAuthor()); 1219 $author->setRequired(true); 1220 $form->addItem($author); 1221 1222 // description 1223 $description = new ilTextInputGUI($this->lng->txt("description"), "comment"); 1224 $description->setValue($this->object->getComment()); 1225 $description->setRequired(false); 1226 $form->addItem($description); 1227 } else { 1228 // author as hidden field 1229 $hi = new ilHiddenInputGUI("author"); 1230 $author = ilUtil::prepareFormOutput($this->object->getAuthor()); 1231 if (trim($author) == "") { 1232 $author = "-"; 1233 } 1234 $hi->setValue($author); 1235 $form->addItem($hi); 1236 } 1237 1238 // lifecycle 1239 $lifecycle = new ilSelectInputGUI($this->lng->txt('qst_lifecycle'), 'lifecycle'); 1240 $lifecycle->setOptions($this->object->getLifecycle()->getSelectOptions($this->lng)); 1241 $lifecycle->setValue($this->object->getLifecycle()->getIdentifier()); 1242 $form->addItem($lifecycle); 1243 1244 // questiontext 1245 $question = new ilTextAreaInputGUI($this->lng->txt("question"), "question"); 1246 $question->setValue($this->object->getQuestion()); 1247 $question->setRequired(true); 1248 $question->setRows(10); 1249 $question->setCols(80); 1250 1251 if (!$this->object->getSelfAssessmentEditingMode()) { 1252 if ($this->object->getAdditionalContentEditingMode() != assQuestion::ADDITIONAL_CONTENT_EDITING_MODE_PAGE_OBJECT) { 1253 $question->setUseRte(true); 1254 include_once "./Services/AdvancedEditing/classes/class.ilObjAdvancedEditing.php"; 1255 $question->setRteTags(ilObjAdvancedEditing::_getUsedHTMLTags("assessment")); 1256 $question->addPlugin("latex"); 1257 $question->addButton("latex"); 1258 $question->addButton("pastelatex"); 1259 $question->setRTESupport($this->object->getId(), "qpl", "assessment"); 1260 } 1261 } else { 1262 require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssSelfAssessmentQuestionFormatter.php'; 1263 $question->setRteTags(ilAssSelfAssessmentQuestionFormatter::getSelfAssessmentTags()); 1264 $question->setUseTagsForRteOnly(false); 1265 } 1266 $form->addItem($question); 1267 1268 if (!$this->object->getSelfAssessmentEditingMode()) { 1269 // duration 1270 $duration = new ilDurationInputGUI($this->lng->txt("working_time"), "Estimated"); 1271 $duration->setShowHours(true); 1272 $duration->setShowMinutes(true); 1273 $duration->setShowSeconds(true); 1274 $ewt = $this->object->getEstimatedWorkingTime(); 1275 $duration->setHours($ewt["h"]); 1276 $duration->setMinutes($ewt["m"]); 1277 $duration->setSeconds($ewt["s"]); 1278 $duration->setRequired(false); 1279 $form->addItem($duration); 1280 } else { 1281 // number of tries 1282 if (strlen($this->object->getNrOfTries())) { 1283 $nr_tries = $this->object->getNrOfTries(); 1284 } else { 1285 $nr_tries = $this->object->getDefaultNrOfTries(); 1286 } 1287 if ($nr_tries < 1) { 1288 $nr_tries = ""; 1289 } 1290 1291 $ni = new ilNumberInputGUI($this->lng->txt("qst_nr_of_tries"), "nr_of_tries"); 1292 $ni->setValue($nr_tries); 1293 $ni->setMinValue(0); 1294 $ni->setSize(5); 1295 $ni->setMaxLength(5); 1296 $form->addItem($ni); 1297 } 1298 } 1299 1300 protected function saveTaxonomyAssignments() 1301 { 1302 if (count($this->getTaxonomyIds())) { 1303 require_once 'Services/Taxonomy/classes/class.ilTaxAssignInputGUI.php'; 1304 1305 foreach ($this->getTaxonomyIds() as $taxonomyId) { 1306 $postvar = "tax_node_assign_$taxonomyId"; 1307 1308 $tax_node_assign = new ilTaxAssignInputGUI($taxonomyId, true, '', $postvar); 1309 // TODO: determine tst/qpl when tax assigns become maintainable within tests 1310 $tax_node_assign->saveInput("qpl", $this->object->getObjId(), "quest", $this->object->getId()); 1311 } 1312 } 1313 } 1314 1315 protected function populateTaxonomyFormSection(ilPropertyFormGUI $form) 1316 { 1317 if (count($this->getTaxonomyIds())) { 1318 // this is needed by ilTaxSelectInputGUI in some cases 1319 require_once 'Services/UIComponent/Overlay/classes/class.ilOverlayGUI.php'; 1320 ilOverlayGUI::initJavaScript(); 1321 1322 $sectHeader = new ilFormSectionHeaderGUI(); 1323 $sectHeader->setTitle($this->lng->txt('qpl_qst_edit_form_taxonomy_section')); 1324 $form->addItem($sectHeader); 1325 1326 require_once 'Services/Taxonomy/classes/class.ilTaxSelectInputGUI.php'; 1327 1328 foreach ($this->getTaxonomyIds() as $taxonomyId) { 1329 $taxonomy = new ilObjTaxonomy($taxonomyId); 1330 $label = sprintf($this->lng->txt('qpl_qst_edit_form_taxonomy'), $taxonomy->getTitle()); 1331 $postvar = "tax_node_assign_$taxonomyId"; 1332 1333 $taxSelect = new ilTaxSelectInputGUI($taxonomy->getId(), $postvar, true); 1334 $taxSelect->setTitle($label); 1335 1336 require_once 'Services/Taxonomy/classes/class.ilTaxNodeAssignment.php'; 1337 $taxNodeAssignments = new ilTaxNodeAssignment(ilObject::_lookupType($this->object->getObjId()), $this->object->getObjId(), 'quest', $taxonomyId); 1338 $assignedNodes = $taxNodeAssignments->getAssignmentsOfItem($this->object->getId()); 1339 1340 $taxSelect->setValue(array_map(function ($assignedNode) { 1341 return $assignedNode['node_id']; 1342 }, $assignedNodes)); 1343 $form->addItem($taxSelect); 1344 } 1345 } 1346 } 1347 1348 /** 1349 * Returns the answer generic feedback depending on the results of the question 1350 * 1351 * @deprecated Use getGenericFeedbackOutput instead. 1352 * @param integer $active_id Active ID of the user 1353 * @param integer $pass Active pass 1354 * @return string HTML Code with the answer specific feedback 1355 * @access public 1356 */ 1357 public function getAnswerFeedbackOutput($active_id, $pass) 1358 { 1359 return $this->getGenericFeedbackOutput($active_id, $pass); 1360 } 1361 1362 /** 1363 * Returns the answer specific feedback for the question 1364 1365 * 1366 * @param integer $active_id Active ID of the user 1367 * @param integer $pass Active pass 1368 * @return string HTML Code with the answer specific feedback 1369 * @access public 1370 */ 1371 public function getGenericFeedbackOutput($active_id, $pass) 1372 { 1373 $output = ""; 1374 include_once "./Modules/Test/classes/class.ilObjTest.php"; 1375 $manual_feedback = ilObjTest::getManualFeedback($active_id, $this->object->getId(), $pass); 1376 if (strlen($manual_feedback)) { 1377 return $manual_feedback; 1378 } 1379 $correct_feedback = $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), true); 1380 $incorrect_feedback = $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), false); 1381 if (strlen($correct_feedback . $incorrect_feedback)) { 1382 $reached_points = $this->object->calculateReachedPoints($active_id, $pass); 1383 $max_points = $this->object->getMaximumPoints(); 1384 if ($reached_points == $max_points) { 1385 $output = $correct_feedback; 1386 } else { 1387 $output = $incorrect_feedback; 1388 } 1389 } 1390 return $this->object->prepareTextareaOutput($output, true); 1391 } 1392 1393 public function getGenericFeedbackOutputForCorrectSolution() 1394 { 1395 return $this->object->prepareTextareaOutput( 1396 $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), true), 1397 true 1398 ); 1399 } 1400 1401 public function getGenericFeedbackOutputForIncorrectSolution() 1402 { 1403 return $this->object->prepareTextareaOutput( 1404 $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), false), 1405 true 1406 ); 1407 } 1408 1409 /** 1410 * Returns the answer specific feedback for the question 1411 * 1412 * This method should be overwritten by the actual question. 1413 * 1414 * @todo Mark this method abstract! 1415 * @param array $userSolution ($userSolution[<value1>] = <value2>) 1416 * @return string HTML Code with the answer specific feedback 1417 * @access public 1418 */ 1419 abstract public function getSpecificFeedbackOutput($userSolution); 1420 1421 public function outQuestionType() 1422 { 1423 $count = $this->object->isInUse(); 1424 1425 if ($this->object->_questionExistsInPool($this->object->getId()) && $count) { 1426 global $DIC; 1427 $rbacsystem = $DIC['rbacsystem']; 1428 if ($rbacsystem->checkAccess("write", $_GET["ref_id"])) { 1429 ilUtil::sendInfo(sprintf($this->lng->txt("qpl_question_is_in_use"), $count)); 1430 } 1431 } 1432 1433 return assQuestion::_getQuestionTypeName($this->object->getQuestionType()); 1434 } 1435 1436 public function showSuggestedSolution() 1437 { 1438 $this->suggestedsolution(); 1439 } 1440 1441 /** 1442 * Allows to add suggested solutions for questions 1443 * 1444 * @access public 1445 */ 1446 public function suggestedsolution() 1447 { 1448 global $DIC; 1449 $ilUser = $DIC['ilUser']; 1450 global $DIC; 1451 $ilAccess = $DIC['ilAccess']; 1452 1453 $save = (is_array($_POST["cmd"]) && array_key_exists("suggestedsolution", $_POST["cmd"])) ? true : false; 1454 1455 if ($save && $_POST["deleteSuggestedSolution"] == 1) { 1456 $this->object->deleteSuggestedSolutions(); 1457 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), true); 1458 $this->ctrl->redirect($this, "suggestedsolution"); 1459 } 1460 1461 $output = ""; 1462 $solution_array = $this->object->getSuggestedSolution(0); 1463 $options = array( 1464 "lm" => $this->lng->txt("obj_lm"), 1465 "st" => $this->lng->txt("obj_st"), 1466 "pg" => $this->lng->txt("obj_pg"), 1467 "git" => $this->lng->txt("glossary_term"), 1468 "file" => $this->lng->txt("fileDownload"), 1469 "text" => $this->lng->txt("solutionText") 1470 ); 1471 1472 if ((strcmp($_POST["solutiontype"], "file") == 0) && (strcmp($solution_array["type"], "file") != 0)) { 1473 $solution_array = array( 1474 "type" => "file" 1475 ); 1476 } elseif ((strcmp($_POST["solutiontype"], "text") == 0) && (strcmp($solution_array["type"], "text") != 0)) { 1477 $oldOutputMode = $this->getRenderPurpose(); 1478 $this->setRenderPurpose(self::RENDER_PURPOSE_INPUT_VALUE); 1479 1480 $solution_array = array( 1481 "type" => "text", 1482 "value" => $this->getSolutionOutput(0, null, false, false, true, false, true) 1483 ); 1484 $this->setRenderPurpose($oldsaveSuggestedSolutionOutputMode); 1485 } 1486 if ($save && strlen($_POST["filename"])) { 1487 $solution_array["value"]["filename"] = $_POST["filename"]; 1488 } 1489 if ($save && strlen($_POST["solutiontext"])) { 1490 $solution_array["value"] = $_POST["solutiontext"]; 1491 } 1492 include_once("./Services/Form/classes/class.ilPropertyFormGUI.php"); 1493 if (count($solution_array)) { 1494 $form = new ilPropertyFormGUI(); 1495 $form->setFormAction($this->ctrl->getFormAction($this)); 1496 $form->setTitle($this->lng->txt("solution_hint")); 1497 $form->setMultipart(true); 1498 $form->setTableWidth("100%"); 1499 $form->setId("suggestedsolutiondisplay"); 1500 1501 // suggested solution output 1502 include_once "./Modules/TestQuestionPool/classes/class.ilSolutionTitleInputGUI.php"; 1503 $title = new ilSolutionTitleInputGUI($this->lng->txt("showSuggestedSolution"), "solutiontype"); 1504 $template = new ilTemplate("tpl.il_as_qpl_suggested_solution_input_presentation.html", true, true, "Modules/TestQuestionPool"); 1505 if (strlen($solution_array["internal_link"])) { 1506 $href = assQuestion::_getInternalLinkHref($solution_array["internal_link"]); 1507 $template->setCurrentBlock("preview"); 1508 $template->setVariable("TEXT_SOLUTION", $this->lng->txt("suggested_solution")); 1509 $template->setVariable("VALUE_SOLUTION", " <a href=\"$href\" target=\"content\">" . $this->lng->txt("view") . "</a> "); 1510 $template->parseCurrentBlock(); 1511 } elseif ((strcmp($solution_array["type"], "file") == 0) && (is_array($solution_array["value"]))) { 1512 $href = $this->object->getSuggestedSolutionPathWeb() . $solution_array["value"]["name"]; 1513 $template->setCurrentBlock("preview"); 1514 $template->setVariable("TEXT_SOLUTION", $this->lng->txt("suggested_solution")); 1515 $template->setVariable("VALUE_SOLUTION", " <a href=\"$href\" target=\"content\">" . ilUtil::prepareFormOutput((strlen($solution_array["value"]["filename"])) ? $solution_array["value"]["filename"] : $solution_array["value"]["name"]) . "</a> "); 1516 $template->parseCurrentBlock(); 1517 } 1518 $template->setVariable("TEXT_TYPE", $this->lng->txt("type")); 1519 $template->setVariable("VALUE_TYPE", $options[$solution_array["type"]]); 1520 $title->setHtml($template->get()); 1521 $deletesolution = new ilCheckboxInputGUI("", "deleteSuggestedSolution"); 1522 $deletesolution->setOptionTitle($this->lng->txt("deleteSuggestedSolution")); 1523 $title->addSubItem($deletesolution); 1524 $form->addItem($title); 1525 1526 if (strcmp($solution_array["type"], "file") == 0) { 1527 // file 1528 $file = new ilFileInputGUI($this->lng->txt("fileDownload"), "file"); 1529 $file->setRequired(true); 1530 $file->enableFileNameSelection("filename"); 1531 //$file->setSuffixes(array("doc","xls","png","jpg","gif","pdf")); 1532 if ($_FILES["file"]["tmp_name"] && $file->checkInput()) { 1533 if (!file_exists($this->object->getSuggestedSolutionPath())) { 1534 ilUtil::makeDirParents($this->object->getSuggestedSolutionPath()); 1535 } 1536 1537 $res = ilUtil::moveUploadedFile($_FILES["file"]["tmp_name"], $_FILES["file"]["name"], $this->object->getSuggestedSolutionPath() . $_FILES["file"]["name"]); 1538 if ($res) { 1539 ilUtil::renameExecutables($this->object->getSuggestedSolutionPath()); 1540 1541 // remove an old file download 1542 if (is_array($solution_array["value"])) { 1543 @unlink($this->object->getSuggestedSolutionPath() . $solution_array["value"]["name"]); 1544 } 1545 $file->setValue($_FILES["file"]["name"]); 1546 $this->object->saveSuggestedSolution("file", "", 0, array("name" => $_FILES["file"]["name"], "type" => $_FILES["file"]["type"], "size" => $_FILES["file"]["size"], "filename" => $_POST["filename"])); 1547 $originalexists = $this->object->_questionExistsInPool($this->object->original_id); 1548 if (($_GET["calling_test"] || (isset($_GET['calling_consumer']) && (int) $_GET['calling_consumer'])) && $originalexists && assQuestion::_isWriteable($this->object->original_id, $ilUser->getId())) { 1549 return $this->originalSyncForm("suggestedsolution"); 1550 } else { 1551 ilUtil::sendSuccess($this->lng->txt("suggested_solution_added_successfully"), true); 1552 $this->ctrl->redirect($this, "suggestedsolution"); 1553 } 1554 } else { 1555 // BH: $res as info string? wtf? it holds a bool or something else!!? 1556 ilUtil::sendInfo($res); 1557 } 1558 } else { 1559 if (is_array($solution_array["value"])) { 1560 $file->setValue($solution_array["value"]["name"]); 1561 $file->setFilename((strlen($solution_array["value"]["filename"])) ? $solution_array["value"]["filename"] : $solution_array["value"]["name"]); 1562 } 1563 } 1564 $form->addItem($file); 1565 $hidden = new ilHiddenInputGUI("solutiontype"); 1566 $hidden->setValue("file"); 1567 $form->addItem($hidden); 1568 } elseif (strcmp($solution_array["type"], "text") == 0) { 1569 $solutionContent = $solution_array['value']; 1570 $solutionContent = $this->object->fixSvgToPng($solutionContent); 1571 $solutionContent = $this->object->fixUnavailableSkinImageSources($solutionContent); 1572 $question = new ilTextAreaInputGUI($this->lng->txt("solutionText"), "solutiontext"); 1573 $question->setValue($this->object->prepareTextareaOutput($solutionContent)); 1574 $question->setRequired(true); 1575 $question->setRows(10); 1576 $question->setCols(80); 1577 $question->setUseRte(true); 1578 $question->addPlugin("latex"); 1579 $question->addButton("latex"); 1580 $question->setRTESupport($this->object->getId(), "qpl", "assessment"); 1581 $hidden = new ilHiddenInputGUI("solutiontype"); 1582 $hidden->setValue("text"); 1583 $form->addItem($hidden); 1584 $form->addItem($question); 1585 } 1586 if ($ilAccess->checkAccess("write", "", $_GET['ref_id'])) { 1587 $form->addCommandButton('showSuggestedSolution', $this->lng->txt('cancel')); 1588 $form->addCommandButton('suggestedsolution', $this->lng->txt('save')); 1589 } 1590 1591 if ($save) { 1592 if ($form->checkInput()) { 1593 switch ($solution_array["type"]) { 1594 case "file": 1595 $this->object->saveSuggestedSolution("file", "", 0, array( 1596 "name" => $solution_array["value"]["name"], 1597 "type" => $solution_array["value"]["type"], 1598 "size" => $solution_array["value"]["size"], 1599 "filename" => $_POST["filename"] 1600 )); 1601 break; 1602 case "text": 1603 $this->object->saveSuggestedSolution("text", "", 0, $solution_array["value"]); 1604 break; 1605 } 1606 $originalexists = $this->object->_questionExistsInPool($this->object->original_id); 1607 if (($_GET["calling_test"] || (isset($_GET['calling_consumer']) && (int) $_GET['calling_consumer'])) && $originalexists && assQuestion::_isWriteable($this->object->original_id, $ilUser->getId())) { 1608 return $this->originalSyncForm("suggestedsolution"); 1609 } else { 1610 ilUtil::sendSuccess($this->lng->txt("msg_obj_modified"), true); 1611 $this->ctrl->redirect($this, "suggestedsolution"); 1612 } 1613 } 1614 } 1615 1616 $output = $form->getHTML(); 1617 } 1618 1619 $savechange = (strcmp($this->ctrl->getCmd(), "saveSuggestedSolution") == 0) ? true : false; 1620 1621 $changeoutput = ""; 1622 if ($ilAccess->checkAccess("write", "", $_GET['ref_id'])) { 1623 $formchange = new ilPropertyFormGUI(); 1624 $formchange->setFormAction($this->ctrl->getFormAction($this)); 1625 $formchange->setTitle((count($solution_array)) ? $this->lng->txt("changeSuggestedSolution") : $this->lng->txt("addSuggestedSolution")); 1626 $formchange->setMultipart(false); 1627 $formchange->setTableWidth("100%"); 1628 $formchange->setId("suggestedsolution"); 1629 1630 $solutiontype = new ilRadioGroupInputGUI($this->lng->txt("suggestedSolutionType"), "solutiontype"); 1631 foreach ($options as $opt_value => $opt_caption) { 1632 $solutiontype->addOption(new ilRadioOption($opt_caption, $opt_value)); 1633 } 1634 if (count($solution_array)) { 1635 $solutiontype->setValue($solution_array["type"]); 1636 } 1637 $solutiontype->setRequired(true); 1638 $formchange->addItem($solutiontype); 1639 1640 $formchange->addCommandButton("saveSuggestedSolution", $this->lng->txt("select")); 1641 1642 if ($savechange) { 1643 $formchange->checkInput(); 1644 } 1645 $changeoutput = $formchange->getHTML(); 1646 } 1647 1648 $this->tpl->setVariable("ADM_CONTENT", $changeoutput . $output); 1649 } 1650 1651 public function outSolutionExplorer() 1652 { 1653 global $DIC; 1654 $tree = $DIC['tree']; 1655 1656 include_once("./Modules/TestQuestionPool/classes/class.ilSolutionExplorer.php"); 1657 $type = $_GET["link_new_type"]; 1658 $search = $_GET["search_link_type"]; 1659 $this->ctrl->setParameter($this, "link_new_type", $type); 1660 $this->ctrl->setParameter($this, "search_link_type", $search); 1661 $this->ctrl->saveParameter($this, array("subquestion_index", "link_new_type", "search_link_type")); 1662 1663 ilUtil::sendInfo($this->lng->txt("select_object_to_link")); 1664 1665 $parent_ref_id = $tree->getParentId($_GET["ref_id"]); 1666 $exp = new ilSolutionExplorer($this->ctrl->getLinkTarget($this, 'suggestedsolution'), get_class($this)); 1667 $exp->setExpand($_GET['expand_sol'] ? $_GET['expand_sol'] : $parent_ref_id); 1668 $exp->setExpandTarget($this->ctrl->getLinkTarget($this, 'outSolutionExplorer')); 1669 $exp->setTargetGet("ref_id"); 1670 $exp->setRefId($_GET["ref_id"]); 1671 $exp->addFilter($type); 1672 $exp->setSelectableType($type); 1673 if (isset($_GET['expandCurrentPath']) && $_GET['expandCurrentPath']) { 1674 $exp->expandPathByRefId($parent_ref_id); 1675 } 1676 1677 // build html-output 1678 $exp->setOutput(0); 1679 1680 $template = new ilTemplate("tpl.il_as_qpl_explorer.html", true, true, "Modules/TestQuestionPool"); 1681 $template->setVariable("EXPLORER_TREE", $exp->getOutput()); 1682 $template->setVariable("BUTTON_CANCEL", $this->lng->txt("cancel")); 1683 $template->setVariable("FORMACTION", $this->ctrl->getFormAction($this, "suggestedsolution")); 1684 $this->tpl->setVariable("ADM_CONTENT", $template->get()); 1685 } 1686 1687 public function saveSuggestedSolution() 1688 { 1689 global $DIC; 1690 $tree = $DIC['tree']; 1691 1692 include_once("./Modules/TestQuestionPool/classes/class.ilSolutionExplorer.php"); 1693 switch ($_POST["solutiontype"]) { 1694 case "lm": 1695 $type = "lm"; 1696 $search = "lm"; 1697 break; 1698 case "git": 1699 $type = "glo"; 1700 $search = "glo"; 1701 break; 1702 case "st": 1703 $type = "lm"; 1704 $search = "st"; 1705 break; 1706 case "pg": 1707 $type = "lm"; 1708 $search = "pg"; 1709 break; 1710 case "file": 1711 case "text": 1712 return $this->suggestedsolution(); 1713 break; 1714 default: 1715 return $this->suggestedsolution(); 1716 break; 1717 } 1718 if (isset($_POST['solutiontype'])) { 1719 $this->ctrl->setParameter($this, 'expandCurrentPath', 1); 1720 } 1721 $this->ctrl->setParameter($this, "link_new_type", $type); 1722 $this->ctrl->setParameter($this, "search_link_type", $search); 1723 $this->ctrl->redirect($this, "outSolutionExplorer"); 1724 } 1725 1726 public function cancelExplorer() 1727 { 1728 $this->ctrl->redirect($this, "suggestedsolution"); 1729 } 1730 1731 public function outPageSelector() 1732 { 1733 require_once 'Modules/TestQuestionPool/classes/tables/class.ilQuestionInternalLinkSelectionTableGUI.php'; 1734 require_once 'Modules/LearningModule/classes/class.ilLMPageObject.php'; 1735 require_once 'Modules/LearningModule/classes/class.ilObjContentObjectGUI.php'; 1736 1737 $this->ctrl->setParameter($this, 'q_id', $this->object->getId()); 1738 1739 $cont_obj_gui = new ilObjContentObjectGUI('', $_GET['source_id'], true); 1740 $cont_obj = $cont_obj_gui->object; 1741 $pages = ilLMPageObject::getPageList($cont_obj->getId()); 1742 $shownpages = array(); 1743 $tree = $cont_obj->getLMTree(); 1744 $chapters = $tree->getSubtree($tree->getNodeData($tree->getRootId())); 1745 1746 $rows = array(); 1747 1748 foreach ($chapters as $chapter) { 1749 $chapterpages = $tree->getChildsByType($chapter['obj_id'], 'pg'); 1750 foreach ($chapterpages as $page) { 1751 if ($page['type'] == $_GET['search_link_type']) { 1752 array_push($shownpages, $page['obj_id']); 1753 1754 if ($tree->isInTree($page['obj_id'])) { 1755 $path_str = $this->getContextPath($cont_obj, $page['obj_id']); 1756 } else { 1757 $path_str = '---'; 1758 } 1759 1760 $this->ctrl->setParameter($this, $page['type'], $page['obj_id']); 1761 $rows[] = array( 1762 'title' => $page['title'], 1763 'description' => ilUtil::prepareFormOutput($path_str), 1764 'text_add' => $this->lng->txt('add'), 1765 'href_add' => $this->ctrl->getLinkTarget($this, 'add' . strtoupper($page['type'])) 1766 ); 1767 } 1768 } 1769 } 1770 foreach ($pages as $page) { 1771 if (!in_array($page['obj_id'], $shownpages)) { 1772 $this->ctrl->setParameter($this, $page['type'], $page['obj_id']); 1773 $rows[] = array( 1774 'title' => $page['title'], 1775 'description' => '---', 1776 'text_add' => $this->lng->txt('add'), 1777 'href_add' => $this->ctrl->getLinkTarget($this, 'add' . strtoupper($page['type'])) 1778 ); 1779 } 1780 } 1781 1782 require_once 'Modules/TestQuestionPool/classes/tables/class.ilQuestionInternalLinkSelectionTableGUI.php'; 1783 $table = new ilQuestionInternalLinkSelectionTableGUI($this, 'cancelExplorer', __METHOD__); 1784 $table->setTitle($this->lng->txt('obj_' . ilUtil::stripSlashes($_GET['search_link_type']))); 1785 $table->setData($rows); 1786 1787 $this->tpl->setContent($table->getHTML()); 1788 } 1789 1790 public function outChapterSelector() 1791 { 1792 require_once 'Modules/TestQuestionPool/classes/tables/class.ilQuestionInternalLinkSelectionTableGUI.php'; 1793 require_once 'Modules/LearningModule/classes/class.ilObjContentObjectGUI.php'; 1794 1795 $this->ctrl->setParameter($this, 'q_id', $this->object->getId()); 1796 1797 $cont_obj_gui = new ilObjContentObjectGUI('', $_GET['source_id'], true); 1798 $cont_obj = $cont_obj_gui->object; 1799 $ctree = $cont_obj->getLMTree(); 1800 $nodes = $ctree->getSubtree($ctree->getNodeData($ctree->getRootId())); 1801 1802 $rows = array(); 1803 1804 foreach ($nodes as $node) { 1805 if ($node['type'] == $_GET['search_link_type']) { 1806 $this->ctrl->setParameter($this, $node['type'], $node['obj_id']); 1807 $rows[] = array( 1808 'title' => $node['title'], 1809 'description' => '', 1810 'text_add' => $this->lng->txt('add'), 1811 'href_add' => $this->ctrl->getLinkTarget($this, 'add' . strtoupper($node['type'])) 1812 ); 1813 } 1814 } 1815 1816 $table = new ilQuestionInternalLinkSelectionTableGUI($this, 'cancelExplorer', __METHOD__); 1817 $table->setTitle($this->lng->txt('obj_' . ilUtil::stripSlashes($_GET['search_link_type']))); 1818 $table->setData($rows); 1819 1820 $this->tpl->setContent($table->getHTML()); 1821 } 1822 1823 public function outGlossarySelector() 1824 { 1825 $this->ctrl->setParameter($this, 'q_id', $this->object->getId()); 1826 1827 $glossary = new ilObjGlossary($_GET['source_id'], true); 1828 $terms = $glossary->getTermList(); 1829 1830 $rows = array(); 1831 1832 foreach ($terms as $term) { 1833 $this->ctrl->setParameter($this, 'git', $term['id']); 1834 $rows[] = array( 1835 'title' => $term['term'], 1836 'description' => '', 1837 'text_add' => $this->lng->txt('add'), 1838 'href_add' => $this->ctrl->getLinkTarget($this, 'addGIT') 1839 ); 1840 } 1841 1842 $table = new ilQuestionInternalLinkSelectionTableGUI($this, 'cancelExplorer', __METHOD__); 1843 $table->setTitle($this->lng->txt('glossary_term')); 1844 $table->setData($rows); 1845 1846 $this->tpl->setContent($table->getHTML()); 1847 } 1848 1849 public function linkChilds() 1850 { 1851 $this->ctrl->saveParameter($this, array("subquestion_index", "link_new_type", "search_link_type")); 1852 switch ($_GET["search_link_type"]) { 1853 case "pg": 1854 return $this->outPageSelector(); 1855 break; 1856 case "st": 1857 return $this->outChapterSelector(); 1858 break; 1859 case "glo": 1860 return $this->outGlossarySelector(); 1861 break; 1862 case "lm": 1863 $subquestion_index = ($_GET["subquestion_index"] > 0) ? $_GET["subquestion_index"] : 0; 1864 $this->object->saveSuggestedSolution("lm", "il__lm_" . $_GET["source_id"], $subquestion_index); 1865 ilUtil::sendSuccess($this->lng->txt("suggested_solution_added_successfully"), true); 1866 $this->ctrl->redirect($this, "suggestedsolution"); 1867 break; 1868 } 1869 } 1870 1871 public function addPG() 1872 { 1873 $subquestion_index = 0; 1874 if (strlen($_GET["subquestion_index"]) && $_GET["subquestion_index"] >= 0) { 1875 $subquestion_index = $_GET["subquestion_index"]; 1876 } 1877 $this->object->saveSuggestedSolution("pg", "il__pg_" . $_GET["pg"], $subquestion_index); 1878 ilUtil::sendSuccess($this->lng->txt("suggested_solution_added_successfully"), true); 1879 $this->ctrl->redirect($this, "suggestedsolution"); 1880 } 1881 1882 public function addST() 1883 { 1884 $subquestion_index = 0; 1885 if (strlen($_GET["subquestion_index"]) && $_GET["subquestion_index"] >= 0) { 1886 $subquestion_index = $_GET["subquestion_index"]; 1887 } 1888 $this->object->saveSuggestedSolution("st", "il__st_" . $_GET["st"], $subquestion_index); 1889 ilUtil::sendSuccess($this->lng->txt("suggested_solution_added_successfully"), true); 1890 $this->ctrl->redirect($this, "suggestedsolution"); 1891 } 1892 1893 public function addGIT() 1894 { 1895 $subquestion_index = 0; 1896 if (strlen($_GET["subquestion_index"]) && $_GET["subquestion_index"] >= 0) { 1897 $subquestion_index = $_GET["subquestion_index"]; 1898 } 1899 $this->object->saveSuggestedSolution("git", "il__git_" . $_GET["git"], $subquestion_index); 1900 ilUtil::sendSuccess($this->lng->txt("suggested_solution_added_successfully"), true); 1901 $this->ctrl->redirect($this, "suggestedsolution"); 1902 } 1903 1904 public function isSaveCommand() 1905 { 1906 return in_array($this->ctrl->getCmd(), array('save', 'saveEdit', 'saveReturn')); 1907 } 1908 1909 /** 1910 * extracts values of all constants of given class with given prefix as array 1911 * can be used to get all possible commands in case of these commands are defined as constants 1912 * 1913 * @param string $guiClassName 1914 * @param string $cmdConstantNameBegin 1915 * @return array 1916 */ 1917 public static function getCommandsFromClassConstants($guiClassName, $cmdConstantNameBegin = 'CMD_') 1918 { 1919 $reflectionClass = new ReflectionClass($guiClassName); 1920 1921 $commands = null; 1922 1923 if ($reflectionClass instanceof ReflectionClass) { 1924 $commands = array(); 1925 1926 foreach ($reflectionClass->getConstants() as $constName => $constValue) { 1927 if (substr($constName, 0, strlen($cmdConstantNameBegin)) == $cmdConstantNameBegin) { 1928 $commands[] = $constValue; 1929 } 1930 } 1931 } 1932 1933 return $commands; 1934 } 1935 1936 public function setQuestionTabs() 1937 { 1938 global $DIC; 1939 $rbacsystem = $DIC['rbacsystem']; 1940 $ilTabs = $DIC['ilTabs']; 1941 1942 $ilTabs->clearTargets(); 1943 1944 $this->ctrl->setParameterByClass("ilAssQuestionPageGUI", "q_id", $_GET["q_id"]); 1945 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 1946 $q_type = $this->object->getQuestionType(); 1947 1948 if (strlen($q_type)) { 1949 $classname = $q_type . "GUI"; 1950 $this->ctrl->setParameterByClass(strtolower($classname), "sel_question_types", $q_type); 1951 $this->ctrl->setParameterByClass(strtolower($classname), "q_id", $_GET["q_id"]); 1952 } 1953 1954 if ($_GET["q_id"]) { 1955 if ($rbacsystem->checkAccess('write', $_GET["ref_id"])) { 1956 // edit page 1957 $ilTabs->addTarget( 1958 "edit_page", 1959 $this->ctrl->getLinkTargetByClass("ilAssQuestionPageGUI", "edit"), 1960 array("edit", "insert", "exec_pg"), 1961 "", 1962 "", 1963 $force_active 1964 ); 1965 } 1966 1967 $this->addTab_QuestionPreview($ilTabs); 1968 } 1969 $force_active = false; 1970 if ($rbacsystem->checkAccess('write', $_GET["ref_id"])) { 1971 $url = ""; 1972 if ($classname) { 1973 $url = $this->ctrl->getLinkTargetByClass($classname, "editQuestion"); 1974 } 1975 $force_active = false; 1976 // edit question properties 1977 $ilTabs->addTarget( 1978 "edit_question", 1979 $url, 1980 $this->getEditQuestionTabCommands(), 1981 $classname, 1982 "", 1983 $force_active 1984 ); 1985 } 1986 1987 // add tab for question feedback within common class assQuestionGUI 1988 $this->addTab_QuestionFeedback($ilTabs); 1989 1990 // add tab for question hint within common class assQuestionGUI 1991 $this->addTab_QuestionHints($ilTabs); 1992 1993 // add tab for question's suggested solution within common class assQuestionGUI 1994 $this->addTab_SuggestedSolution($ilTabs, $classname); 1995 1996 // Assessment of questions sub menu entry 1997 if ($_GET["q_id"]) { 1998 $ilTabs->addTarget( 1999 "statistics", 2000 $this->ctrl->getLinkTargetByClass($classname, "assessment"), 2001 array("assessment"), 2002 $classname, 2003 "" 2004 ); 2005 } 2006 2007 $this->addBackTab($ilTabs); 2008 } 2009 2010 public function addTab_SuggestedSolution(ilTabsGUI $tabs, $classname) 2011 { 2012 if ($_GET["q_id"]) { 2013 $tabs->addTarget( 2014 "suggested_solution", 2015 $this->ctrl->getLinkTargetByClass($classname, "suggestedsolution"), 2016 array("suggestedsolution", "saveSuggestedSolution", "outSolutionExplorer", "cancel", 2017 "addSuggestedSolution","cancelExplorer", "linkChilds", "removeSuggestedSolution" 2018 ), 2019 $classname, 2020 "" 2021 ); 2022 } 2023 } 2024 2025 final public function getEditQuestionTabCommands() 2026 { 2027 return array_merge($this->getBasicEditQuestionTabCommands(), $this->getAdditionalEditQuestionCommands()); 2028 } 2029 2030 protected function getBasicEditQuestionTabCommands() 2031 { 2032 return array('editQuestion', 'save', 'saveEdit', 'originalSyncForm'); 2033 } 2034 2035 protected function getAdditionalEditQuestionCommands() 2036 { 2037 return array(); 2038 } 2039 2040 /** 2041 * adds the feedback tab to ilTabsGUI 2042 * 2043 * @global ilCtrl $ilCtrl 2044 * @param ilTabsGUI $tabs 2045 */ 2046 protected function addTab_QuestionFeedback(ilTabsGUI $tabs) 2047 { 2048 global $DIC; 2049 $ilCtrl = $DIC['ilCtrl']; 2050 2051 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionFeedbackEditingGUI.php'; 2052 $tabCommands = self::getCommandsFromClassConstants('ilAssQuestionFeedbackEditingGUI'); 2053 2054 $tabLink = $ilCtrl->getLinkTargetByClass('ilAssQuestionFeedbackEditingGUI', ilAssQuestionFeedbackEditingGUI::CMD_SHOW); 2055 2056 $tabs->addTarget('tst_feedback', $tabLink, $tabCommands, $ilCtrl->getCmdClass(), ''); 2057 } 2058 2059 /** 2060 * @param ilTabsGUI $tabs 2061 */ 2062 protected function addTab_Units(ilTabsGUI $tabs) 2063 { 2064 /** 2065 * @var $ilCtrl ilCtrl 2066 */ 2067 global $DIC; 2068 $ilCtrl = $DIC['ilCtrl']; 2069 2070 $tabs->addTarget('units', $ilCtrl->getLinkTargetByClass('ilLocalUnitConfigurationGUI', ''), '', 'illocalunitconfigurationgui'); 2071 } 2072 2073 /** 2074 * adds the hints tab to ilTabsGUI 2075 * 2076 * @global ilCtrl $ilCtrl 2077 * @param ilTabsGUI $tabs 2078 */ 2079 protected function addTab_QuestionHints(ilTabsGUI $tabs) 2080 { 2081 global $DIC; 2082 $ilCtrl = $DIC['ilCtrl']; 2083 2084 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintsGUI.php'; 2085 2086 switch ($ilCtrl->getCmdClass()) { 2087 case 'ilassquestionhintsgui': 2088 2089 $tabCommands = self::getCommandsFromClassConstants('ilAssQuestionHintsGUI'); 2090 break; 2091 2092 case 'ilassquestionhintgui': 2093 2094 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintGUI.php'; 2095 $tabCommands = self::getCommandsFromClassConstants('ilAssQuestionHintGUI'); 2096 break; 2097 2098 default: 2099 2100 $tabCommands = array(); 2101 } 2102 2103 $tabLink = $ilCtrl->getLinkTargetByClass('ilAssQuestionHintsGUI', ilAssQuestionHintsGUI::CMD_SHOW_LIST); 2104 2105 $tabs->addTarget('tst_question_hints_tab', $tabLink, $tabCommands, $ilCtrl->getCmdClass(), ''); 2106 } 2107 2108 protected function addTab_QuestionPreview(ilTabsGUI $tabsGUI) 2109 { 2110 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionPreviewGUI.php'; 2111 2112 $tabsGUI->addTarget( 2113 ilAssQuestionPreviewGUI::TAB_ID_QUESTION_PREVIEW, 2114 $this->ctrl->getLinkTargetByClass('ilAssQuestionPreviewGUI', ilAssQuestionPreviewGUI::CMD_SHOW), 2115 array(), 2116 array('ilAssQuestionPreviewGUI') 2117 ); 2118 } 2119 2120 abstract public function getSolutionOutput( 2121 $active_id, 2122 $pass = null, 2123 $graphicalOutput = false, 2124 $result_output = false, 2125 $show_question_only = true, 2126 $show_feedback = false, 2127 $show_correct_solution = false, 2128 $show_manual_scoring = false, 2129 $show_question_text = true 2130 ); 2131 2132 /** 2133 * Question type specific support of intermediate solution output 2134 * The function getSolutionOutput respects getUseIntermediateSolution() 2135 * @return bool 2136 */ 2137 public function supportsIntermediateSolutionOutput() 2138 { 2139 return false; 2140 } 2141 2142 /** 2143 * Check if the question has an intermediate solution 2144 * @param int $activeId 2145 * @param int $passIndex 2146 * @return bool 2147 */ 2148 public function hasIntermediateSolution($activeId, $passIndex) 2149 { 2150 $result = $this->object->lookupForExistingSolutions($activeId, $passIndex); 2151 return ($result['intermediate']); 2152 } 2153 2154 /** 2155 * Set to use the intermediate solution for solution output 2156 * @var bool $use 2157 */ 2158 public function setUseIntermediateSolution($use) 2159 { 2160 $this->use_intermediate_solution = (bool) $use; 2161 } 2162 2163 /** 2164 * Get if intermediate solution should be used for solution output 2165 * @return bool 2166 */ 2167 public function getUseIntermediateSolution() 2168 { 2169 return (bool) $this->use_intermediate_solution; 2170 } 2171 2172 protected function hasCorrectSolution($activeId, $passIndex) 2173 { 2174 $reachedPoints = $this->object->getAdjustedReachedPoints($activeId, $passIndex, true); 2175 $maximumPoints = $this->object->getMaximumPoints(); 2176 2177 return $reachedPoints == $maximumPoints; 2178 } 2179 2180 public function isAutosaveable() 2181 { 2182 return $this->object->isAutosaveable(); 2183 } 2184 2185 protected function writeQuestionGenericPostData() 2186 { 2187 $this->object->setTitle($_POST["title"]); 2188 $this->object->setAuthor($_POST["author"]); 2189 $this->object->setComment($_POST["comment"]); 2190 if ($this->object->getSelfAssessmentEditingMode()) { 2191 $this->object->setNrOfTries($_POST['nr_of_tries']); 2192 } 2193 2194 try { 2195 $lifecycle = ilAssQuestionLifecycle::getInstance($_POST['lifecycle']); 2196 $this->object->setLifecycle($lifecycle); 2197 } catch (ilTestQuestionPoolInvalidArgumentException $e) { 2198 } 2199 2200 $this->object->setQuestion(ilUtil::stripOnlySlashes($_POST['question'])); // ? 2201 $this->object->setEstimatedWorkingTime( 2202 $_POST["Estimated"]["hh"], 2203 $_POST["Estimated"]["mm"], 2204 $_POST["Estimated"]["ss"] 2205 ); 2206 } 2207 2208 abstract public function getPreview($show_question_only = false, $showInlineFeedback = false); 2209 2210 /** 2211 * @param string $formaction 2212 * @param integer $active_id 2213 * @param integer|null $pass 2214 * @param bool $is_question_postponed 2215 * @param bool $user_post_solutions 2216 * @param bool $show_specific_inline_feedback 2217 */ 2218 final public function outQuestionForTest( 2219 $formaction, 2220 $active_id, 2221 // hey: prevPassSolutions - pass will be always available from now on 2222 $pass, 2223 // hey. 2224 $is_question_postponed = false, 2225 $user_post_solutions = false, 2226 $show_specific_inline_feedback = false 2227 ) { 2228 $formaction = $this->completeTestOutputFormAction($formaction, $active_id, $pass); 2229 2230 $test_output = $this->getTestOutput( 2231 $active_id, 2232 $pass, 2233 $is_question_postponed, 2234 $user_post_solutions, 2235 $show_specific_inline_feedback 2236 ); 2237 2238 $this->magicAfterTestOutput(); 2239 2240 $this->tpl->setVariable("QUESTION_OUTPUT", $test_output); 2241 $this->tpl->setVariable("FORMACTION", $formaction); 2242 $this->tpl->setVariable("ENCTYPE", 'enctype="' . $this->getFormEncodingType() . '"'); 2243 $this->tpl->setVariable("FORM_TIMESTAMP", time()); 2244 } 2245 2246 // hey: prevPassSolutions - $pass will be passed always from now on 2247 protected function completeTestOutputFormAction($formAction, $active_id, $pass) 2248 // hey. 2249 { 2250 return $formAction; 2251 } 2252 2253 public function magicAfterTestOutput() 2254 { 2255 return; 2256 } 2257 2258 abstract public function getTestOutput( 2259 $active_id, 2260 $pass, 2261 $is_question_postponed, 2262 $user_post_solutions, 2263 $show_specific_inline_feedback 2264 ); 2265 2266 public function getFormEncodingType() 2267 { 2268 return self::FORM_ENCODING_URLENCODE; 2269 } 2270 2271 /** 2272 * @param ilTabsGUI $ilTabs 2273 */ 2274 protected function addBackTab(ilTabsGUI $ilTabs) 2275 { 2276 if (($_GET["calling_test"] > 0) || ($_GET["test_ref_id"] > 0)) { 2277 $ref_id = $_GET["calling_test"]; 2278 if (strlen($ref_id) == 0) { 2279 $ref_id = $_GET["test_ref_id"]; 2280 } 2281 2282 if (!$_GET['test_express_mode'] && !$GLOBALS['___test_express_mode']) { 2283 $ilTabs->setBackTarget($this->lng->txt("backtocallingtest"), "ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=$ref_id"); 2284 } else { 2285 $link = ilTestExpressPage::getReturnToPageLink(); 2286 $ilTabs->setBackTarget($this->lng->txt("backtocallingtest"), $link); 2287 } 2288 } elseif (isset($_GET['calling_consumer']) && (int) $_GET['calling_consumer']) { 2289 $ref_id = (int) $_GET['calling_consumer']; 2290 $consumer = ilObjectFactory::getInstanceByRefId($ref_id); 2291 if ($consumer instanceof ilQuestionEditingFormConsumer) { 2292 $ilTabs->setBackTarget($consumer->getQuestionEditingFormBackTargetLabel(), $consumer->getQuestionEditingFormBackTarget($_GET['consumer_context'])); 2293 } else { 2294 require_once 'Services/Link/classes/class.ilLink.php'; 2295 $ilTabs->setBackTarget($this->lng->txt("qpl"), ilLink::_getLink($ref_id)); 2296 } 2297 } else { 2298 $ilTabs->setBackTarget($this->lng->txt("qpl"), $this->ctrl->getLinkTargetByClass("ilobjquestionpoolgui", "questions")); 2299 } 2300 } 2301 2302 /** 2303 * @var ilAssQuestionPreviewSession 2304 */ 2305 private $previewSession; 2306 2307 /** 2308 * @param \ilAssQuestionPreviewSession $previewSession 2309 */ 2310 public function setPreviewSession($previewSession) 2311 { 2312 $this->previewSession = $previewSession; 2313 } 2314 2315 /** 2316 * @return \ilAssQuestionPreviewSession 2317 */ 2318 public function getPreviewSession() 2319 { 2320 return $this->previewSession; 2321 } 2322 2323 /** 2324 * @return ilPropertyFormGUI 2325 */ 2326 protected function buildBasicEditFormObject() 2327 { 2328 require_once 'Services/Form/classes/class.ilPropertyFormGUI.php'; 2329 $form = new ilPropertyFormGUI(); 2330 2331 $form->setFormAction($this->ctrl->getFormAction($this)); 2332 2333 $form->setId($this->getType()); 2334 $form->setTitle($this->outQuestionType()); 2335 2336 $form->setTableWidth('100%'); 2337 2338 $form->setMultipart(true); 2339 2340 return $form; 2341 } 2342 2343 public function showHints() 2344 { 2345 global $DIC; 2346 $ilCtrl = $DIC['ilCtrl']; 2347 $ilCtrl->redirectByClass('ilAssQuestionHintsGUI', ilAssQuestionHintsGUI::CMD_SHOW_LIST); 2348 } 2349 2350 /** 2351 * 2352 */ 2353 protected function buildEditForm() 2354 { 2355 $errors = $this->editQuestion(true); // TODO bheyser: editQuestion should be added to the abstract base class with a unified signature 2356 return $this->editForm; 2357 } 2358 2359 /** 2360 * @return string 2361 */ 2362 public function buildFocusAnchorHtml() 2363 { 2364 return '<div id="focus"></div>'; 2365 } 2366 2367 public function isAnswerFreuqencyStatisticSupported() 2368 { 2369 return true; 2370 } 2371 2372 public function getSubQuestionsIndex() 2373 { 2374 return array(0); 2375 } 2376 2377 public function getAnswersFrequency($relevantAnswers, $questionIndex) 2378 { 2379 return array(); 2380 } 2381 2382 /** 2383 * @param $parentGui 2384 * @param $parentCmd 2385 * @param $relevantAnswers 2386 * @param $questionIndex 2387 * @return ilAnswerFrequencyStatisticTableGUI 2388 */ 2389 public function getAnswerFrequencyTableGUI($parentGui, $parentCmd, $relevantAnswers, $questionIndex) 2390 { 2391 require_once 'Modules/TestQuestionPool/classes/tables/class.ilAnswerFrequencyStatisticTableGUI.php'; 2392 2393 $table = new ilAnswerFrequencyStatisticTableGUI($parentGui, $parentCmd, $this->object); 2394 $table->setQuestionIndex($questionIndex); 2395 $table->setData($this->getAnswersFrequency($relevantAnswers, $questionIndex)); 2396 $table->initColumns(); 2397 2398 return $table; 2399 } 2400 2401 /** 2402 * @param ilPropertyFormGUI $form 2403 */ 2404 public function prepareReprintableCorrectionsForm(ilPropertyFormGUI $form) 2405 { 2406 } 2407 2408 /** 2409 * @param ilPropertyFormGUI $form 2410 */ 2411 public function populateCorrectionsFormProperties(ilPropertyFormGUI $form) 2412 { 2413 } 2414 2415 /** 2416 * @param ilPropertyFormGUI $form 2417 */ 2418 public function saveCorrectionsFormProperties(ilPropertyFormGUI $form) 2419 { 2420 } 2421} 2422