1<?php 2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */ 3 4require_once 'Services/Object/classes/class.ilObject.php'; 5require_once 'Modules/Test/classes/inc.AssessmentConstants.php'; 6require_once 'Modules/Test/interfaces/interface.ilMarkSchemaAware.php'; 7require_once 'Modules/Test/interfaces/interface.ilEctsGradesEnabled.php'; 8require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssQuestionType.php'; 9 10/** 11 * Class ilObjTest 12 * 13 * @author Helmut Schottmüller <helmut.schottmueller@mac.com> 14 * @author Björn Heyser <bheyser@databay.de> 15 * @version $Id$ 16 * 17 * @defgroup ModulesTest Modules/Test 18 * @extends ilObject 19 */ 20class ilObjTest extends ilObject implements ilMarkSchemaAware, ilEctsGradesEnabled 21{ 22 const DEFAULT_PROCESSING_TIME_MINUTES = 90; 23 24 #region Properties 25 26 /** 27 * type setting value for fixed question set 28 */ 29 const QUESTION_SET_TYPE_FIXED = 'FIXED_QUEST_SET'; 30 31 /** 32 * type setting value for random question set 33 */ 34 const QUESTION_SET_TYPE_RANDOM = 'RANDOM_QUEST_SET'; 35 36 /** 37 * type setting value for dynamic question set (continues testing mode) 38 */ 39 const QUESTION_SET_TYPE_DYNAMIC = 'DYNAMIC_QUEST_SET'; 40 41 /** 42 * 43 */ 44 const HIGHSCORE_SHOW_OWN_TABLE = 1; 45 46 /** 47 * 48 */ 49 const HIGHSCORE_SHOW_TOP_TABLE = 2; 50 51 /** 52 * 53 */ 54 const HIGHSCORE_SHOW_ALL_TABLES = 3; 55 56 /** 57 * question set type setting 58 * 59 * @var string 60 */ 61 private $questionSetType = self::QUESTION_SET_TYPE_FIXED; 62 63 /** 64 * @var bool 65 */ 66 private $skillServiceEnabled = false; 67 68 /** 69 * @var array 70 */ 71 private $resultFilterTaxIds = array(); 72 73 /** 74 * Kiosk mode 75 * 76 * Tells wheather the test runs in a kiosk mode or not 77 * 78 * @var integer 79 */ 80 protected $_kiosk; 81 82 /** 83* The database id of the additional test data dataset 84* 85* @var integer 86*/ 87 public $test_id; 88 89 /** 90* Defines if the test will be placed on users personal desktops 91* 92* @var integer 93*/ 94 public $invitation = INVITATION_OFF; 95 96 /** 97* A text representation of the authors name. The name of the author must 98* not necessary be the name of the owner. 99* 100* @var string 101*/ 102 public $author; 103 104 /** 105* A reference to an IMS compatible matadata set 106* 107* @var object 108*/ 109 public $metadata; 110 111 /** 112* An array which contains all the test questions 113* 114* @var array 115*/ 116 public $questions; 117 118 /** 119 * @var bool 120 */ 121 protected $introductionEnabled; 122 123 /** 124 * An introduction text to give users more information 125 * on the test. 126 * 127 * @var string 128 */ 129 protected $introduction; 130 131 /** 132* Defines the mark schema 133* 134* @var ASS_MarkSchema 135*/ 136 public $mark_schema; 137 138 /** 139* Defines the sequence settings for the test user. There are two values: 140* TEST_FIXED_SEQUENCE (=0) and TEST_POSTPONE (=1). The default value is 141* TEST_FIXED_SEQUENCE. 142* 143* @var integer 144*/ 145 public $sequence_settings; 146 147 /** 148* Defines the score reporting for the test. There are two values: 149* REPORT_AFTER_TEST (=1), REPORT_ALWAYS (=2) AND REPORT_AFTER_DATE (=3). The default 150* value is REPORT_AFTER_TEST. If the score reporting is set to 151* REPORT_AFTER_TEST, it is also possible to use the $reporting_date 152* attribute to set a time/date for the earliest reporting time. 153* 154* @var integer 155*/ 156 public $score_reporting; 157 158 /** 159* Defines the question verification type for the test. When set to 1 160* a instant verification button will be offered during the test to verify 161* the question solution 162* 163* @var integer 164*/ 165 public $instant_verification; 166 167 /** 168* Defines wheather or not the reached points are shown as answer feedback 169* 170* @var integer 171*/ 172 public $answer_feedback_points; 173 174 /** 175* A time/date value to set the earliest reporting time for the test score. 176* If you set this attribute, the sequence settings will be set to REPORT_AFTER_TEST 177* automatically. If $reporting_date is not set, the user will get a direct feedback. 178* The reporting date is given in database TIMESTAMP notation (yyyymmddhhmmss). 179* 180* @var string 181*/ 182 public $reporting_date; 183 184 /** 185* Contains the evaluation data settings the tutor defines for the user 186* 187* @var object 188*/ 189 public $evaluation_data; 190 191 /** 192* Number of tries the user is allowed to do. If set to 0, the user has 193* infinite tries. 194* 195* @var integer 196*/ 197 public $nr_of_tries; 198 199 protected $blockPassesAfterPassedEnabled = false; 200 201 /** 202* Tells ILIAS to use the previous answers of a learner in a later test pass 203* The default is 1 which shows the previous answers in the next pass. 204* 205* @var integer 206*/ 207 public $use_previous_answers; 208 209 /** 210* Tells ILIAS how to deal with the test titles. The test title will be shown with 211* the full title and the points when title_output is 0. When title_output is 1, 212* the available points will be hidden and when title_output is 2, the full title 213* will be hidden. 214* 215* @var integer 216*/ 217 public $title_output; 218 219 /** 220* The maximum processing time as hh:mm:ss string the user is allowed to do. 221* 222* @var integer 223*/ 224 public $processing_time; 225 226 /** 227* Contains 0 if the processing time is disabled, 1 if the processing time is enabled 228* 229* @var integer 230*/ 231 public $enable_processing_time; 232 233 /** 234* Contains 0 if the processing time should not be reset, 1 if the processing time should be reset 235* 236* @var integer 237*/ 238 public $reset_processing_time; 239 240 /** 241 * @var bool 242 */ 243 protected $starting_time_enabled; 244 245 /** 246 * The starting time in database timestamp format which defines the earliest starting time for the test 247 * 248 * @var string 249 */ 250 protected $starting_time; 251 252 /** 253 * @var bool 254 */ 255 protected $ending_time_enabled; 256 257 /** 258 * The ending time in database timestamp format which defines the latest ending time for the test 259 * 260 * @var string 261 */ 262 protected $ending_time; 263 264 /** 265 * Indicates if ECTS grades will be used 266 * @var int|boolean 267 */ 268 protected $ects_output = false; 269 270 /** 271 * Contains the percentage of maximum points a failed user needs to get the FX ECTS grade 272 * @var float|null 273 */ 274 protected $ects_fx = null; 275 276 /** 277 * The percentiles of the ECTS grades for this test 278 * @var array 279 */ 280 protected $ects_grades = array(); 281 282 283 /** 284* Indicates if the points for answers are counted for partial solutions 285* or only for correct solutions 286* 287* @var integer 288*/ 289 public $count_system; 290 291 /** 292* Indicates if the points unchecked multiple choice questions are given or not 293* 294* @var integer 295*/ 296 public $mc_scoring; 297 298 /** 299* Defines which pass should be used for scoring 300* 301* @var integer 302*/ 303 public $pass_scoring; 304 305 /** 306* Indicates if the questions in a test are shuffled before 307* a user accesses the test 308* 309* @var boolean 310*/ 311 public $shuffle_questions; 312 313 /** 314* Contains the presentation settings for the test results 315* 316* @var integer 317*/ 318 public $results_presentation; 319 320 /** 321* Determines wheather or not a question summary is shown to the users 322* 323* @var boolean 324*/ 325 public $show_summary; 326 327 /** 328* Determines if the score of every question should be cut at 0 points or the score of the complete test 329* 330* @var boolean 331*/ 332 public $score_cutting; 333 334 /** 335 * @var bool 336 */ 337 protected $passwordEnabled; 338 339 /** 340 * Password access to enter the test 341 * 342 * @var string 343 */ 344 protected $password; 345 346 /** 347 * @var bool 348 */ 349 protected $limitUsersEnabled; 350 351 /** 352 * number of allowed users for the test 353 * 354 * @var int 355 */ 356 protected $allowedUsers; 357 358 /** 359 * inactivity time gap of the allowed users to let new users into the test 360 * 361 * @var int 362 */ 363 protected $allowedUsersTimeGap; 364 365 /** 366* visiblity settings for a test certificate 367* 368* @var int 369*/ 370 public $certificate_visibility; 371 372 /** 373* Anonymity of the test users 374* 375* @var int 376*/ 377 public $anonymity; 378 379 /** 380* determines wheather a cancel test button is shown or not 381* 382* @var int 383*/ 384 public $show_cancel; 385 386 /** 387* determines wheather a marker button is shown or not 388* 389* @var int 390*/ 391 public $show_marker; 392 393 /** 394* determines wheather a test may have fixed participants or not 395* 396* @var int 397*/ 398 public $fixed_participants; 399 400 /** 401* determines wheather an answer specific feedback is shown or not 402* 403* @var int 404*/ 405 public $answer_feedback; 406 407 /** 408 * contains the test session data 409 * 410 * @var object 411 */ 412 public $testSession; 413 414 /** 415 * contains the test sequence data 416 * 417 * @var object 418 */ 419 public $testSequence; 420 421 /** 422 * Determines whether or not a final statement should be shown on test completion 423 * 424 * @var integer 425 */ 426 private $_showfinalstatement; 427 428 /** 429 * A final statement for test completion 430 * 431 * @var string 432 */ 433 private $_finalstatement; 434 435 /** 436 * Show the complete data on the test information page 437 * 438 * @var boolean 439 */ 440 private $_showinfo; 441 442 /** 443 * Force JavaScript for test questions 444 * 445 * @var boolean 446 */ 447 private $_forcejs = true; 448 449 /** 450 * Name of a custom style sheet for the test 451 * 452 * @var string; 453 */ 454 private $_customStyle; 455 456 protected $mailnotification; 457 458 protected $mailnottype; 459 460 protected $exportsettings; 461 462 protected $poolUsage; 463 464 private $template_id; 465 466 protected $oldOnlineStatus = null; 467 468 /** 469 * @var bool 470 */ 471 protected $print_best_solution_with_result = true; 472 473 /** 474 * defines wether question specific hints are offered or not 475 * 476 * @var boolean 477 */ 478 private $offeringQuestionHintsEnabled = null; 479 480 /** 481 * defines wether it is possible to define obligatory questions 482 * 483 * @var boolean 484 */ 485 private $obligationsEnabled = null; 486 487 protected $activation_visibility; 488 489 protected $activation_starting_time; 490 491 protected $activation_ending_time; 492 493 protected $autosave; 494 495 protected $autosave_ival; 496 497 /** 498 * defines wether it is possible for users 499 * to delete their own test passes or not 500 * 501 * @var boolean 502 */ 503 private $passDeletionAllowed = null; 504 505 /** 506 * holds the fact wether participant data exists or not 507 * DO NOT USE TIS PROPERTY DRIRECTLY 508 * ALWAYS USE ilObjTest::paricipantDataExist() since this method initialises this property 509 */ 510 private $participantDataExist = null; 511 512 /** @var $enable_examview bool */ 513 protected $enable_examview; 514 515 /** @var $show_examview_html bool */ 516 protected $show_examview_html; 517 518 /** @var $show_examview_pdf bool */ 519 protected $show_examview_pdf; 520 521 /** @var $enbale_archiving bool */ 522 protected $enable_archiving; 523 524 /** 525 * @var int 526 */ 527 private $redirection_mode = 0; 528 529 /** 530 * @var string null 531 */ 532 private $redirection_url = null; 533 534 /** @var bool $show_exam_id_in_test_pass_enabled */ 535 protected $show_exam_id_in_test_pass_enabled; 536 537 /** @var bool $show_exam_id_in_test_results_enabled */ 538 protected $show_exam_id_in_test_results_enabled; 539 540 /** @var bool $sign_submission */ 541 protected $sign_submission; 542 543 /** @var mixed availability of selector for special characters */ 544 protected $char_selector_availability; 545 546 /** @var string definition of selector for special characters */ 547 protected $char_selector_definition; 548 549 /** 550 * @var bool 551 */ 552 protected $showGradingStatusEnabled; 553 554 /** 555 * @var bool 556 */ 557 protected $showGradingMarkEnabled; 558 559 /** 560 * @var bool 561 */ 562 protected $followupQuestionAnswerFixationEnabled; 563 564 /** 565 * @var bool 566 */ 567 protected $instantFeedbackAnswerFixationEnabled; 568 569 /** 570 * @var bool 571 */ 572 protected $forceInstantFeedbackEnabled; 573 574 /** 575 * @var bool 576 */ 577 protected $testFinalBroken; 578 579 /** 580 * @var integer 581 */ 582 private $tmpCopyWizardCopyId; 583 584 /** 585 * @var string mm:ddd:hh:ii:ss 586 */ 587 protected $pass_waiting = "00:000:00:00:00"; 588 #endregion 589 590 /** 591 * Constructor 592 * 593 * @param $a_id integer Reference_id or object_id. 594 * @param $a_call_by_reference boolean Treat the id as reference_id (true) or object_id (false). 595 * 596 * @return \ilObjTest 597 */ 598 public function __construct($a_id = 0, $a_call_by_reference = true) 599 { 600 global $DIC; 601 $ilUser = $DIC['ilUser']; 602 $lng = $DIC['lng']; 603 $this->type = "tst"; 604 605 $lng->loadLanguageModule("assessment"); 606 // Defaults: 607 include_once "./Modules/Test/classes/class.assMarkSchema.php"; 608 $this->mark_schema = new ASS_MarkSchema(); 609 $this->mark_schema->createSimpleSchema( 610 $lng->txt("failed_short"), 611 $lng->txt("failed_official"), 612 0, 613 0, 614 $lng->txt("passed_short"), 615 $lng->txt("passed_official"), 616 50, 617 1 618 ); 619 620 $this->test_id = -1; 621 $this->author = $ilUser->fullname; 622 $this->introductionEnabled = false; 623 $this->introduction = ""; 624 $this->questions = array(); 625 $this->sequence_settings = TEST_FIXED_SEQUENCE; 626 $this->score_reporting = self::SCORE_REPORTING_FINISHED; 627 $this->instant_verification = 0; 628 $this->answer_feedback_points = 0; 629 $this->reporting_date = ""; 630 $this->nr_of_tries = 0; 631 $this->_kiosk = 0; 632 $this->use_previous_answers = 1; 633 $this->title_output = 0; 634 $this->starting_time = ""; 635 $this->ending_time = ""; 636 $this->processing_time = ""; 637 $this->enable_processing_time = "0"; 638 $this->reset_processing_time = 0; 639 $this->ects_output = false; 640 $this->ects_fx = null; 641 $this->shuffle_questions = false; 642 $this->mailnottype = 0; 643 $this->exportsettings = 0; 644 $this->show_summary = 8; 645 $this->count_system = COUNT_PARTIAL_SOLUTIONS; 646 $this->mc_scoring = SCORE_ZERO_POINTS_WHEN_UNANSWERED; 647 $this->score_cutting = SCORE_CUT_QUESTION; 648 $this->pass_scoring = SCORE_LAST_PASS; 649 $this->answer_feedback = 0; 650 $this->password = ""; 651 $this->certificate_visibility = 0; 652 $this->allowedUsers = ""; 653 $this->_showfinalstatement = false; 654 $this->_finalstatement = ""; 655 $this->_showinfo = true; 656 $this->_forcejs = true; 657 $this->_customStyle = ""; 658 $this->allowedUsersTimeGap = ""; 659 $this->anonymity = 0; 660 $this->show_cancel = 0; 661 $this->show_marker = 0; 662 $this->fixed_participants = 0; 663 $this->setShowPassDetails(true); 664 $this->setShowSolutionDetails(true); 665 $this->setShowSolutionAnswersOnly(false); 666 $this->setShowSolutionSignature(false); 667 $this->testSession = false; 668 $this->testSequence = false; 669 $this->mailnotification = 0; 670 $this->poolUsage = 1; 671 672 $this->ects_grades = array( 673 'A' => 90, 674 'B' => 65, 675 'C' => 35, 676 'D' => 10, 677 'E' => 0 678 ); 679 680 $this->autosave = false; 681 $this->autosave_ival = 30000; 682 683 $this->enable_examview = false; 684 $this->show_examview_html = false; 685 $this->show_examview_pdf = false; 686 $this->enable_archiving = false; 687 688 $this->express_mode = false; 689 $this->template_id = ''; 690 $this->redirection_mode = 0; 691 $this->redirection_url = null; 692 $this->show_exam_id_in_test_pass_enabled = false; 693 $this->show_exam_id_in_test_results_enabled = false; 694 $this->sign_submission = false; 695 $this->char_selector_availability = 0; 696 $this->char_selector_definition = null; 697 698 $this->showGradingStatusEnabled = true; 699 $this->showGradingMarkEnabled = true; 700 701 $this->followupQuestionAnswerFixationEnabled = false; 702 $this->instantFeedbackAnswerFixationEnabled = false; 703 704 $this->testFinalBroken = false; 705 706 $this->tmpCopyWizardCopyId = null; 707 708 parent::__construct($a_id, $a_call_by_reference); 709 } 710 711 /** 712 * returns the object title prepared to be used as a filename 713 * 714 * @return string 715 */ 716 public function getTitleFilenameCompliant() 717 { 718 require_once 'Services/Utilities/classes/class.ilUtil.php'; 719 return ilUtil::getASCIIFilename($this->getTitle()); 720 } 721 722 /** 723 * @return int 724 */ 725 public function getTmpCopyWizardCopyId() 726 { 727 return $this->tmpCopyWizardCopyId; 728 } 729 730 /** 731 * @param int $tmpCopyWizardCopyId 732 */ 733 public function setTmpCopyWizardCopyId($tmpCopyWizardCopyId) 734 { 735 $this->tmpCopyWizardCopyId = $tmpCopyWizardCopyId; 736 } 737 738 /** 739 * create test object 740 */ 741 public function create() 742 { 743 $this->setOfflineStatus(true); 744 parent::create(); 745 746 // meta data will be created by 747 // import parser 748 if (!$a_upload) { 749 $this->createMetaData(); 750 } 751 } 752 753 /** 754 * update object data 755 * 756 * @access public 757 * @return boolean 758 */ 759 public function update() 760 { 761 if (!parent::update()) { 762 return false; 763 } 764 765 // put here object specific stuff 766 $this->updateMetaData(); 767 return true; 768 } 769 770 /** 771 * read object data from db into object 772 * @param boolean 773 * @access public 774 */ 775 public function read() 776 { 777 parent::read(); 778 $this->loadFromDb(); 779 } 780 781 782 /** 783 * delete object and all related data 784 * 785 * @access public 786 * @return boolean true if all object data were removed; false if only a references were removed 787 */ 788 public function delete() 789 { 790 // always call parent delete function first!! 791 if (!parent::delete()) { 792 return false; 793 } 794 795 // delet meta data 796 $this->deleteMetaData(); 797 798 //put here your module specific stuff 799 $this->deleteTest(); 800 801 require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssQuestionSkillAssignmentImportFails.php'; 802 $qsaImportFails = new ilAssQuestionSkillAssignmentImportFails($this->getId()); 803 $qsaImportFails->deleteRegisteredImportFails(); 804 require_once 'Modules/Test/classes/class.ilTestSkillLevelThresholdImportFails.php'; 805 $sltImportFails = new ilTestSkillLevelThresholdImportFails($this->getId()); 806 $sltImportFails->deleteRegisteredImportFails(); 807 808 return true; 809 } 810 811 /** 812 * Deletes the test and all related objects, files and database entries 813 * 814 * @access public 815 */ 816 public function deleteTest() 817 { 818 global $DIC; 819 $tree = $DIC['tree']; 820 $ilDB = $DIC['ilDB']; 821 $ilPluginAdmin = $DIC['ilPluginAdmin']; 822 $lng = $DIC['lng']; 823 824 require_once 'Modules/Test/classes/class.ilTestParticipantData.php'; 825 $participantData = new ilTestParticipantData($ilDB, $lng); 826 $participantData->load($this->getTestId()); 827 $this->removeTestResults($participantData); 828 829 $affectedRows = $ilDB->manipulateF( 830 "DELETE FROM tst_mark WHERE test_fi = %s", 831 array('integer'), 832 array($this->getTestId()) 833 ); 834 835 $affectedRows = $ilDB->manipulateF( 836 "DELETE FROM tst_tests WHERE test_id = %s", 837 array('integer'), 838 array($this->getTestId()) 839 ); 840 841 require_once 'Modules/Test/classes/class.ilTestQuestionSetConfigFactory.php'; 842 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $ilPluginAdmin, $this); 843 $testQuestionSetConfigFactory->getQuestionSetConfig()->removeQuestionSetRelatedData(); 844 845 // delete export files 846 include_once "./Services/Utilities/classes/class.ilUtil.php"; 847 $tst_data_dir = ilUtil::getDataDir() . "/tst_data"; 848 $directory = $tst_data_dir . "/tst_" . $this->getId(); 849 if (is_dir($directory)) { 850 include_once "./Services/Utilities/classes/class.ilUtil.php"; 851 ilUtil::delDir($directory); 852 } 853 include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php"); 854 $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId()); 855 // remaining usages are not in text anymore -> delete them 856 // and media objects (note: delete method of ilObjMediaObject 857 // checks whether object is used in another context; if yes, 858 // the object is not deleted!) 859 foreach ($mobs as $mob) { 860 ilObjMediaObject::_removeUsage($mob, "tst:html", $this->getId()); 861 if (ilObjMediaObject::_exists($mob)) { 862 $mob_obj = new ilObjMediaObject($mob); 863 $mob_obj->delete(); 864 } 865 } 866 } 867 868 /** 869 * creates data directory for export files 870 * (data_dir/tst_data/tst_<id>/export, depending on data 871 * directory that is set in ILIAS setup/ini) 872 */ 873 public function createExportDirectory() 874 { 875 include_once "./Services/Utilities/classes/class.ilUtil.php"; 876 $tst_data_dir = ilUtil::getDataDir() . "/tst_data"; 877 ilUtil::makeDir($tst_data_dir); 878 if (!is_writable($tst_data_dir)) { 879 $this->ilias->raiseError("Test Data Directory (" . $tst_data_dir 880 . ") not writeable.", $this->ilias->error_obj->MESSAGE); 881 } 882 883 // create learning module directory (data_dir/lm_data/lm_<id>) 884 $tst_dir = $tst_data_dir . "/tst_" . $this->getId(); 885 ilUtil::makeDir($tst_dir); 886 if (!@is_dir($tst_dir)) { 887 $this->ilias->raiseError("Creation of Test Directory failed.", $this->ilias->error_obj->MESSAGE); 888 } 889 // create Export subdirectory (data_dir/lm_data/lm_<id>/Export) 890 $export_dir = $tst_dir . "/export"; 891 ilUtil::makeDir($export_dir); 892 if (!@is_dir($export_dir)) { 893 $this->ilias->raiseError("Creation of Export Directory failed.", $this->ilias->error_obj->MESSAGE); 894 } 895 } 896 897 /** 898 * Get the location of the export directory for the test 899 * 900 * @access public 901 */ 902 public function getExportDirectory() 903 { 904 include_once "./Services/Utilities/classes/class.ilUtil.php"; 905 $export_dir = ilUtil::getDataDir() . "/tst_data" . "/tst_" . $this->getId() . "/export"; 906 return $export_dir; 907 } 908 909 /** 910 * Get a list of the already exported files in the export directory 911 * 912 * @return array A list of file names 913 * @access public 914 */ 915 public function getExportFiles($dir) 916 { 917 // quit if import dir not available 918 if (!@is_dir($dir) || !is_writeable($dir)) { 919 return array(); 920 } 921 922 $files = array(); 923 foreach (new DirectoryIterator($dir) as $file) { 924 /** 925 * @var $file SplFileInfo 926 */ 927 if ($file->isDir()) { 928 continue; 929 } 930 931 $files[] = $file->getBasename(); 932 } 933 934 sort($files); 935 936 return $files; 937 } 938 939 /** 940 * set import directory 941 */ 942 public static function _setImportDirectory($a_import_dir = null) 943 { 944 if (strlen($a_import_dir)) { 945 $_SESSION["tst_import_dir"] = $a_import_dir; 946 } else { 947 unset($_SESSION["tst_import_dir"]); 948 } 949 } 950 951 /** 952 * Get the import directory location of the test 953 * 954 * @return string The location of the import directory or false if the directory doesn't exist 955 * @access public 956 */ 957 public static function _getImportDirectory() 958 { 959 if (strlen($_SESSION["tst_import_dir"])) { 960 return $_SESSION["tst_import_dir"]; 961 } 962 return null; 963 } 964 965 public function getImportDirectory() 966 { 967 return ilObjTest::_getImportDirectory(); 968 } 969 970 /** 971 * creates data directory for import files 972 * (data_dir/tst_data/tst_<id>/import, depending on data 973 * directory that is set in ILIAS setup/ini) 974 */ 975 public static function _createImportDirectory() 976 { 977 global $DIC; 978 $ilias = $DIC['ilias']; 979 include_once "./Services/Utilities/classes/class.ilUtil.php"; 980 $tst_data_dir = ilUtil::getDataDir() . "/tst_data"; 981 ilUtil::makeDir($tst_data_dir); 982 983 if (!is_writable($tst_data_dir)) { 984 $ilias->raiseError("Test Data Directory (" . $tst_data_dir 985 . ") not writeable.", $ilias->error_obj->FATAL); 986 } 987 988 // create test directory (data_dir/tst_data/tst_import) 989 $tst_dir = $tst_data_dir . "/tst_import"; 990 ilUtil::makeDir($tst_dir); 991 if (!@is_dir($tst_dir)) { 992 $ilias->raiseError("Creation of test import directory failed.", $ilias->error_obj->FATAL); 993 } 994 995 // assert that this is empty and does not contain old data 996 ilUtil::delDir($tst_dir, true); 997 998 return $tst_dir; 999 } 1000 1001 /** 1002 * Returns TRUE if the test contains single choice results 1003 * 1004 * @return boolean 1005 * @access public 1006 */ 1007 public function hasSingleChoiceQuestions() 1008 { 1009 global $DIC; 1010 $ilDB = $DIC['ilDB']; 1011 1012 $result = $ilDB->queryF( 1013 "SELECT DISTINCT(qpl_qst_type.type_tag) foundtypes FROM qpl_questions, tst_test_result, qpl_qst_type, tst_active WHERE tst_test_result.question_fi = qpl_questions.question_id AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id AND tst_test_result.active_fi = tst_active.active_id AND tst_active.test_fi = %s", 1014 array('integer'), 1015 array($this->getTestId()) 1016 ); 1017 $hasSC = false; 1018 while ($row = $ilDB->fetchAssoc($result)) { 1019 if (strcmp($row['foundtypes'], 'assSingleChoice') == 0) { 1020 $hasSC = true; 1021 } 1022 } 1023 return $hasSC; 1024 } 1025 1026 /** 1027 * Returns TRUE if the test contains single choice results only 1028 * 1029 * @return boolean 1030 * @access public 1031 */ 1032 public function isSingleChoiceTest() 1033 { 1034 global $DIC; 1035 $ilDB = $DIC['ilDB']; 1036 1037 $result = $ilDB->queryF( 1038 "SELECT DISTINCT(qpl_qst_type.type_tag) foundtypes FROM qpl_questions, tst_test_result, qpl_qst_type, tst_active WHERE tst_test_result.question_fi = qpl_questions.question_id AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id AND tst_test_result.active_fi = tst_active.active_id AND tst_active.test_fi = %s", 1039 array('integer'), 1040 array($this->getTestId()) 1041 ); 1042 if ($result->numRows() == 1) { 1043 $row = $ilDB->fetchAssoc($result); 1044 if (strcmp($row['foundtypes'], 'assSingleChoice') == 0) { 1045 return true; 1046 } else { 1047 return false; 1048 } 1049 } 1050 return false; 1051 } 1052 1053 /** 1054 * Returns TRUE if the test contains single choice results and no shuffle only 1055 * 1056 * @return boolean 1057 * @access public 1058 */ 1059 public function isSingleChoiceTestWithoutShuffle() 1060 { 1061 global $DIC; 1062 $ilDB = $DIC['ilDB']; 1063 1064 if (!$this->hasSingleChoiceQuestions()) { 1065 return false; 1066 } 1067 1068 $result = $ilDB->queryF( 1069 " 1070 SELECT DISTINCT(qpl_qst_sc.shuffle) foundshuffles 1071 FROM qpl_questions, 1072 qpl_qst_sc, 1073 tst_test_result, 1074 qpl_qst_type, 1075 tst_active 1076 WHERE tst_test_result.question_fi = qpl_questions.question_id 1077 AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id 1078 AND tst_test_result.active_fi = tst_active.active_id 1079 AND qpl_questions.question_id = qpl_qst_sc.question_fi 1080 AND tst_active.test_fi = %s 1081 AND qpl_qst_type.type_tag = %s 1082 ", 1083 array('integer', 'text'), 1084 array($this->getTestId(), 'assSingleChoice') 1085 ); 1086 if ($result->numRows() == 1) { 1087 $row = $ilDB->fetchAssoc($result); 1088 return ($row['foundshuffles'] == 0); 1089 } 1090 return false; 1091 } 1092 1093 /** 1094 * Returns true, if a test is complete for use and can be set online 1095 * 1096 * @param ilTestQuestionSetConfig $testQuestionSetConfig 1097 * @return boolean 1098 */ 1099 final public function isComplete(ilTestQuestionSetConfig $testQuestionSetConfig) 1100 { 1101 if (!count($this->mark_schema->mark_steps)) { 1102 return false; 1103 } 1104 1105 if (!$testQuestionSetConfig->isQuestionSetConfigured()) { 1106 return false; 1107 } 1108 1109 return true; 1110 } 1111 1112 /** 1113 * Returns true, if a test is complete for use 1114 * 1115 * @return boolean True, if the test is complete for use, otherwise false 1116 * @access public 1117 */ 1118 public function _isComplete($obj_id) 1119 { 1120 global $DIC; 1121 $tree = $DIC['tree']; 1122 $ilDB = $DIC['ilDB']; 1123 $ilPluginAdmin = $DIC['ilPluginAdmin']; 1124 1125 $test = new ilObjTest($obj_id, false); 1126 $test->loadFromDb(); 1127 1128 require_once 'Modules/Test/classes/class.ilTestQuestionSetConfigFactory.php'; 1129 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $ilPluginAdmin, $test); 1130 1131 return $test->isComplete($testQuestionSetConfigFactory->getQuestionSetConfig()); 1132 } 1133 1134 /** 1135 * Saves the ECTS status (output of ECTS grades in a test) to the database 1136 */ 1137 public function saveECTSStatus() 1138 { 1139 /** 1140 * @var $ilDB ilDBInterface 1141 */ 1142 global $DIC; 1143 $ilDB = $DIC['ilDB']; 1144 1145 if ($this->getTestId() > 0) { 1146 $this->setECTSFX(preg_replace('/,/', '.', $this->getECTSFX())); 1147 if (!preg_match('/\d+/', $this->getECTSFX())) { 1148 $this->setECTSFX(null); 1149 } 1150 1151 $grades = $this->getECTSGrades(); 1152 $ilDB->manipulateF( 1153 "UPDATE tst_tests 1154 SET ects_output = %s, ects_a = %s, ects_b = %s, ects_c = %s, ects_d = %s, ects_e = %s, ects_fx = %s 1155 WHERE test_id = %s", 1156 array('text', 'float', 'float', 'float', 'float', 'float', 'float', 'integer'), 1157 array( 1158 (int) $this->getECTSOutput(), 1159 $grades['A'], $grades['B'], $grades['C'], $grades['D'], $grades['E'], 1160 $this->getECTSFX(), 1161 $this->getTestId() 1162 ) 1163 ); 1164 } 1165 } 1166 1167 /** 1168 * Checks if the test is complete and saves the status in the database 1169 * @param ilTestQuestionSetConfig $testQuestionSetConfig 1170 */ 1171 public function saveCompleteStatus(ilTestQuestionSetConfig $testQuestionSetConfig) 1172 { 1173 global $DIC; 1174 $ilDB = $DIC['ilDB']; 1175 1176 $complete = 0; 1177 if ($this->isComplete($testQuestionSetConfig)) { 1178 $complete = 1; 1179 } 1180 if ($this->getTestId() > 0) { 1181 $ilDB->manipulateF( 1182 "UPDATE tst_tests SET complete = %s WHERE test_id = %s", 1183 array('text', 'integer'), 1184 array($complete, $this->test_id) 1185 ); 1186 } 1187 } 1188 1189 /** 1190 * Returns the content of all RTE enabled text areas in the test 1191 * 1192 * @access private 1193 */ 1194 public function getAllRTEContent() 1195 { 1196 $result = array(); 1197 array_push($result, $this->getIntroduction()); 1198 array_push($result, $this->getFinalStatement()); 1199 return $result; 1200 } 1201 1202 /** 1203 * Cleans up the media objects for all text fields in a test which are using an RTE field 1204 * 1205 * @access private 1206 */ 1207 public function cleanupMediaobjectUsage() 1208 { 1209 include_once("./Services/RTE/classes/class.ilRTE.php"); 1210 $completecontent = ""; 1211 foreach ($this->getAllRTEContent() as $content) { 1212 $completecontent .= $content; 1213 } 1214 ilRTE::_cleanupMediaObjectUsage( 1215 $completecontent, 1216 $this->getType() . ":html", 1217 $this->getId() 1218 ); 1219 } 1220 1221 /** 1222 * Saves a ilObjTest object to a database 1223 * 1224 * @param bool $properties_only 1225 */ 1226 public function saveToDb($properties_only = false) 1227 { 1228 global $DIC; 1229 $tree = $DIC['tree']; 1230 $ilDB = $DIC['ilDB']; 1231 $ilPluginAdmin = $DIC['ilPluginAdmin']; 1232 1233 // moved online_status to ilObjectActivation (see below) 1234 1235 // cleanup RTE images 1236 $this->cleanupMediaobjectUsage(); 1237 1238 require_once 'Modules/Test/classes/class.ilTestQuestionSetConfigFactory.php'; 1239 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $ilPluginAdmin, $this); 1240 $testQuestionSetConfig = $testQuestionSetConfigFactory->getQuestionSetConfig(); 1241 1242 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php"); 1243 if ($this->test_id == -1) { 1244 // Create new dataset 1245 $next_id = $ilDB->nextId('tst_tests'); 1246 1247 $ilDB->insert('tst_tests', array( 1248 'test_id' => array('integer', $next_id), 1249 'obj_fi' => array('integer', $this->getId()), 1250 'author' => array('text', $this->getAuthor()), 1251 'intro_enabled' => array('integer', (int) $this->isIntroductionEnabled()), 1252 'introduction' => array('text', ilRTE::_replaceMediaObjectImageSrc($this->getIntroduction(), 0)), 1253 'finalstatement' => array('text', ilRTE::_replaceMediaObjectImageSrc($this->getFinalStatement(), 0)), 1254 'showinfo' => array('integer', $this->getShowInfo()), 1255 'forcejs' => array('integer', $this->getForceJS()), 1256 'customstyle' => array('text', $this->getCustomStyle()), 1257 'showfinalstatement' => array('integer', $this->getShowFinalStatement()), 1258 'sequence_settings' => array('integer', $this->getSequenceSettings()), 1259 'score_reporting' => array('integer', $this->getScoreReporting()), 1260 'instant_verification' => array('text', $this->getInstantFeedbackSolution()), 1261 'answer_feedback_points' => array('text', $this->getAnswerFeedbackPoints()), 1262 'answer_feedback' => array('text', $this->getAnswerFeedback()), 1263 'anonymity' => array('text', $this->getAnonymity()), 1264 'show_cancel' => array('text', $this->getShowCancel()), 1265 'show_marker' => array('integer', $this->getShowMarker()), 1266 'fixed_participants' => array('text', $this->getFixedParticipants()), 1267 'nr_of_tries' => array('integer', $this->getNrOfTries()), 1268 'block_after_passed' => array('integer', (int) $this->isBlockPassesAfterPassedEnabled()), 1269 'kiosk' => array('integer', $this->getKiosk()), 1270 'use_previous_answers' => array('text', $this->getUsePreviousAnswers()), 1271 'title_output' => array('text', $this->getTitleOutput()), 1272 'processing_time' => array('text', $this->getProcessingTime()), 1273 'enable_processing_time' => array('text', $this->getEnableProcessingTime()), 1274 'reset_processing_time' => array('integer', $this->getResetProcessingTime()), 1275 'reporting_date' => array('text', $this->getReportingDate()), 1276 'starting_time_enabled' => array('integer', $this->isStartingTimeEnabled()), 1277 'starting_time' => array('integer', $this->getStartingTime()), 1278 'ending_time_enabled' => array('integer', $this->isEndingTimeEnabled()), 1279 'ending_time' => array('integer', $this->getEndingTime()), 1280 'complete' => array('text', $this->isComplete($testQuestionSetConfig)), 1281 'ects_output' => array('text', $this->getECTSOutput()), 1282 'ects_a' => array('float', strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : null), 1283 'ects_b' => array('float', strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : null), 1284 'ects_c' => array('float', strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : null), 1285 'ects_d' => array('float', strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : null), 1286 'ects_e' => array('float', strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : null), 1287 'ects_fx' => array('float', $this->getECTSFX()), 1288 'count_system' => array('text', $this->getCountSystem()), 1289 'mc_scoring' => array('text', $this->getMCScoring()), 1290 'score_cutting' => array('text', $this->getScoreCutting()), 1291 'pass_scoring' => array('text', $this->getPassScoring()), 1292 'shuffle_questions' => array('text', $this->getShuffleQuestions()), 1293 'results_presentation' => array('integer', $this->getResultsPresentation()), 1294 'show_summary' => array('integer', $this->getListOfQuestionsSettings()), 1295 'password_enabled' => array('integer', (int) $this->isPasswordEnabled()), 1296 'password' => array('text', $this->getPassword()), 1297 'limit_users_enabled' => array('integer', (int) $this->isLimitUsersEnabled()), 1298 'allowedusers' => array('integer', $this->getAllowedUsers()), 1299 'alloweduserstimegap' => array('integer', $this->getAllowedUsersTimeGap()), 1300 'mailnottype' => array('integer', $this->getMailNotificationType()), 1301 'exportsettings' => array('integer', $this->getExportSettings()), 1302 'certificate_visibility' => array('text', $this->getCertificateVisibility()), 1303 'mailnotification' => array('integer', $this->getMailNotification()), 1304 'created' => array('integer', time()), 1305 'tstamp' => array('integer', time()), 1306 'enabled_view_mode' => array('text', $this->getEnabledViewMode()), 1307 'template_id' => array('integer', $this->getTemplate()), 1308 'pool_usage' => array('integer', $this->getPoolUsage()), 1309 'print_bs_with_res' => array('integer', (int) $this->isBestSolutionPrintedWithResult()), 1310 'obligations_enabled' => array('integer', (int) $this->areObligationsEnabled()), 1311 'offer_question_hints' => array('integer', (int) $this->isOfferingQuestionHintsEnabled()), 1312 'highscore_enabled' => array('integer', (int) $this->getHighscoreEnabled()), 1313 'highscore_anon' => array('integer', (int) $this->getHighscoreAnon()), 1314 'highscore_achieved_ts' => array('integer', (int) $this->getHighscoreAchievedTS()), 1315 'highscore_score' => array('integer', (int) $this->getHighscoreScore()), 1316 'highscore_percentage' => array('integer', (int) $this->getHighscorePercentage()), 1317 'highscore_hints' => array('integer', (int) $this->getHighscoreHints()), 1318 'highscore_wtime' => array('integer', (int) $this->getHighscoreWTime()), 1319 'highscore_own_table' => array('integer', (int) $this->getHighscoreOwnTable()), 1320 'highscore_top_table' => array('integer', (int) $this->getHighscoreTopTable()), 1321 'highscore_top_num' => array('integer', (int) $this->getHighscoreTopNum()), 1322 'specific_feedback' => array('integer', (int) $this->getSpecificAnswerFeedback()), 1323 'autosave' => array('integer', (int) $this->getAutosave()), 1324 'autosave_ival' => array('integer', (int) $this->getAutosaveIval()), 1325 'pass_deletion_allowed' => array('integer', (int) $this->isPassDeletionAllowed()), 1326 'enable_examview' => array('integer', (int) $this->getEnableExamview()), 1327 'show_examview_html' => array('integer', (int) $this->getShowExamviewHtml()), 1328 'show_examview_pdf' => array('integer', (int) $this->getShowExamviewPdf()), 1329 'redirection_mode' => array('integer', (int) $this->getRedirectionMode()), 1330 'redirection_url' => array('text', (string) $this->getRedirectionUrl()), 1331 'enable_archiving' => array('integer', (int) $this->getEnableArchiving()), 1332 'examid_in_test_pass' => array('integer', (int) $this->isShowExamIdInTestPassEnabled()), 1333 'examid_in_test_res' => array('integer', (int) $this->isShowExamIdInTestResultsEnabled()), 1334 'sign_submission' => array('integer', (int) $this->getSignSubmission()), 1335 'question_set_type' => array('text', $this->getQuestionSetType()), 1336 'char_selector_availability' => array('integer', (int) $this->getCharSelectorAvailability()), 1337 'char_selector_definition' => array('text', (string) $this->getCharSelectorDefinition()), 1338 'skill_service' => array('integer', (int) $this->isSkillServiceEnabled()), 1339 'result_tax_filters' => array('text', serialize((array) $this->getResultFilterTaxIds())), 1340 'show_grading_status' => array('integer', (int) $this->isShowGradingStatusEnabled()), 1341 'show_grading_mark' => array('integer', (int) $this->isShowGradingMarkEnabled()), 1342 'follow_qst_answer_fixation' => array('integer', (int) $this->isFollowupQuestionAnswerFixationEnabled()), 1343 'inst_fb_answer_fixation' => array('integer', (int) $this->isInstantFeedbackAnswerFixationEnabled()), 1344 'force_inst_fb' => array('integer', (int) $this->isForceInstantFeedbackEnabled()), 1345 'broken' => array('integer', (int) $this->isTestFinalBroken()), 1346 'pass_waiting' => array('text', (string) $this->getPassWaiting()) 1347 )); 1348 1349 $this->test_id = $next_id; 1350 1351 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 1352 $this->logAction($this->lng->txtlng("assessment", "log_create_new_test", ilObjAssessmentFolder::_getLogLanguage())); 1353 } 1354 } else { 1355 // Modify existing dataset 1356 $oldrow = array(); 1357 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 1358 $result = $ilDB->queryF( 1359 "SELECT * FROM tst_tests WHERE test_id = %s", 1360 array('integer'), 1361 array($this->test_id) 1362 ); 1363 if ($result->numRows() == 1) { 1364 $oldrow = $ilDB->fetchAssoc($result); 1365 } 1366 } 1367 1368 $ilDB->update( 1369 'tst_tests', 1370 array( 1371 'author' => array('text', $this->getAuthor()), 1372 'intro_enabled' => array('integer', (int) $this->isIntroductionEnabled()), 1373 'introduction' => array('text', ilRTE::_replaceMediaObjectImageSrc($this->getIntroduction(), 0)), 1374 'finalstatement' => array('text', ilRTE::_replaceMediaObjectImageSrc($this->getFinalStatement(), 0)), 1375 'showinfo' => array('integer', $this->getShowInfo()), 1376 'forcejs' => array('integer', $this->getForceJS()), 1377 'customstyle' => array('text', $this->getCustomStyle()), 1378 'showfinalstatement' => array('integer', $this->getShowFinalStatement()), 1379 'sequence_settings' => array('integer', $this->getSequenceSettings()), 1380 'score_reporting' => array('integer', $this->getScoreReporting()), 1381 'instant_verification' => array('text', $this->getInstantFeedbackSolution()), 1382 'answer_feedback_points' => array('text', $this->getAnswerFeedbackPoints()), 1383 'answer_feedback' => array('text', $this->getGenericAnswerFeedback()), 1384 'anonymity' => array('text', $this->getAnonymity()), 1385 'show_cancel' => array('text', $this->getShowCancel()), 1386 'show_marker' => array('integer', $this->getShowMarker()), 1387 'fixed_participants' => array('text', $this->getFixedParticipants()), 1388 'nr_of_tries' => array('integer', $this->getNrOfTries()), 1389 'block_after_passed' => array('integer', (int) $this->isBlockPassesAfterPassedEnabled()), 1390 'kiosk' => array('integer', $this->getKiosk()), 1391 'use_previous_answers' => array('text', $this->getUsePreviousAnswers()), 1392 'title_output' => array('text', $this->getTitleOutput()), 1393 'processing_time' => array('text', $this->getProcessingTime()), 1394 'enable_processing_time' => array('text', $this->getEnableProcessingTime()), 1395 'reset_processing_time' => array('integer', $this->getResetProcessingTime()), 1396 'reporting_date' => array('text', $this->getReportingDate()), 1397 'starting_time_enabled' => array('integer', $this->isStartingTimeEnabled()), 1398 'starting_time' => array('integer', $this->getStartingTime()), 1399 'ending_time_enabled' => array('integer', $this->isEndingTimeEnabled()), 1400 'ending_time' => array('integer', $this->getEndingTime()), 1401 'complete' => array('text', $this->isComplete($testQuestionSetConfig)), 1402 'ects_output' => array('text', $this->getECTSOutput()), 1403 'ects_a' => array('float', strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : null), 1404 'ects_b' => array('float', strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : null), 1405 'ects_c' => array('float', strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : null), 1406 'ects_d' => array('float', strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : null), 1407 'ects_e' => array('float', strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : null), 1408 'ects_fx' => array('float', $this->getECTSFX()), 1409 'count_system' => array('text', $this->getCountSystem()), 1410 'mc_scoring' => array('text', $this->getMCScoring()), 1411 'score_cutting' => array('text', $this->getScoreCutting()), 1412 'pass_scoring' => array('text', $this->getPassScoring()), 1413 'shuffle_questions' => array('text', $this->getShuffleQuestions()), 1414 'results_presentation' => array('integer', $this->getResultsPresentation()), 1415 'show_summary' => array('integer', $this->getListOfQuestionsSettings()), 1416 'password_enabled' => array('integer', (int) $this->isPasswordEnabled()), 1417 'password' => array('text', $this->getPassword()), 1418 'limit_users_enabled' => array('integer', (int) $this->isLimitUsersEnabled()), 1419 'allowedusers' => array('integer', $this->getAllowedUsers()), 1420 'alloweduserstimegap' => array('integer', $this->getAllowedUsersTimeGap()), 1421 'mailnottype' => array('integer', $this->getMailNotificationType()), 1422 'exportsettings' => array('integer', $this->getExportSettings()), 1423 'certificate_visibility' => array('text', $this->getCertificateVisibility()), 1424 'mailnotification' => array('integer', $this->getMailNotification()), 1425 'tstamp' => array('integer', time()), 1426 'enabled_view_mode' => array('text', $this->getEnabledViewMode()), 1427 'template_id' => array('integer', $this->getTemplate()), 1428 'pool_usage' => array('integer', $this->getPoolUsage()), 1429 'print_bs_with_res' => array('integer', (int) $this->isBestSolutionPrintedWithResult()), 1430 'obligations_enabled' => array('integer', (int) $this->areObligationsEnabled()), 1431 'offer_question_hints' => array('integer', (int) $this->isOfferingQuestionHintsEnabled()), 1432 'highscore_enabled' => array('integer', (int) $this->getHighscoreEnabled()), 1433 'highscore_anon' => array('integer', (int) $this->getHighscoreAnon()), 1434 'highscore_achieved_ts' => array('integer', (int) $this->getHighscoreAchievedTS()), 1435 'highscore_score' => array('integer', (int) $this->getHighscoreScore()), 1436 'highscore_percentage' => array('integer', (int) $this->getHighscorePercentage()), 1437 'highscore_hints' => array('integer', (int) $this->getHighscoreHints()), 1438 'highscore_wtime' => array('integer', (int) $this->getHighscoreWTime()), 1439 'highscore_own_table' => array('integer', (int) $this->getHighscoreOwnTable()), 1440 'highscore_top_table' => array('integer', (int) $this->getHighscoreTopTable()), 1441 'highscore_top_num' => array('integer', (int) $this->getHighscoreTopNum()), 1442 'specific_feedback' => array('integer', (int) $this->getSpecificAnswerFeedback()), 1443 'autosave' => array('integer', (int) $this->getAutosave()), 1444 'autosave_ival' => array('integer', (int) $this->getAutosaveIval()), 1445 'pass_deletion_allowed' => array('integer', (int) $this->isPassDeletionAllowed()), 1446 'enable_examview' => array('integer', (int) $this->getEnableExamview()), 1447 'show_examview_html' => array('integer', (int) $this->getShowExamviewHtml()), 1448 'show_examview_pdf' => array('integer', (int) $this->getShowExamviewPdf()), 1449 'redirection_mode' => array('integer', (int) $this->getRedirectionMode()), 1450 'redirection_url' => array('text', (string) $this->getRedirectionUrl()), 1451 'enable_archiving' => array('integer', (int) $this->getEnableArchiving()), 1452 'examid_in_test_pass' => array('integer', (int) $this->isShowExamIdInTestPassEnabled()), 1453 'examid_in_test_res' => array('integer', (int) $this->isShowExamIdInTestResultsEnabled()), 1454 'sign_submission' => array('integer', (int) $this->getSignSubmission()), 1455 'question_set_type' => array('text', $this->getQuestionSetType()), 1456 'char_selector_availability' => array('integer', (int) $this->getCharSelectorAvailability()), 1457 'char_selector_definition' => array('text', (string) $this->getCharSelectorDefinition()), 1458 'skill_service' => array('integer', (int) $this->isSkillServiceEnabled()), 1459 'result_tax_filters' => array('text', serialize((array) $this->getResultFilterTaxIds())), 1460 'show_grading_status' => array('integer', (int) $this->isShowGradingStatusEnabled()), 1461 'show_grading_mark' => array('integer', (int) $this->isShowGradingMarkEnabled()), 1462 'follow_qst_answer_fixation' => array('integer', (int) $this->isFollowupQuestionAnswerFixationEnabled()), 1463 'inst_fb_answer_fixation' => array('integer', (int) $this->isInstantFeedbackAnswerFixationEnabled()), 1464 'force_inst_fb' => array('integer', (int) $this->isForceInstantFeedbackEnabled()), 1465 'broken' => array('integer', (int) $this->isTestFinalBroken()), 1466 'pass_waiting' => array('text', (string) $this->getPassWaiting()) 1467 ), 1468 array( 1469 'test_id' => array('integer', (int) $this->getTestId()) 1470 ) 1471 ); 1472 1473 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php"); 1474 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 1475 $logresult = $ilDB->queryF( 1476 "SELECT * FROM tst_tests WHERE test_id = %s", 1477 array('integer'), 1478 array($this->getTestId()) 1479 ); 1480 $newrow = array(); 1481 if ($logresult->numRows() == 1) { 1482 $newrow = $ilDB->fetchAssoc($logresult); 1483 } 1484 $changed_fields = array(); 1485 foreach ($oldrow as $key => $value) { 1486 if (strcmp($oldrow[$key], $newrow[$key]) != 0) { 1487 array_push($changed_fields, "$key: " . $oldrow[$key] . " => " . $newrow[$key]); 1488 } 1489 } 1490 $changes = join(", ", $changed_fields); 1491 if (count($changed_fields) > 0) { 1492 $this->logAction($this->lng->txtlng("assessment", "log_modified_test", ilObjAssessmentFolder::_getLogLanguage()) . " [" . $changes . "]"); 1493 } 1494 } 1495 if ($this->evalTotalPersons() > 0) { 1496 // reset the finished status of participants if the nr of test passes did change 1497 if ($this->getNrOfTries() > 0) { 1498 // set all unfinished tests with nr of passes >= allowed passes finished 1499 $aresult = $ilDB->queryF( 1500 "SELECT active_id FROM tst_active WHERE test_fi = %s AND tries >= %s AND submitted = %s", 1501 array('integer', 'integer', 'integer'), 1502 array($this->getTestId(), $this->getNrOfTries(), 0) 1503 ); 1504 while ($row = $ilDB->fetchAssoc($aresult)) { 1505 $ilDB->manipulateF( 1506 "UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s", 1507 array('integer', 'timestamp', 'integer'), 1508 array(1, date('Y-m-d H:i:s'), $row["active_id"]) 1509 ); 1510 } 1511 1512 // set all finished tests with nr of passes < allowed passes not finished 1513 $aresult = $ilDB->queryF( 1514 "SELECT active_id FROM tst_active WHERE test_fi = %s AND tries < %s AND submitted = %s", 1515 array('integer', 'integer', 'integer'), 1516 array($this->getTestId(), $this->getNrOfTries() - 1, 1) 1517 ); 1518 while ($row = $ilDB->fetchAssoc($aresult)) { 1519 $ilDB->manipulateF( 1520 "UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s", 1521 array('integer', 'timestamp', 'integer'), 1522 array(0, null, $row["active_id"]) 1523 ); 1524 } 1525 } else { 1526 // set all finished tests with nr of passes >= allowed passes not finished 1527 $aresult = $ilDB->queryF( 1528 "SELECT active_id FROM tst_active WHERE test_fi = %s AND submitted = %s", 1529 array('integer', 'integer'), 1530 array($this->getTestId(), 1) 1531 ); 1532 while ($row = $ilDB->fetchAssoc($aresult)) { 1533 $ilDB->manipulateF( 1534 "UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s", 1535 array('integer', 'timestamp', 'integer'), 1536 array(0, null, $row["active_id"]) 1537 ); 1538 } 1539 } 1540 } 1541 } 1542 1543 // news item creation/update/deletion 1544 include_once 'Services/News/classes/class.ilNewsItem.php'; 1545 if (!$this->getOldOnlineStatus() && !$this->getOfflineStatus()) { 1546 global $DIC; 1547 $ilUser = $DIC['ilUser']; 1548 $newsItem = new ilNewsItem(); 1549 $newsItem->setContext($this->getId(), 'tst'); 1550 $newsItem->setPriority(NEWS_NOTICE); 1551 $newsItem->setTitle('new_test_online'); 1552 $newsItem->setContentIsLangVar(true); 1553 $newsItem->setContent(''); 1554 $newsItem->setUserId($ilUser->getId()); 1555 $newsItem->setVisibility(NEWS_USERS); 1556 $newsItem->create(); 1557 } elseif ($this->getOldOnlineStatus() && !$this->getOfflineStatus()) { 1558 ilNewsItem::deleteNewsOfContext($this->getId(), 'tst'); 1559 } elseif (!$this->getOfflineStatus()) { 1560 $newsId = ilNewsItem::getFirstNewsIdForContext($this->getId(), 'tst'); 1561 if ($newsId > 0) { 1562 $newsItem = new ilNewsItem($newsId); 1563 $newsItem->setTitle('new_test_online'); 1564 $newsItem->setContentIsLangVar(true); 1565 $newsItem->setContent(''); 1566 $newsItem->update(); 1567 } 1568 } 1569 1570 // moved activation to ilObjectActivation 1571 if ($this->ref_id) { 1572 include_once "./Services/Object/classes/class.ilObjectActivation.php"; 1573 ilObjectActivation::getItem($this->ref_id); 1574 1575 $item = new ilObjectActivation; 1576 if (!$this->isActivationLimited()) { 1577 $item->setTimingType(ilObjectActivation::TIMINGS_DEACTIVATED); 1578 } else { 1579 $item->setTimingType(ilObjectActivation::TIMINGS_ACTIVATION); 1580 $item->setTimingStart($this->getActivationStartingTime()); 1581 $item->setTimingEnd($this->getActivationEndingTime()); 1582 $item->toggleVisible($this->getActivationVisibility()); 1583 } 1584 1585 $item->update($this->ref_id); 1586 } 1587 1588 if (!$properties_only) { 1589 if ($this->getQuestionSetType() == self::QUESTION_SET_TYPE_FIXED) { 1590 $this->saveQuestionsToDb(); 1591 } 1592 1593 $this->mark_schema->saveToDb($this->test_id); 1594 } 1595 } 1596 1597 /** 1598 * Saves the test questions to the database 1599 * 1600 * @access public 1601 * @see $questions 1602 */ 1603 public function saveQuestionsToDb() 1604 { 1605 global $DIC; 1606 $ilDB = $DIC['ilDB']; 1607 1608 $oldquestions = array(); 1609 include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php"; 1610 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 1611 $result = $ilDB->queryF( 1612 "SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence", 1613 array('integer'), 1614 array($this->getTestId()) 1615 ); 1616 if ($result->numRows() > 0) { 1617 while ($row = $ilDB->fetchAssoc($result)) { 1618 array_push($oldquestions, $row["question_fi"]); 1619 } 1620 } 1621 } 1622 // workaround for lost obligations 1623 // this method is called if a question is removed 1624 $currentQuestionsObligationsQuery = 'SELECT question_fi, obligatory FROM tst_test_question WHERE test_fi = %s'; 1625 $rset = $ilDB->queryF($currentQuestionsObligationsQuery, array('integer'), array($this->getTestId())); 1626 while ($row = $ilDB->fetchAssoc($rset)) { 1627 $obligatoryQuestionState[$row['question_fi']] = $row['obligatory']; 1628 } 1629 // delete existing category relations 1630 $affectedRows = $ilDB->manipulateF( 1631 "DELETE FROM tst_test_question WHERE test_fi = %s", 1632 array('integer'), 1633 array($this->getTestId()) 1634 ); 1635 // create new category relations 1636 foreach ($this->questions as $key => $value) { 1637 // workaround for import witout obligations information 1638 if (!isset($obligatoryQuestionState[$value]) || is_null($obligatoryQuestionState[$value])) { 1639 $obligatoryQuestionState[$value] = 0; 1640 } 1641 1642 // insert question 1643 $next_id = $ilDB->nextId('tst_test_question'); 1644 $ilDB->insert('tst_test_question', array( 1645 'test_question_id' => array('integer', $next_id), 1646 'test_fi' => array('integer', $this->getTestId()), 1647 'question_fi' => array('integer', $value), 1648 'sequence' => array('integer', $key), 1649 'obligatory' => array('integer', $obligatoryQuestionState[$value]), 1650 'tstamp' => array('integer', time()) 1651 )); 1652 } 1653 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php"); 1654 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 1655 $result = $ilDB->queryF( 1656 "SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence", 1657 array('integer'), 1658 array($this->getTestId()) 1659 ); 1660 $newquestions = array(); 1661 if ($result->numRows() > 0) { 1662 while ($row = $ilDB->fetchAssoc($result)) { 1663 array_push($newquestions, $row["question_fi"]); 1664 } 1665 } 1666 foreach ($oldquestions as $index => $question_id) { 1667 if (strcmp($newquestions[$index], $question_id) != 0) { 1668 $pos = array_search($question_id, $newquestions); 1669 if ($pos === false) { 1670 $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id); 1671 } else { 1672 $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index + 1) . " => " . ($pos + 1), $question_id); 1673 } 1674 } 1675 } 1676 foreach ($newquestions as $index => $question_id) { 1677 if (array_search($question_id, $oldquestions) === false) { 1678 $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index + 1), $question_id); 1679 } 1680 } 1681 } 1682 } 1683 1684 /** 1685 * Checks wheather the test is a new random test (using tst_rnd_cpy) or an old one 1686 * 1687 * @deprecated --> old school random test 1688 */ 1689 protected function isNewRandomTest() 1690 { 1691 global $DIC; 1692 $ilDB = $DIC['ilDB']; 1693 $result = $ilDB->queryF( 1694 'SELECT copy_id FROM tst_rnd_cpy WHERE tst_fi = %s', 1695 array('integer'), 1696 array($this->getTestId()) 1697 ); 1698 return $result->numRows() > 0; 1699 } 1700 1701 /** 1702 * Returns a random selection of questions 1703 * 1704 * @param integer $nr_of_questions Number of questions to return 1705 * @param integer $questionpool ID of questionpool to choose the questions from (0 = all available questionpools) 1706 * @param boolean $user_obj_id Use the object id instead of the reference id when set to true 1707 * @param array $qpls An array of questionpool id's if the random questions should only be chose from the contained questionpools 1708 * @return array A random selection of questions 1709 * @access public 1710 * 1711 * @deprecated --> old school random test 1712 */ 1713 public function randomSelectQuestions($nr_of_questions, $questionpool, $use_obj_id = 0, $qpls = "", $pass = null) 1714 { 1715 global $DIC; 1716 $rbacsystem = $DIC['rbacsystem']; 1717 $ilDB = $DIC['ilDB']; 1718 1719 // retrieve object id instead of ref id if necessary 1720 if (($questionpool != 0) && (!$use_obj_id)) { 1721 $questionpool = ilObject::_lookupObjId($questionpool); 1722 } 1723 1724 // get original ids of all existing questions in the test 1725 $result = $ilDB->queryF( 1726 "SELECT qpl_questions.original_id FROM qpl_questions, tst_test_question WHERE qpl_questions.question_id = tst_test_question.question_fi AND qpl_questions.tstamp > 0 AND tst_test_question.test_fi = %s", 1727 array("integer"), 1728 array($this->getTestId()) 1729 ); 1730 $original_ids = array(); 1731 $paramtypes = array(); 1732 $paramvalues = array(); 1733 while ($row = $ilDB->fetchAssoc($result)) { 1734 array_push($original_ids, $row['original_id']); 1735 } 1736 1737 $available = ""; 1738 // get a list of all available questionpools 1739 if (($questionpool == 0) && (!is_array($qpls))) { 1740 include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php"; 1741 $available_pools = array_keys(ilObjQuestionPool::_getAvailableQuestionpools($use_object_id = true, $equal_points = false, $could_be_offline = false, $showPath = false, $with_questioncount = false, "read", ilObject::_lookupOwner($this->getId()))); 1742 if (count($available_pools)) { 1743 $available = " AND " . $ilDB->in('obj_fi', $available_pools, false, 'integer'); 1744 } else { 1745 return array(); 1746 } 1747 } 1748 1749 $constraint_qpls = ""; 1750 $result_array = array(); 1751 if ($questionpool == 0) { 1752 if (is_array($qpls)) { 1753 if (count($qpls) > 0) { 1754 $constraint_qpls = " AND " . $ilDB->in('obj_fi', $qpls, false, 'integer'); 1755 } 1756 } 1757 } 1758 1759 $original_clause = ""; 1760 if (count($original_ids)) { 1761 $original_clause = " AND " . $ilDB->in('question_id', $original_ids, true, 'integer'); 1762 } 1763 1764 if ($questionpool == 0) { 1765 $result = $ilDB->queryF( 1766 "SELECT question_id FROM qpl_questions WHERE original_id IS NULL $available $constraint_qpls AND owner > %s AND complete = %s $original_clause", 1767 array('integer', 'text'), 1768 array(0, "1") 1769 ); 1770 } else { 1771 $result = $ilDB->queryF( 1772 "SELECT question_id FROM qpl_questions WHERE original_id IS NULL AND obj_fi = %s AND owner > %s AND complete = %s $original_clause", 1773 array('integer','integer', 'text'), 1774 array($questionpool, 0, "1") 1775 ); 1776 } 1777 $found_ids = array(); 1778 while ($row = $ilDB->fetchAssoc($result)) { 1779 array_push($found_ids, $row['question_id']); 1780 } 1781 $nr_of_questions = ($nr_of_questions > count($found_ids)) ? count($found_ids) : $nr_of_questions; 1782 if ($nr_of_questions == 0) { 1783 return array(); 1784 } 1785 $rand_keys = array_rand($found_ids, $nr_of_questions); 1786 $result = array(); 1787 if (is_array($rand_keys)) { 1788 foreach ($rand_keys as $key) { 1789 $result[$found_ids[$key]] = $found_ids[$key]; 1790 } 1791 } else { 1792 $result[$found_ids[$rand_keys]] = $found_ids[$rand_keys]; 1793 } 1794 return $result; 1795 } 1796 1797 /** 1798 * Calculates the number of user results for a specific test pass 1799 * 1800 * @access private 1801 * 1802 * @deprecated: still in use? 1803 */ 1804 public function getNrOfResultsForPass($active_id, $pass) 1805 { 1806 global $DIC; 1807 $ilDB = $DIC['ilDB']; 1808 1809 $result = $ilDB->queryF( 1810 "SELECT test_result_id FROM tst_test_result WHERE active_fi = %s AND pass = %s", 1811 array('integer','integer'), 1812 array($active_id, $pass) 1813 ); 1814 return $result->numRows(); 1815 } 1816 1817 /** 1818 * Checkes wheather a random test has already created questions for a given pass or not 1819 * 1820 * @access private 1821 * @param $active_id Active id of the test 1822 * @param $pass Pass of the test 1823 * @return boolean TRUE if the test already contains questions, FALSE otherwise 1824 * 1825 * @deprecated: still in use? 1826 */ 1827 public function hasRandomQuestionsForPass($active_id, $pass) 1828 { 1829 global $DIC; 1830 $ilDB = $DIC['ilDB']; 1831 $result = $ilDB->queryF( 1832 "SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s", 1833 array('integer','integer'), 1834 array($active_id, $pass) 1835 ); 1836 return ($result->numRows() > 0) ? true : false; 1837 } 1838 1839 /** 1840 * Loads a ilObjTest object from a database 1841 */ 1842 public function loadFromDb() 1843 { 1844 global $DIC; 1845 $ilDB = $DIC['ilDB']; 1846 1847 $result = $ilDB->queryF( 1848 "SELECT * FROM tst_tests WHERE obj_fi = %s", 1849 array('integer'), 1850 array($this->getId()) 1851 ); 1852 if ($result->numRows() == 1) { 1853 $data = $ilDB->fetchObject($result); 1854 $this->setTestId($data->test_id); 1855 if (strlen($this->getAuthor()) == 0) { 1856 $this->saveAuthorToMetadata($data->author); 1857 } 1858 $this->setAuthor($data->author); 1859 include_once("./Services/RTE/classes/class.ilRTE.php"); 1860 $this->setIntroductionEnabled($data->intro_enabled); 1861 $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc($data->introduction, 1)); 1862 $this->setShowInfo($data->showinfo); 1863 $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc($data->finalstatement, 1)); 1864 $this->setForceJS($data->forcejs); 1865 $this->setCustomStyle($data->customstyle); 1866 $this->setShowFinalStatement($data->showfinalstatement); 1867 $this->setSequenceSettings($data->sequence_settings); 1868 $this->setScoreReporting($data->score_reporting); 1869 $this->setInstantFeedbackSolution($data->instant_verification); 1870 $this->setAnswerFeedbackPoints($data->answer_feedback_points); 1871 $this->setAnswerFeedback($data->answer_feedback); 1872 $this->setAnonymity($data->anonymity); 1873 $this->setShowCancel($data->show_cancel); 1874 $this->setShowMarker($data->show_marker); 1875 $this->setFixedParticipants($data->fixed_participants); 1876 $this->setNrOfTries($data->nr_of_tries); 1877 $this->setBlockPassesAfterPassedEnabled((bool) $data->block_after_passed); 1878 $this->setKiosk($data->kiosk); 1879 $this->setUsePreviousAnswers($data->use_previous_answers); 1880 $this->setRedirectionMode($data->redirection_mode); 1881 $this->setRedirectionUrl($data->redirection_url); 1882 $this->setTitleOutput($data->title_output); 1883 $this->setProcessingTime($data->processing_time); 1884 $this->setEnableProcessingTime($data->enable_processing_time); 1885 $this->setResetProcessingTime($data->reset_processing_time); 1886 $this->setReportingDate($data->reporting_date); 1887 $this->setShuffleQuestions($data->shuffle_questions); 1888 $this->setResultsPresentation($data->results_presentation); 1889 $this->setStartingTimeEnabled($data->starting_time_enabled); 1890 $this->setStartingTime($data->starting_time); 1891 $this->setEndingTimeEnabled($data->ending_time_enabled); 1892 $this->setEndingTime($data->ending_time); 1893 $this->setListOfQuestionsSettings($data->show_summary); 1894 $this->setECTSOutput($data->ects_output); 1895 $this->setECTSGrades( 1896 array( 1897 "A" => $data->ects_a, 1898 "B" => $data->ects_b, 1899 "C" => $data->ects_c, 1900 "D" => $data->ects_d, 1901 "E" => $data->ects_e 1902 ) 1903 ); 1904 $this->setECTSFX($data->ects_fx); 1905 $this->mark_schema->flush(); 1906 $this->mark_schema->loadFromDb($this->getTestId()); 1907 $this->setCountSystem($data->count_system); 1908 $this->setMCScoring($data->mc_scoring); 1909 $this->setMailNotification($data->mailnotification); 1910 $this->setMailNotificationType($data->mailnottype); 1911 $this->setExportSettings($data->exportsettings); 1912 $this->setScoreCutting($data->score_cutting); 1913 $this->setPasswordEnabled($data->password_enabled); 1914 $this->setPassword($data->password); 1915 $this->setLimitUsersEnabled($data->limit_users_enabled); 1916 $this->setAllowedUsers($data->allowedusers); 1917 $this->setAllowedUsersTimeGap($data->alloweduserstimegap); 1918 $this->setPassScoring($data->pass_scoring); 1919 $this->setObligationsEnabled($data->obligations_enabled); 1920 $this->setOfferingQuestionHintsEnabled($data->offer_question_hints); 1921 $this->setCertificateVisibility($data->certificate_visibility); 1922 $this->setEnabledViewMode($data->enabled_view_mode); 1923 $this->setTemplate($data->template_id); 1924 $this->setPoolUsage($data->pool_usage); 1925 $this->setPrintBestSolutionWithResult((bool) $data->print_bs_with_res); 1926 $this->setHighscoreEnabled((bool) $data->highscore_enabled); 1927 $this->setHighscoreAnon((bool) $data->highscore_anon); 1928 $this->setHighscoreAchievedTS((bool) $data->highscore_achieved_ts); 1929 $this->setHighscoreScore((bool) $data->highscore_score); 1930 $this->setHighscorePercentage((bool) $data->highscore_percentage); 1931 $this->setHighscoreHints((bool) $data->highscore_hints); 1932 $this->setHighscoreWTime((bool) $data->highscore_wtime); 1933 $this->setHighscoreOwnTable((bool) $data->highscore_own_table); 1934 $this->setHighscoreTopTable((bool) $data->highscore_top_table); 1935 $this->setHighscoreTopNum((int) $data->highscore_top_num); 1936 $this->setOldOnlineStatus((bool) !$this->getOfflineStatus()); 1937 $this->setSpecificAnswerFeedback((int) $data->specific_feedback); 1938 $this->setAutosave((bool) $data->autosave); 1939 $this->setAutosaveIval((int) $data->autosave_ival); 1940 $this->setPassDeletionAllowed($data->pass_deletion_allowed); 1941 $this->setEnableExamview((bool) $data->enable_examview); 1942 $this->setShowExamviewHtml((bool) $data->show_examview_html); 1943 $this->setShowExamviewPdf((bool) $data->show_examview_pdf); 1944 $this->setEnableArchiving((bool) $data->enable_archiving); 1945 $this->setShowExamIdInTestPassEnabled((bool) $data->examid_in_test_pass); 1946 $this->setShowExamIdInTestResultsEnabled((bool) $data->examid_in_test_res); 1947 $this->setSignSubmission((bool) $data->sign_submission); 1948 $this->setQuestionSetType($data->question_set_type); 1949 $this->setCharSelectorAvailability((int) $data->char_selector_availability); 1950 $this->setCharSelectorDefinition($data->char_selector_definition); 1951 $this->setSkillServiceEnabled((bool) $data->skill_service); 1952 $this->setResultFilterTaxIds(strlen($data->result_tax_filters) ? unserialize($data->result_tax_filters) : array()); 1953 $this->setShowGradingStatusEnabled((bool) $data->show_grading_status); 1954 $this->setShowGradingMarkEnabled((bool) $data->show_grading_mark); 1955 $this->setFollowupQuestionAnswerFixationEnabled((bool) $data->follow_qst_answer_fixation); 1956 $this->setInstantFeedbackAnswerFixationEnabled((bool) $data->inst_fb_answer_fixation); 1957 $this->setForceInstantFeedbackEnabled((bool) $data->force_inst_fb); 1958 $this->setTestFinalBroken((bool) $data->broken); 1959 $this->setPassWaiting($data->pass_waiting); 1960 $this->loadQuestions(); 1961 } 1962 1963 // moved activation to ilObjectActivation 1964 if ($this->ref_id) { 1965 include_once "./Services/Object/classes/class.ilObjectActivation.php"; 1966 $activation = ilObjectActivation::getItem($this->ref_id); 1967 switch ($activation["timing_type"]) { 1968 case ilObjectActivation::TIMINGS_ACTIVATION: 1969 $this->setActivationLimited(true); 1970 $this->setActivationStartingTime($activation["timing_start"]); 1971 $this->setActivationEndingTime($activation["timing_end"]); 1972 $this->setActivationVisibility($activation["visible"]); 1973 break; 1974 1975 default: 1976 $this->setActivationLimited(false); 1977 break; 1978 } 1979 } 1980 } 1981 1982 /** 1983 * Load the test question id's from the database 1984 * 1985 * @param integer $user_id The user id of the test user (necessary for random tests) 1986 * @access public 1987 */ 1988 public function loadQuestions($active_id = "", $pass = null) 1989 { 1990 global $DIC; 1991 $ilUser = $DIC['ilUser']; 1992 $ilDB = $DIC['ilDB']; 1993 1994 $this->questions = array(); 1995 if ($this->isRandomTest()) { 1996 if (strcmp($active_id, "") == 0) { 1997 $active_id = $this->getActiveIdOfUser($ilUser->getId()); 1998 } 1999 if (is_null($pass)) { 2000 $pass = self::_getPass($active_id); 2001 } 2002 $result = $ilDB->queryF( 2003 "SELECT tst_test_rnd_qst.* FROM tst_test_rnd_qst, qpl_questions WHERE tst_test_rnd_qst.active_fi = %s AND qpl_questions.question_id = tst_test_rnd_qst.question_fi AND tst_test_rnd_qst.pass = %s ORDER BY sequence", 2004 array('integer', 'integer'), 2005 array($active_id, $pass) 2006 ); 2007 // The following is a fix for random tests prior to ILIAS 3.8. If someone started a random test in ILIAS < 3.8, there 2008 // is only one test pass (pass = 0) in tst_test_rnd_qst while with ILIAS 3.8 there are questions for every test pass. 2009 // To prevent problems with tests started in an older version and continued in ILIAS 3.8, the first pass should be taken if 2010 // no questions are present for a newer pass. 2011 if ($result->numRows() == 0) { 2012 $result = $ilDB->queryF( 2013 "SELECT tst_test_rnd_qst.* FROM tst_test_rnd_qst, qpl_questions WHERE tst_test_rnd_qst.active_fi = %s AND qpl_questions.question_id = tst_test_rnd_qst.question_fi AND tst_test_rnd_qst.pass = 0 ORDER BY sequence", 2014 array('integer'), 2015 array($active_id) 2016 ); 2017 } 2018 } else { 2019 $result = $ilDB->queryF( 2020 "SELECT tst_test_question.* FROM tst_test_question, qpl_questions WHERE tst_test_question.test_fi = %s AND qpl_questions.question_id = tst_test_question.question_fi ORDER BY sequence", 2021 array('integer'), 2022 array($this->test_id) 2023 ); 2024 } 2025 $index = 1; 2026 while ($data = $ilDB->fetchAssoc($result)) { 2027 $this->questions[$index++] = $data["question_fi"]; 2028 } 2029 } 2030 2031 /** 2032 * @return boolean 2033 */ 2034 public function isIntroductionEnabled() 2035 { 2036 return $this->introductionEnabled; 2037 } 2038 2039 /** 2040 * @param boolean $introductionEnabled 2041 */ 2042 public function setIntroductionEnabled($introductionEnabled) 2043 { 2044 $this->introductionEnabled = $introductionEnabled; 2045 } 2046 2047 /** 2048 * Gets the introduction text of the ilObjTest object 2049 * 2050 * @return mixed The introduction text of the test, NULL if empty 2051 * @see $introduction 2052 */ 2053 public function getIntroduction() 2054 { 2055 return (strlen($this->introduction)) ? $this->introduction : null; 2056 } 2057 2058 /** 2059 * Sets the introduction text of the ilObjTest object 2060 * 2061 * @param string $introduction An introduction string for the test 2062 * @access public 2063 * @see $introduction 2064 */ 2065 public function setIntroduction($introduction = "") 2066 { 2067 $this->introduction = $introduction; 2068 } 2069 2070 2071 /** 2072 * Sets the final statement text of the ilObjTest object 2073 * 2074 * @param string $a_statement A final statement 2075 * @access public 2076 * @see $_finalstatement 2077 */ 2078 public function setFinalStatement($a_statement = "") 2079 { 2080 $this->_finalstatement = $a_statement; 2081 } 2082 2083 /** 2084 * Set whether the complete information page is shown or the required data only 2085 * 2086 * @param integer $a_info 1 for the complete information, 0 otherwise 2087 * @access public 2088 * @see $_showinfo 2089 */ 2090 public function setShowInfo($a_info = 1) 2091 { 2092 $this->_showinfo = ($a_info) ? 1 : 0; 2093 } 2094 2095 /** 2096 * Set whether JavaScript should be forced for tests 2097 * 2098 * @param integer $a_js 1 to force JavaScript, 0 otherwise 2099 * @access public 2100 * @see $_forcejs 2101 */ 2102 public function setForceJS($a_js = 1) 2103 { 2104 $this->_forcejs = ($a_js) ? 1 : 0; 2105 } 2106 2107 /** 2108 * Set the custom style 2109 * 2110 * @param string $a_customStyle The custom style 2111 * @access public 2112 * @see $_customStyle 2113 */ 2114 public function setCustomStyle($a_customStyle = null) 2115 { 2116 $this->_customStyle = $a_customStyle; 2117 } 2118 2119 /** 2120 * Get the custom style 2121 * 2122 * @return mixed The custom style, NULL if empty 2123 * @access public 2124 * @see $_customStyle 2125 */ 2126 public function getCustomStyle() 2127 { 2128 return (strlen($this->_customStyle)) ? $this->_customStyle : null; 2129 } 2130 2131 /** 2132 * Return the available custom styles 2133 * 2134 * @return array An array of strings containing the available custom styles 2135 * @access public 2136 * @see $_customStyle 2137 */ 2138 public function getCustomStyles() 2139 { 2140 $css_path = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test"); 2141 $css_path = str_replace("ta.css", "customstyles", $css_path) . "/"; 2142 $customstyles = array(); 2143 if (is_dir($css_path)) { 2144 $results = array(); 2145 include_once "./Services/Utilities/classes/class.ilFileUtils.php"; 2146 ilFileUtils::recursive_dirscan($css_path, $results); 2147 if (is_array($results["file"])) { 2148 foreach ($results["file"] as $filename) { 2149 if (strpos($filename, ".css")) { 2150 array_push($customstyles, $filename); 2151 } 2152 } 2153 } 2154 } 2155 return $customstyles; 2156 } 2157 2158 /** 2159 * get full style sheet file name (path inclusive) of current user 2160 * 2161 * @param $mode string Output mode of the style sheet ("output" or "filesystem"). !"filesystem" generates the ILIAS 2162 * version number as attribute to force the reload of the style sheet in a different ILIAS version 2163 * @access public 2164 */ 2165 public function getTestStyleLocation($mode = "output") 2166 { 2167 if (strlen($this->getCustomStyle())) { 2168 $default = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test"); 2169 $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $default); 2170 if (file_exists($custom)) { 2171 $custom = ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test"); 2172 $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $custom); 2173 return $custom; 2174 } else { 2175 return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test"); 2176 } 2177 } else { 2178 return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test"); 2179 } 2180 } 2181 2182 /** 2183 * Sets whether the final statement should be shown or not 2184 * 2185 * @param integer $show 1 if TRUE or 0 if FALSE 2186 * @access public 2187 * @see $_finalstatement 2188 */ 2189 public function setShowFinalStatement($show = 0) 2190 { 2191 $this->_showfinalstatement = ($show) ? 1 : 0; 2192 } 2193 2194 /** 2195 * Gets the final statement 2196 * 2197 * @return mixed The final statement, NULL if empty 2198 * @see $_finalstatement 2199 */ 2200 public function getFinalStatement() 2201 { 2202 return (strlen($this->_finalstatement)) ? $this->_finalstatement : null; 2203 } 2204 2205 /** 2206 * Gets whether the complete information page is shown or the required data only 2207 * 2208 * @return integer 1 for the complete information, 0 otherwise 2209 * @access public 2210 * @see $_showinfo 2211 */ 2212 public function getShowInfo() 2213 { 2214 return ($this->_showinfo) ? 1 : 0; 2215 } 2216 2217 /** 2218 * Gets whether JavaScript should be forced for tests 2219 * 2220 * @return integer 1 to force JavaScript, 0 otherwise 2221 * @access public 2222 * @see $_forcejs 2223 */ 2224 public function getForceJS() 2225 { 2226 return ($this->_forcejs) ? 1 : 0; 2227 } 2228 2229 /** 2230 * Returns whether the final statement should be shown or not 2231 * 2232 * @return integer 0 if false, 1 if true 2233 * @access public 2234 * @see $_showfinalstatement 2235 */ 2236 public function getShowFinalStatement() 2237 { 2238 return ($this->_showfinalstatement) ? 1 : 0; 2239 } 2240 2241 /** 2242 * Gets the database id of the additional test data 2243 * 2244 * @return integer The database id of the additional test data 2245 * @access public 2246 * @see $test_id 2247 */ 2248 public function getTestId() 2249 { 2250 return $this->test_id; 2251 } 2252 2253 /** 2254 * {@inheritdoc} 2255 */ 2256 public function getECTSOutput() 2257 { 2258 return ($this->ects_output) ? 1 : 0; 2259 } 2260 2261 /** 2262 * {@inheritdoc} 2263 */ 2264 public function setECTSOutput($a_ects_output) 2265 { 2266 $this->ects_output = $a_ects_output ? 1 : 0; 2267 } 2268 2269 /** 2270 * {@inheritdoc} 2271 */ 2272 public function getECTSFX() 2273 { 2274 return (strlen($this->ects_fx)) ? $this->ects_fx : null; 2275 } 2276 2277 /** 2278 * {@inheritdoc} 2279 */ 2280 public function setECTSFX($a_ects_fx) 2281 { 2282 $this->ects_fx = $a_ects_fx; 2283 } 2284 2285 /** 2286 * {@inheritdoc} 2287 */ 2288 public function getECTSGrades() 2289 { 2290 return $this->ects_grades; 2291 } 2292 2293 /** 2294 * {@inheritdoc} 2295 */ 2296 public function setECTSGrades(array $a_ects_grades) 2297 { 2298 $this->ects_grades = $a_ects_grades; 2299 } 2300 2301 /** 2302 * SEQUENCE SETTING = POSTPONING ENABLED !! 2303 * 2304 * @return integer The POSTPONING ENABLED status 2305 */ 2306 public function getSequenceSettings() 2307 { 2308 return ($this->sequence_settings) ? $this->sequence_settings : 0; 2309 } 2310 2311 /** 2312 * SEQUENCE SETTING = POSTPONING ENABLED !! 2313 * 2314 * @param integer $sequence_settings The POSTPONING ENABLED status 2315 */ 2316 public function setSequenceSettings($sequence_settings = 0) 2317 { 2318 $this->sequence_settings = $sequence_settings; 2319 } 2320 2321 /** 2322 * @return bool $postponingEnabled 2323 */ 2324 public function isPostponingEnabled() 2325 { 2326 return (bool) $this->getSequenceSettings(); 2327 } 2328 2329 /** 2330 * @param bool $postponingEnabled 2331 */ 2332 public function setPostponingEnabled($postponingEnabled) 2333 { 2334 $this->setSequenceSettings((int) $postponingEnabled); 2335 } 2336 2337 /** 2338* Sets the score reporting of the ilObjTest object 2339* 2340* @param integer $score_reporting The score reporting 2341* @access public 2342* @see $score_reporting 2343*/ 2344 public function setScoreReporting($score_reporting = 0) 2345 { 2346 $this->score_reporting = $score_reporting; 2347 } 2348 2349 /** 2350 * Sets the instant feedback for the solution 2351 * 2352 * @param integer $instant_feedback If 1, the solution will be shown after answering a question 2353 * @access public 2354 * @see $instant_verification 2355 */ 2356 public function setInstantFeedbackSolution($instant_feedback = 0) 2357 { 2358 switch ($instant_feedback) { 2359 case 1: 2360 $this->instant_verification = 1; 2361 break; 2362 default: 2363 $this->instant_verification = 0; 2364 break; 2365 } 2366 } 2367 2368 /** 2369 * Sets the generic feedback for the test 2370 * @deprecate Use setGenericAnswerFeedback instead. 2371 * @param integer $answer_feedback If 1, answer specific feedback will be shown after answering a question 2372 * @access public 2373 * @see $answer_feedback 2374 */ 2375 public function setAnswerFeedback($answer_feedback = 0) 2376 { 2377 switch ($answer_feedback) { 2378 case 1: 2379 $this->answer_feedback = 1; 2380 break; 2381 default: 2382 $this->answer_feedback = 0; 2383 break; 2384 } 2385 } 2386 2387 /** 2388 * Sets if the generic feedback is to be shown in the test. 2389 * 2390 * @param int $generic_answer_feedback 2391 */ 2392 public function setGenericAnswerFeedback($generic_answer_feedback = 0) 2393 { 2394 switch ($generic_answer_feedback) { 2395 case 1: 2396 $this->answer_feedback = 1; 2397 break; 2398 default: 2399 $this->answer_feedback = 0; 2400 break; 2401 } 2402 } 2403 2404 /** 2405 * Sets the answer specific feedback of reached points for the test 2406 * 2407 * @param integer $answer_feedback_points If 1, answer specific feedback will show the reached points after answering a question 2408 * @access public 2409 * @see $answer_feedback_points 2410 */ 2411 public function setAnswerFeedbackPoints($answer_feedback_points = 0) 2412 { 2413 switch ($answer_feedback_points) { 2414 case 1: 2415 $this->answer_feedback_points = 1; 2416 break; 2417 default: 2418 $this->answer_feedback_points = 0; 2419 break; 2420 } 2421 } 2422 2423 /** 2424 * Sets the reporting date of the ilObjTest object 2425 * @param timestamp $reporting_date The date and time the score reporting is available 2426 */ 2427 public function setReportingDate($reporting_date) 2428 { 2429 if (!$reporting_date) { 2430 $this->reporting_date = ''; 2431 $this->setECTSOutput(false); 2432 } else { 2433 $this->reporting_date = $reporting_date; 2434 } 2435 } 2436 2437 const SCORE_REPORTING_DISABLED = 0; 2438 const SCORE_REPORTING_FINISHED = 1; 2439 const SCORE_REPORTING_IMMIDIATLY = 2; 2440 const SCORE_REPORTING_DATE = 3; 2441 const SCORE_REPORTING_AFTER_PASSED = 4; 2442 2443 /** 2444 * Gets the score reporting of the ilObjTest object 2445 * 2446 * @return integer The score reporting of the test 2447 * @access public 2448 * @see $score_reporting 2449 */ 2450 public function getScoreReporting() 2451 { 2452 return ($this->score_reporting) ? $this->score_reporting : 0; 2453 } 2454 2455 public function isScoreReportingEnabled() 2456 { 2457 switch ($this->getScoreReporting()) { 2458 case self::SCORE_REPORTING_FINISHED: 2459 case self::SCORE_REPORTING_IMMIDIATLY: 2460 case self::SCORE_REPORTING_DATE: 2461 case self::SCORE_REPORTING_AFTER_PASSED: 2462 2463 return true; 2464 2465 case self::SCORE_REPORTING_DISABLED: 2466 default: 2467 2468 return false; 2469 } 2470 } 2471 2472 /** 2473 * Returns 1 if the correct solution will be shown after answering a question 2474 * 2475 * @return integer The status of the solution instant feedback 2476 * @access public 2477 * @see $instant_verification 2478 */ 2479 public function getInstantFeedbackSolution() 2480 { 2481 return ($this->instant_verification) ? $this->instant_verification : 0; 2482 } 2483 2484 /** 2485 * Returns 1 if generic answer feedback is activated 2486 * 2487 * @deprecated Use getGenericAnswerFeedback instead. 2488 * @return integer The status of the answer specific feedback 2489 * @access public 2490 * @see $answer_feedback 2491 */ 2492 public function getAnswerFeedback() 2493 { 2494 return ($this->answer_feedback) ? $this->answer_feedback : 0; 2495 } 2496 2497 /** 2498 * Returns 1 if generic answer feedback is to be shown. 2499 * 2500 * @return integer 1, if answer specific feedback is to be shown. 2501 * @access public 2502 */ 2503 public function getGenericAnswerFeedback() 2504 { 2505 return ($this->answer_feedback) ? $this->answer_feedback : 0; 2506 } 2507 2508 /** 2509 * Returns 1 if answer specific feedback as reached points is activated 2510 * 2511 * @return integer The status of the answer specific feedback as reached points 2512 * @access public 2513 * @see $answer_feedback_points 2514 */ 2515 public function getAnswerFeedbackPoints() 2516 { 2517 return ($this->answer_feedback_points) ? $this->answer_feedback_points : 0; 2518 } 2519 2520 /** 2521 * Gets the count system for the calculation of points 2522 * 2523 * @return integer The count system for the calculation of points 2524 * @access public 2525 * @see $count_system 2526 */ 2527 public function getCountSystem() 2528 { 2529 return ($this->count_system) ? $this->count_system : 0; 2530 } 2531 2532 /** 2533 * Gets the count system for the calculation of points 2534 * 2535 * @return integer The count system for the calculation of points 2536 * @access public 2537 * @see $count_system 2538 */ 2539 public static function _getCountSystem($active_id) 2540 { 2541 global $DIC; 2542 $ilDB = $DIC['ilDB']; 2543 $result = $ilDB->queryF( 2544 "SELECT tst_tests.count_system FROM tst_tests, tst_active WHERE tst_active.active_id = %s AND tst_active.test_fi = tst_tests.test_id", 2545 array('integer'), 2546 array($active_id) 2547 ); 2548 if ($result->numRows()) { 2549 $row = $ilDB->fetchAssoc($result); 2550 return $row["count_system"]; 2551 } 2552 return false; 2553 } 2554 2555 /** 2556 * Gets the scoring type for multiple choice questions 2557 * 2558 * @return integer The scoring type for multiple choice questions 2559 * @access public 2560 * @see $mc_scoring 2561 */ 2562 public function getMCScoring() 2563 { 2564 return ($this->mc_scoring) ? $this->mc_scoring : 0; 2565 } 2566 2567 /** 2568 * Determines if the score of a question should be cut at 0 points or the score of the whole test 2569 * 2570 * @return integer The score cutting type. 0 for question cutting, 1 for test cutting 2571 * @access public 2572 * @see $score_cutting 2573 */ 2574 public function getScoreCutting() 2575 { 2576 return ($this->score_cutting) ? $this->score_cutting : 0; 2577 } 2578 2579 /** 2580 * Gets the pass scoring type 2581 * 2582 * @return integer The pass scoring type 2583 * @access public 2584 * @see $pass_scoring 2585 */ 2586 public function getPassScoring() 2587 { 2588 return ($this->pass_scoring) ? $this->pass_scoring : 0; 2589 } 2590 2591 /** 2592 * Gets the pass scoring type 2593 * 2594 * @return integer The pass scoring type 2595 * @access public 2596 * @see $pass_scoring 2597 */ 2598 public static function _getPassScoring($active_id) 2599 { 2600 global $DIC; 2601 $ilDB = $DIC['ilDB']; 2602 $result = $ilDB->queryF( 2603 "SELECT tst_tests.pass_scoring FROM tst_tests, tst_active WHERE tst_tests.test_id = tst_active.test_fi AND tst_active.active_id = %s", 2604 array('integer'), 2605 array($active_id) 2606 ); 2607 if ($result->numRows()) { 2608 $row = $ilDB->fetchAssoc($result); 2609 return $row["pass_scoring"]; 2610 } 2611 return 0; 2612 } 2613 2614 /** 2615 * Gets the scoring type for multiple choice questions 2616 * 2617 * @return mixed The scoring type for multiple choice questions 2618 * @access public 2619 * @see $mc_scoring 2620 */ 2621 public static function _getMCScoring($active_id) 2622 { 2623 global $DIC; 2624 $ilDB = $DIC['ilDB']; 2625 $result = $ilDB->queryF( 2626 "SELECT tst_tests.mc_scoring FROM tst_tests, tst_active WHERE tst_active.active_id = %s AND tst_active.test_fi = tst_tests.test_id", 2627 array('integer'), 2628 array($active_id) 2629 ); 2630 if ($result->numRows()) { 2631 $row = $ilDB->fetchAssoc($result); 2632 return $row["mc_scoring"]; 2633 } 2634 return false; 2635 } 2636 2637 /** 2638 * Determines if the score of a question should be cut at 0 points or the score of the whole test 2639 * 2640 * @return boolean The score cutting type. 0 for question cutting, 1 for test cutting 2641 * @access public 2642 * @see $score_cutting 2643 */ 2644 public static function _getScoreCutting($active_id) 2645 { 2646 global $DIC; 2647 $ilDB = $DIC['ilDB']; 2648 $result = $ilDB->queryF( 2649 "SELECT tst_tests.score_cutting FROM tst_tests, tst_active WHERE tst_active.active_id = %s AND tst_tests.test_id = tst_active.test_fi", 2650 array('integer'), 2651 array($active_id) 2652 ); 2653 if ($result->numRows()) { 2654 $row = $ilDB->fetchAssoc($result); 2655 return $row["score_cutting"]; 2656 } 2657 return false; 2658 } 2659 2660 /** 2661 * Gets the reporting date of the ilObjTest object 2662 * 2663 * @return string The reporting date of the test of an empty string (=FALSE) if no reporting date is set 2664 * @access public 2665 * @see $reporting_date 2666 */ 2667 public function getReportingDate() 2668 { 2669 return (strlen($this->reporting_date)) ? $this->reporting_date : null; 2670 } 2671 2672 /** 2673 * Returns the nr of tries for the test 2674 * 2675 * @return integer The maximum number of tries 2676 * @access public 2677 * @see $nr_of_tries 2678 */ 2679 public function getNrOfTries() 2680 { 2681 return ($this->nr_of_tries) ? $this->nr_of_tries : 0; 2682 } 2683 2684 /** 2685 * @return bool 2686 */ 2687 public function isBlockPassesAfterPassedEnabled() 2688 { 2689 return $this->blockPassesAfterPassedEnabled; 2690 } 2691 2692 /** 2693 * @param bool $blockPassesAfterPassedEnabled 2694 */ 2695 public function setBlockPassesAfterPassedEnabled($blockPassesAfterPassedEnabled) 2696 { 2697 $this->blockPassesAfterPassedEnabled = $blockPassesAfterPassedEnabled; 2698 } 2699 2700 /** 2701 * Returns the kiosk mode 2702 * 2703 * @return integer Kiosk mode 2704 * @access public 2705 * @see $_kiosk 2706 */ 2707 public function getKiosk() 2708 { 2709 return ($this->_kiosk) ? $this->_kiosk : 0; 2710 } 2711 2712 2713 /** 2714 * Sets the kiosk mode for the test 2715 * 2716 * @param integer $kiosk The value for the kiosk mode. 2717 * @access public 2718 * @see $_kiosk 2719 */ 2720 public function setKiosk($kiosk = 0) 2721 { 2722 $this->_kiosk = $kiosk; 2723 } 2724 2725 /** 2726 * Returns the kiosk mode 2727 * 2728 * @return boolean Kiosk mode 2729 * @access public 2730 * @see $_kiosk 2731 */ 2732 public function getKioskMode() 2733 { 2734 if (($this->_kiosk & 1) > 0) { 2735 return true; 2736 } else { 2737 return false; 2738 } 2739 } 2740 2741 /** 2742 * Sets the kiosk mode for the test 2743 * 2744 * @param boolean $kiosk The value for the kiosk mode 2745 * @access public 2746 * @see $_kiosk 2747 */ 2748 public function setKioskMode($a_kiosk = false) 2749 { 2750 if ($a_kiosk) { 2751 $this->_kiosk = $this->_kiosk | 1; 2752 } else { 2753 if ($this->getKioskMode()) { 2754 $this->_kiosk = $this->_kiosk ^ 1; 2755 } 2756 } 2757 } 2758 2759 /** 2760 * Returns the status of the kiosk mode title 2761 * 2762 * @return boolean Kiosk mode title 2763 * @access public 2764 * @see $_kiosk 2765 */ 2766 public function getShowKioskModeTitle() 2767 { 2768 if (($this->_kiosk & 2) > 0) { 2769 return true; 2770 } else { 2771 return false; 2772 } 2773 } 2774 2775 /** 2776 * Set to true, if the full test title should be shown in kiosk mode 2777 * 2778 * @param boolean $a_title TRUE if the test title should be shown in kiosk mode, FALSE otherwise 2779 * @access public 2780 */ 2781 public function setShowKioskModeTitle($a_title = false) 2782 { 2783 if ($a_title) { 2784 $this->_kiosk = $this->_kiosk | 2; 2785 } else { 2786 if ($this->getShowKioskModeTitle()) { 2787 $this->_kiosk = $this->_kiosk ^ 2; 2788 } 2789 } 2790 } 2791 2792 /** 2793 * Returns the status of the kiosk mode participant 2794 * 2795 * @return boolean Kiosk mode participant 2796 * @access public 2797 * @see $_kiosk 2798 */ 2799 public function getShowKioskModeParticipant() 2800 { 2801 if (($this->_kiosk & 4) > 0) { 2802 return true; 2803 } else { 2804 return false; 2805 } 2806 } 2807 2808 /** 2809 * Set to true, if the participant's name should be shown in kiosk mode 2810 * 2811 * @param boolean $a_title TRUE if the participant's name should be shown in kiosk mode, FALSE otherwise 2812 * @access public 2813 */ 2814 public function setShowKioskModeParticipant($a_participant = false) 2815 { 2816 if ($a_participant) { 2817 $this->_kiosk = $this->_kiosk | 4; 2818 } else { 2819 if ($this->getShowKioskModeParticipant()) { 2820 $this->_kiosk = $this->_kiosk ^ 4; 2821 } 2822 } 2823 } 2824 2825 /** 2826 * Returns if the previous answers should be shown for a learner 2827 * 2828 * @return integer 1 if the previous answers should be shown, 0 otherwise 2829 * @access public 2830 * @see $use_previous_answers 2831 */ 2832 public function getUsePreviousAnswers() 2833 { 2834 return ($this->use_previous_answers) ? $this->use_previous_answers : 0; 2835 } 2836 2837 /** 2838 * Returns the value of the title_output status 2839 * 2840 * @return integer 0 for full title, 1 for title without points, 2 for no title 2841 * @access public 2842 * @see $title_output 2843 */ 2844 public function getTitleOutput() 2845 { 2846 return ($this->title_output) ? $this->title_output : 0; 2847 } 2848 2849 /** 2850 * Returns the value of the title_output status 2851 * 2852 * @param integer $active_id The active id of a user 2853 * @return integer 0 for full title, 1 for title without points, 2 for no title 2854 * @access public 2855 * @see $title_output 2856 */ 2857 public function _getTitleOutput($active_id) 2858 { 2859 global $DIC; 2860 $ilDB = $DIC['ilDB']; 2861 2862 $result = $ilDB->queryF( 2863 "SELECT tst_tests.title_output FROM tst_tests, tst_active WHERE tst_tests.test_id = tst_active.test_fi AND tst_active.active_id = %s", 2864 array('integer'), 2865 array($active_id) 2866 ); 2867 if ($result->numRows()) { 2868 $row = $ilDB->fetchAssoc($result); 2869 return $row["title_output"]; 2870 } 2871 return 0; 2872 } 2873 2874 // hey: prevPassSolutions - serious (nonstatic) identifier, for use in high level controller gui 2875 public function isPreviousSolutionReuseEnabled($activeId) 2876 { 2877 // checks if allowed in general and if enabled by participant 2878 return self::_getUsePreviousAnswers($activeId, true); 2879 } 2880 // hey. 2881 2882 /** 2883 * Returns if the previous results should be hidden for a learner 2884 * 2885 * @param integer $test_id The test id 2886 * @param boolean $use_active_user_setting If true, the tst_use_previous_answers- of the active user should be used as well 2887 * @return integer 1 if the previous results should be hidden, 0 otherwise 2888 * @access public 2889 * @see $use_previous_answers 2890 */ 2891 public static function _getUsePreviousAnswers($active_id, $user_active_user_setting = false) 2892 { 2893 global $DIC; 2894 $ilDB = $DIC['ilDB']; 2895 $ilUser = $DIC['ilUser']; 2896 2897 $use_previous_answers = 1; 2898 2899 $result = $ilDB->queryF( 2900 "SELECT tst_tests.use_previous_answers FROM tst_tests, tst_active WHERE tst_tests.test_id = tst_active.test_fi AND tst_active.active_id = %s", 2901 array("integer"), 2902 array($active_id) 2903 ); 2904 if ($result->numRows()) { 2905 $row = $ilDB->fetchAssoc($result); 2906 $use_previous_answers = $row["use_previous_answers"]; 2907 } 2908 2909 if ($use_previous_answers == 1) { 2910 if ($user_active_user_setting) { 2911 $res = $ilUser->getPref("tst_use_previous_answers"); 2912 if ($res !== false) { 2913 $use_previous_answers = $res; 2914 } 2915 } 2916 } 2917 return $use_previous_answers; 2918 } 2919 2920 /** 2921 * Returns the processing time for the test 2922 * 2923 * @return string The processing time for the test 2924 * @access public 2925 * @see $processing_time 2926 */ 2927 public function getProcessingTime() 2928 { 2929 return (strlen($this->processing_time)) ? $this->processing_time : null; 2930 } 2931 2932 /** 2933 * Returns the processing time for the test 2934 * 2935 * @return string The processing time for the test 2936 * @see $processing_time 2937 */ 2938 public function getProcessingTimeAsArray() 2939 { 2940 if (strlen($this->processing_time)) { 2941 if (preg_match("/(\d{2}):(\d{2}):(\d{2})/is", $this->processing_time, $matches)) { 2942 if ((int) $matches[1] + (int) $matches[2] + (int) $matches[3] == 0) { 2943 return $this->getEstimatedWorkingTime(); 2944 } else { 2945 return array( 2946 'hh' => $matches[1], 2947 'mm' => $matches[2], 2948 'ss' => $matches[3], 2949 ); 2950 } 2951 } 2952 } 2953 return $this->getEstimatedWorkingTime(); 2954 } 2955 2956 public function getProcessingTimeAsMinutes() 2957 { 2958 if (strlen($this->processing_time)) { 2959 if (preg_match("/(\d{2}):(\d{2}):(\d{2})/is", $this->processing_time, $matches)) { 2960 return ($matches[1] * 60) + $matches[2]; 2961 } 2962 } 2963 2964 return self::DEFAULT_PROCESSING_TIME_MINUTES; 2965 } 2966 2967 /** 2968 * Returns the processing time for the test in seconds 2969 * 2970 * @return integer The processing time for the test in seconds 2971 * @access public 2972 * @see $processing_time 2973 */ 2974 public function getProcessingTimeInSeconds($active_id = "") 2975 { 2976 if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $this->getProcessingTime(), $matches)) { 2977 $extratime = $this->getExtraTime($active_id) * 60; 2978 return ($matches[1] * 3600) + ($matches[2] * 60) + $matches[3] + $extratime; 2979 } else { 2980 return 0; 2981 } 2982 } 2983 2984 /** 2985 * Returns the seconds left from the actual time until the ending time 2986 * 2987 * @return integer The seconds left until the ending time is reached 2988 * @access public 2989 * @see $ending_time 2990 */ 2991 public function getSecondsUntilEndingTime() 2992 { 2993 if ($this->getEndingTime() != 0) { 2994 $ending = $this->getEndingTime(); 2995 $now = time(); 2996 return $ending - $now; 2997 } else { 2998 return 0; 2999 } 3000 } 3001 3002 /** 3003 * Returns the state of the processing time (enabled/disabled) 3004 * 3005 * @return integer The processing time state (0 for disabled, 1 for enabled) 3006 * @access public 3007 * @see $processing_time 3008 */ 3009 public function getEnableProcessingTime() 3010 { 3011 return ($this->enable_processing_time) ? $this->enable_processing_time : 0; 3012 } 3013 3014 /** 3015 * Returns wheather the processing time should be reset or not 3016 * 3017 * @return integer 0 for no reset, 1 for a reset 3018 * @access public 3019 * @see $reset_processing_time 3020 */ 3021 public function getResetProcessingTime() 3022 { 3023 return ($this->reset_processing_time) ? $this->reset_processing_time : 0; 3024 } 3025 3026 /** 3027 * @return boolean 3028 */ 3029 public function isStartingTimeEnabled() 3030 { 3031 return $this->starting_time_enabled; 3032 } 3033 3034 /** 3035 * @param boolean $starting_time_enabled 3036 */ 3037 public function setStartingTimeEnabled($starting_time_enabled) 3038 { 3039 $this->starting_time_enabled = $starting_time_enabled; 3040 } 3041 3042 /** 3043 * Returns the starting time of the test 3044 * 3045 * @return string The starting time of the test 3046 * @access public 3047 * @see $starting_time 3048 */ 3049 public function getStartingTime() 3050 { 3051 return ($this->starting_time != 0) ? $this->starting_time : 0; 3052 } 3053 3054 /** 3055 * Sets the starting time in database timestamp format for the test 3056 * 3057 * @param string $starting_time The starting time for the test. Empty string for no starting time. 3058 * @access public 3059 * @see $starting_time 3060 */ 3061 public function setStartingTime($starting_time = null) 3062 { 3063 $this->starting_time = $starting_time; 3064 } 3065 3066 /** 3067 * @return boolean 3068 */ 3069 public function isEndingTimeEnabled() 3070 { 3071 return $this->ending_time_enabled; 3072 } 3073 3074 /** 3075 * @param boolean $ending_time_enabled 3076 */ 3077 public function setEndingTimeEnabled($ending_time_enabled) 3078 { 3079 $this->ending_time_enabled = $ending_time_enabled; 3080 } 3081 3082 /** 3083 * Returns the ending time of the test 3084 * 3085 * @return string The ending time of the test 3086 * @access public 3087 * @see $ending_time 3088 */ 3089 public function getEndingTime() 3090 { 3091 return ($this->ending_time != 0) ? $this->ending_time : 0; 3092 } 3093 3094 /** 3095 * Sets the ending time in database timestamp format for the test 3096 * 3097 * @param string $ending_time The ending time for the test. Empty string for no ending time. 3098 * @access public 3099 * @see $ending_time 3100 */ 3101 public function setEndingTime($ending_time = null) 3102 { 3103 $this->ending_time = $ending_time; 3104 } 3105 3106 /** 3107 * Sets the nr of tries for the test 3108 * 3109 * @param integer $nr_of_tries The maximum number of tries for the test. 0 for infinite tries. 3110 * @access public 3111 * @see $nr_of_tries 3112 */ 3113 public function setNrOfTries($nr_of_tries = 0) 3114 { 3115 $this->nr_of_tries = $nr_of_tries; 3116 } 3117 3118 /** 3119 * Sets the status of the visibility of previous learner answers 3120 ** 3121 * @param integer $use_previous_answers 1 if the previous answers should be shown 3122 * @access public 3123 * @see $use_previous_answers 3124 */ 3125 public function setUsePreviousAnswers($use_previous_answers = 1) 3126 { 3127 if ($use_previous_answers) { 3128 $this->use_previous_answers = 1; 3129 } else { 3130 $this->use_previous_answers = 0; 3131 } 3132 } 3133 3134 public function setRedirectionMode($redirection_mode = 0) 3135 { 3136 $this->redirection_mode = $redirection_mode; 3137 } 3138 public function getRedirectionMode() 3139 { 3140 return $this->redirection_mode; 3141 } 3142 public function setRedirectionUrl($redirection_url = null) 3143 { 3144 $this->redirection_url = $redirection_url; 3145 } 3146 public function getRedirectionUrl() 3147 { 3148 return $this->redirection_url; 3149 } 3150 3151 /** 3152* Sets the status of the title output 3153** 3154* @param integer $title_output 0 for full title, 1 for title without points, 2 for no title 3155* @access public 3156* @see $title_output 3157*/ 3158 public function setTitleOutput($title_output = 0) 3159 { 3160 switch ($title_output) { 3161 case 1: 3162 $this->title_output = 1; 3163 break; 3164 case 2: 3165 $this->title_output = 2; 3166 break; 3167 default: 3168 $this->title_output = 0; 3169 break; 3170 } 3171 } 3172 3173 /** 3174 * Sets the processing time for the test 3175 * 3176 * @param string $processing_time The maximum processing time for the test given in hh:mm:ss 3177 * @access public 3178 * @see $processing_time 3179 */ 3180 public function setProcessingTime($processing_time = "00:00:00") 3181 { 3182 $this->processing_time = $processing_time; 3183 } 3184 3185 public function setProcessingTimeByMinutes($minutes) 3186 { 3187 $this->processing_time = sprintf("%02d:%02d:00", floor($minutes / 60), $minutes % 60); 3188 } 3189 3190 /** 3191* Sets the processing time enabled or disabled 3192* 3193* @param integer $enable 0 to disable the processing time, 1 to enable the processing time 3194* @access public 3195* @see $processing_time 3196*/ 3197 public function setEnableProcessingTime($enable = 0) 3198 { 3199 if ($enable) { 3200 $this->enable_processing_time = "1"; 3201 } else { 3202 $this->enable_processing_time = "0"; 3203 } 3204 } 3205 3206 /** 3207 * Sets wheather the processing time should be reset or not 3208 * 3209 * @param integer $reset 1 to reset the processing time, 0 otherwise 3210 * @access public 3211 * @see $processing_time 3212 */ 3213 public function setResetProcessingTime($reset = 0) 3214 { 3215 if ($reset) { 3216 $this->reset_processing_time = 1; 3217 } else { 3218 $this->reset_processing_time = 0; 3219 } 3220 } 3221 3222 /** 3223 * Sets the count system for the calculation of points 3224 * 3225 * @param integer $a_count_system The count system for the calculation of points. 3226 * @access public 3227 * @see $count_system 3228 */ 3229 public function setCountSystem($a_count_system = COUNT_PARTIAL_SOLUTIONS) 3230 { 3231 $this->count_system = $a_count_system; 3232 } 3233 3234 /** 3235 * @return boolean 3236 */ 3237 public function isPasswordEnabled() 3238 { 3239 return $this->passwordEnabled; 3240 } 3241 3242 /** 3243 * @param boolean $passwordEnabled 3244 */ 3245 public function setPasswordEnabled($passwordEnabled) 3246 { 3247 $this->passwordEnabled = $passwordEnabled; 3248 } 3249 3250 /** 3251 * Returns the password for test access 3252 * 3253 * @return striong Password for test access 3254 * @access public 3255 * @see $password 3256 */ 3257 public function getPassword() 3258 { 3259 return (strlen($this->password)) ? $this->password : null; 3260 } 3261 3262 /** 3263 * Sets the password for test access 3264 * 3265 * @param string $a_password The password for test access 3266 * @access public 3267 * @see $password 3268 */ 3269 public function setPassword($a_password = null) 3270 { 3271 $this->password = $a_password; 3272 } 3273 3274 /** 3275 * Sets the type of score cutting 3276 * 3277 * @param integer $a_score_cutting The type of score cutting. 0 for cut questions, 1 for cut tests 3278 * @access public 3279 * @see $score_cutting 3280 */ 3281 public function setScoreCutting($a_score_cutting = SCORE_CUT_QUESTION) 3282 { 3283 $this->score_cutting = $a_score_cutting; 3284 } 3285 3286 /** 3287 * Sets the multiple choice scoring 3288 * 3289 * @param integer $a_mc_scoring The scoring for multiple choice questions 3290 * @access public 3291 * @see $mc_scoring 3292 */ 3293 public function setMCScoring($a_mc_scoring = SCORE_ZERO_POINTS_WHEN_UNANSWERED) 3294 { 3295 $this->mc_scoring = $a_mc_scoring; 3296 } 3297 3298 /** 3299 * Sets the pass scoring 3300 * 3301 * @param integer $a_pass_scoring The pass scoring type 3302 * @access public 3303 * @see $pass_scoring 3304 */ 3305 public function setPassScoring($a_pass_scoring = SCORE_LAST_PASS) 3306 { 3307 switch ($a_pass_scoring) { 3308 case SCORE_BEST_PASS: 3309 $this->pass_scoring = SCORE_BEST_PASS; 3310 break; 3311 default: 3312 $this->pass_scoring = SCORE_LAST_PASS; 3313 break; 3314 } 3315 } 3316 3317 /** 3318 * @return string 3319 */ 3320 public function getPassWaiting() 3321 { 3322 return $this->pass_waiting; 3323 } 3324 3325 /** 3326 * @param string $pass_waiting mm:ddd:hh:ii:ss 3327 */ 3328 public function setPassWaiting($pass_waiting) 3329 { 3330 $this->pass_waiting = $pass_waiting; 3331 } 3332 /** 3333 * @return bool 3334 */ 3335 public function isPassWaitingEnabled() 3336 { 3337 if (array_sum(explode(':', $this->getPassWaiting())) > 0) { 3338 return true; 3339 } 3340 return false; 3341 } 3342 3343 /** 3344 * @param int $questionId 3345 * @param array $activeIds 3346 * @param ilTestReindexedSequencePositionMap $reindexedSequencePositionMap 3347 */ 3348 public function removeQuestionFromSequences($questionId, $activeIds, ilTestReindexedSequencePositionMap $reindexedSequencePositionMap) 3349 { 3350 global $DIC; /* @var ILIAS\DI\Container $DIC */ 3351 3352 $testSequenceFactory = new ilTestSequenceFactory( 3353 $DIC->database(), 3354 $DIC->language(), 3355 $DIC['ilPluginAdmin'], 3356 $this 3357 ); 3358 3359 foreach ($activeIds as $activeId) { 3360 $passSelector = new ilTestPassesSelector($DIC->database(), $this); 3361 $passSelector->setActiveId($activeId); 3362 3363 foreach ($passSelector->getExistingPasses() as $pass) { 3364 $testSequence = $testSequenceFactory->getSequenceByActiveIdAndPass($activeId, $pass); 3365 $testSequence->loadFromDb(); 3366 3367 $testSequence->removeQuestion($questionId, $reindexedSequencePositionMap); 3368 $testSequence->saveToDb(); 3369 } 3370 } 3371 } 3372 3373 /** 3374 * @param array $removeQuestionIds 3375 */ 3376 public function removeQuestions($removeQuestionIds) 3377 { 3378 foreach ($removeQuestionIds as $value) { 3379 $this->removeQuestion($value); 3380 } 3381 3382 $this->reindexFixedQuestionOrdering(); 3383 } 3384 3385 /** 3386 * Removes a question from the test object 3387 * 3388 * @param integer $question_id The database id of the question to be removed 3389 * @access public 3390 * @see $test_id 3391 */ 3392 public function removeQuestion($question_id) 3393 { 3394 $question = &ilObjTest::_instanciateQuestion($question_id); 3395 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php"); 3396 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 3397 $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id); 3398 } 3399 $question->delete($question_id); 3400 } 3401 3402 /** 3403 * - at the time beeing ilObjTest::removeTestResults needs to call the LP service for deletion 3404 * - ilTestLP calls ilObjTest::removeTestResultsByUserIds 3405 * 3406 * this method should only be used from non refactored soap context i think 3407 * 3408 * @param $userIds 3409 */ 3410 public function removeTestResultsFromSoapLpAdministration($userIds) 3411 { 3412 $this->removeTestResultsByUserIds($userIds); 3413 3414 global $DIC; 3415 $ilDB = $DIC['ilDB']; 3416 $lng = $DIC['lng']; 3417 3418 require_once 'Modules/Test/classes/class.ilTestParticipantData.php'; 3419 $participantData = new ilTestParticipantData($ilDB, $lng); 3420 $participantData->setUserIdsFilter($userIds); 3421 $participantData->load($this->getTestId()); 3422 3423 $this->removeTestActives($participantData->getActiveIds()); 3424 } 3425 3426 public function removeTestResults(ilTestParticipantData $participantData) 3427 { 3428 if (count($participantData->getAnonymousActiveIds())) { 3429 $this->removeTestResultsByActiveIds($participantData->getAnonymousActiveIds()); 3430 } 3431 3432 if (count($participantData->getUserIds())) { 3433 /* @var ilTestLP $testLP */ 3434 require_once 'Services/Object/classes/class.ilObjectLP.php'; 3435 $testLP = ilObjectLP::getInstance($this->getId()); 3436 $testLP->setTestObject($this); 3437 $testLP->resetLPDataForUserIds($participantData->getUserIds(), false); 3438 } 3439 3440 if (count($participantData->getActiveIds())) { 3441 $this->removeTestActives($participantData->getActiveIds()); 3442 } 3443 } 3444 3445 public function removeTestResultsByUserIds($userIds) 3446 { 3447 global $DIC; 3448 $ilDB = $DIC['ilDB']; 3449 $lng = $DIC['lng']; 3450 3451 require_once 'Modules/Test/classes/class.ilTestParticipantData.php'; 3452 $participantData = new ilTestParticipantData($ilDB, $lng); 3453 $participantData->setUserIdsFilter($userIds); 3454 $participantData->load($this->getTestId()); 3455 3456 $IN_userIds = $ilDB->in('usr_id', $participantData->getUserIds(), false, 'integer'); 3457 $ilDB->manipulateF( 3458 "DELETE FROM usr_pref WHERE $IN_userIds AND keyword = %s", 3459 array('text'), 3460 array("tst_password_" . $this->getTestId()) 3461 ); 3462 3463 if (count($participantData->getActiveIds())) { 3464 $this->removeTestResultsByActiveIds($participantData->getActiveIds()); 3465 } 3466 } 3467 3468 public function removeTestResultsByActiveIds($activeIds) 3469 { 3470 global $DIC; 3471 $ilDB = $DIC['ilDB']; 3472 3473 $IN_activeIds = $ilDB->in('active_fi', $activeIds, false, 'integer'); 3474 3475 $ilDB->manipulate("DELETE FROM tst_solutions WHERE $IN_activeIds"); 3476 $ilDB->manipulate("DELETE FROM tst_qst_solved WHERE $IN_activeIds"); 3477 $ilDB->manipulate("DELETE FROM tst_test_result WHERE $IN_activeIds"); 3478 $ilDB->manipulate("DELETE FROM tst_pass_result WHERE $IN_activeIds"); 3479 $ilDB->manipulate("DELETE FROM tst_result_cache WHERE $IN_activeIds"); 3480 $ilDB->manipulate("DELETE FROM tst_sequence WHERE $IN_activeIds"); 3481 $ilDB->manipulate("DELETE FROM tst_times WHERE $IN_activeIds"); 3482 3483 if ($this->isRandomTest()) { 3484 $ilDB->manipulate("DELETE FROM tst_test_rnd_qst WHERE $IN_activeIds"); 3485 } elseif ($this->isDynamicTest()) { 3486 $ilDB->manipulate("DELETE FROM tst_seq_qst_tracking WHERE $IN_activeIds"); 3487 $ilDB->manipulate("DELETE FROM tst_seq_qst_answstatus WHERE $IN_activeIds"); 3488 $ilDB->manipulate("DELETE FROM tst_seq_qst_postponed WHERE $IN_activeIds"); 3489 $ilDB->manipulate("DELETE FROM tst_seq_qst_checked WHERE $IN_activeIds"); 3490 } 3491 3492 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php"); 3493 3494 foreach ($activeIds as $active_id) { 3495 // remove file uploads 3496 if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id")) { 3497 ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"); 3498 } 3499 3500 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 3501 $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id)))); 3502 } 3503 } 3504 3505 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php'; 3506 ilAssQuestionHintTracking::deleteRequestsByActiveIds($activeIds); 3507 } 3508 3509 public function removeTestActives($activeIds) 3510 { 3511 global $DIC; 3512 $ilDB = $DIC['ilDB']; 3513 3514 $IN_activeIds = $ilDB->in('active_id', $activeIds, false, 'integer'); 3515 $ilDB->manipulate("DELETE FROM tst_active WHERE $IN_activeIds"); 3516 } 3517 3518 /** 3519 * Moves a question up in order 3520 * 3521 * @param integer $question_id The database id of the question to be moved up 3522 * @access public 3523 * @see $test_id 3524 */ 3525 public function questionMoveUp($question_id) 3526 { 3527 global $DIC; 3528 $ilDB = $DIC['ilDB']; 3529 3530 // Move a question up in sequence 3531 $result = $ilDB->queryF( 3532 "SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s", 3533 array('integer', 'integer'), 3534 array($this->getTestId(), $question_id) 3535 ); 3536 $data = $ilDB->fetchObject($result); 3537 if ($data->sequence > 1) { 3538 // OK, it's not the top question, so move it up 3539 $result = $ilDB->queryF( 3540 "SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s", 3541 array('integer','integer'), 3542 array($this->getTestId(), $data->sequence - 1) 3543 ); 3544 $data_previous = $ilDB->fetchObject($result); 3545 // change previous dataset 3546 $affectedRows = $ilDB->manipulateF( 3547 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s", 3548 array('integer','integer'), 3549 array($data->sequence, $data_previous->test_question_id) 3550 ); 3551 // move actual dataset up 3552 $affectedRows = $ilDB->manipulateF( 3553 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s", 3554 array('integer','integer'), 3555 array($data->sequence - 1, $data->test_question_id) 3556 ); 3557 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php"); 3558 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 3559 $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence - 1), $question_id); 3560 } 3561 } 3562 $this->loadQuestions(); 3563 } 3564 3565 /** 3566 * Moves a question down in order 3567 * 3568 * @param integer $question_id The database id of the question to be moved down 3569 * @access public 3570 * @see $test_id 3571 */ 3572 public function questionMoveDown($question_id) 3573 { 3574 global $DIC; 3575 $ilDB = $DIC['ilDB']; 3576 3577 // Move a question down in sequence 3578 $result = $ilDB->queryF( 3579 "SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s", 3580 array('integer','integer'), 3581 array($this->getTestId(), $question_id) 3582 ); 3583 $data = $ilDB->fetchObject($result); 3584 $result = $ilDB->queryF( 3585 "SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s", 3586 array('integer','integer'), 3587 array($this->getTestId(), $data->sequence + 1) 3588 ); 3589 if ($result->numRows() == 1) { 3590 // OK, it's not the last question, so move it down 3591 $data_next = $ilDB->fetchObject($result); 3592 // change next dataset 3593 $affectedRows = $ilDB->manipulateF( 3594 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s", 3595 array('integer','integer'), 3596 array($data->sequence, $data_next->test_question_id) 3597 ); 3598 // move actual dataset down 3599 $affectedRows = $ilDB->manipulateF( 3600 "UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s", 3601 array('integer','integer'), 3602 array($data->sequence + 1, $data->test_question_id) 3603 ); 3604 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php"); 3605 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 3606 $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence + 1), $question_id); 3607 } 3608 } 3609 $this->loadQuestions(); 3610 } 3611 3612 /** 3613 * Takes a question and creates a copy of the question for use in the test 3614 * 3615 * @param integer $question_id The database id of the question 3616 * @result integer The database id of the copied question 3617 * @access public 3618 */ 3619 public function duplicateQuestionForTest($question_id) 3620 { 3621 global $DIC; 3622 $ilUser = $DIC['ilUser']; 3623 $question = &ilObjTest::_instanciateQuestion($question_id); 3624 $duplicate_id = $question->duplicate(true, null, null, null, $this->getId()); 3625 3626 return $duplicate_id; 3627 } 3628 3629 /** 3630 * Insert a question in the list of questions 3631 * 3632 * @param ilTestQuestionSetConfig $testQuestionSetConfig 3633 * @param integer $question_id The database id of the inserted question 3634 * @param boolean $linkOnly 3635 * @return integer $duplicate_id 3636 */ 3637 public function insertQuestion(ilTestQuestionSetConfig $testQuestionSetConfig, $question_id, $linkOnly = false) 3638 { 3639 global $DIC; 3640 $ilDB = $DIC['ilDB']; 3641 #var_dump($question_id); 3642 if ($linkOnly) { 3643 $duplicate_id = $question_id; 3644 } else { 3645 $duplicate_id = $this->duplicateQuestionForTest($question_id); 3646 } 3647 3648 // get maximum sequence index in test 3649 $result = $ilDB->queryF( 3650 "SELECT MAX(sequence) seq FROM tst_test_question WHERE test_fi=%s", 3651 array('integer'), 3652 array($this->getTestId()) 3653 ); 3654 $sequence = 1; 3655 3656 if ($result->numRows() == 1) { 3657 $data = $ilDB->fetchObject($result); 3658 $sequence = $data->seq + 1; 3659 } 3660 3661 $next_id = $ilDB->nextId('tst_test_question'); 3662 $affectedRows = $ilDB->manipulateF( 3663 "INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)", 3664 array('integer', 'integer','integer','integer','integer'), 3665 array($next_id, $this->getTestId(), $duplicate_id, $sequence, time()) 3666 ); 3667 if ($affectedRows == 1) { 3668 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php"); 3669 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 3670 $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . $sequence, $duplicate_id); 3671 } 3672 } 3673 // remove test_active entries, because test has changed 3674 $affectedRows = $ilDB->manipulateF( 3675 "DELETE FROM tst_active WHERE test_fi = %s", 3676 array('integer'), 3677 array($this->getTestId()) 3678 ); 3679 $this->loadQuestions(); 3680 $this->saveCompleteStatus($testQuestionSetConfig); 3681 return $duplicate_id; 3682 } 3683 3684 /** 3685 * Returns the titles of the test questions in question sequence 3686 * 3687 * @return array The question titles 3688 * @access public 3689 * @see $questions 3690 */ 3691 public function &getQuestionTitles() 3692 { 3693 $titles = array(); 3694 if ($this->getQuestionSetType() == self::QUESTION_SET_TYPE_FIXED) { 3695 global $DIC; 3696 $ilDB = $DIC['ilDB']; 3697 $result = $ilDB->queryF( 3698 "SELECT qpl_questions.title FROM tst_test_question, qpl_questions WHERE tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id ORDER BY tst_test_question.sequence", 3699 array('integer'), 3700 array($this->getTestId()) 3701 ); 3702 while ($row = $ilDB->fetchAssoc($result)) { 3703 array_push($titles, $row["title"]); 3704 } 3705 } 3706 return $titles; 3707 } 3708 3709 /** 3710 * Returns the titles of the test questions in question sequence 3711 * 3712 * @return array The question titles 3713 * @access public 3714 * @see $questions 3715 */ 3716 public function &getQuestionTitlesAndIndexes() 3717 { 3718 $titles = array(); 3719 if ($this->getQuestionSetType() == self::QUESTION_SET_TYPE_FIXED) { 3720 global $DIC; 3721 $ilDB = $DIC['ilDB']; 3722 $result = $ilDB->queryF( 3723 "SELECT qpl_questions.title, qpl_questions.question_id FROM tst_test_question, qpl_questions WHERE tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id ORDER BY tst_test_question.sequence", 3724 array('integer'), 3725 array($this->getTestId()) 3726 ); 3727 while ($row = $ilDB->fetchAssoc($result)) { 3728 $titles[$row['question_id']] = $row["title"]; 3729 } 3730 } 3731 return $titles; 3732 } 3733 3734 // fau: testNav - add number parameter (to show if title should not be shown) 3735 /** 3736 * Returns the title of a test question and checks if the title output is allowed. 3737 * If not, the localized text "question" will be returned. 3738 * 3739 * @param string $title The original title of the question 3740 * @param integer $nr The number of the question in the sequence 3741 * @return string The title for the question title output 3742 * @access public 3743 */ 3744 public function getQuestionTitle($title, $nr = null) 3745 { 3746 if ($this->getTitleOutput() == 2) { 3747 if ($this->getQuestionSetType() == self::QUESTION_SET_TYPE_DYNAMIC) { 3748 // avoid legacy setting combination: ctm without question titles 3749 return $title; 3750 } elseif (isset($nr)) { 3751 return $this->lng->txt("ass_question") . ' ' . $nr; 3752 } else { 3753 return $this->lng->txt("ass_question"); 3754 } 3755 } else { 3756 return $title; 3757 } 3758 } 3759 // fau. 3760 3761 /** 3762 * Returns the dataset for a given question id 3763 * 3764 * @param integer $question_id The database id of the question 3765 * @return object Question dataset 3766 * @access public 3767 * @see $questions 3768 */ 3769 public function getQuestionDataset($question_id) 3770 { 3771 global $DIC; 3772 $ilDB = $DIC['ilDB']; 3773 3774 $result = $ilDB->queryF( 3775 "SELECT qpl_questions.*, qpl_qst_type.type_tag FROM qpl_questions, qpl_qst_type WHERE qpl_questions.question_id = %s AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id", 3776 array('integer'), 3777 array($question_id) 3778 ); 3779 $row = $ilDB->fetchObject($result); 3780 return $row; 3781 } 3782 3783 /** 3784 * Get the id's of the questions which are already part of the test 3785 * 3786 * @return array An array containing the already existing questions 3787 * @access public 3788 */ 3789 public function &getExistingQuestions($pass = null) 3790 { 3791 global $DIC; 3792 $ilUser = $DIC['ilUser']; 3793 $ilDB = $DIC['ilDB']; 3794 3795 $existing_questions = array(); 3796 $active_id = $this->getActiveIdOfUser($ilUser->getId()); 3797 if ($this->isRandomTest()) { 3798 if (is_null($pass)) { 3799 $pass = 0; 3800 } 3801 $result = $ilDB->queryF( 3802 "SELECT qpl_questions.original_id FROM qpl_questions, tst_test_rnd_qst WHERE tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.question_fi = qpl_questions.question_id AND tst_test_rnd_qst.pass = %s", 3803 array('integer','integer'), 3804 array($active_id, $pass) 3805 ); 3806 } else { 3807 $result = $ilDB->queryF( 3808 "SELECT qpl_questions.original_id FROM qpl_questions, tst_test_question WHERE tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id", 3809 array('integer'), 3810 array($this->getTestId()) 3811 ); 3812 } 3813 while ($data = $ilDB->fetchObject($result)) { 3814 if ($data->original_id === null) { 3815 continue; 3816 } 3817 3818 array_push($existing_questions, $data->original_id); 3819 } 3820 return $existing_questions; 3821 } 3822 3823 /** 3824 * Returns the question type of a question with a given id 3825 * 3826 * @param integer $question_id The database id of the question 3827 * @result string The question type string 3828 * @access private 3829 */ 3830 public function getQuestionType($question_id) 3831 { 3832 global $DIC; 3833 $ilDB = $DIC['ilDB']; 3834 3835 if ($question_id < 1) { 3836 return -1; 3837 } 3838 $result = $ilDB->queryF( 3839 "SELECT type_tag FROM qpl_questions, qpl_qst_type WHERE qpl_questions.question_id = %s AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id", 3840 array('integer'), 3841 array($question_id) 3842 ); 3843 if ($result->numRows() == 1) { 3844 $data = $ilDB->fetchObject($result); 3845 return $data->type_tag; 3846 } else { 3847 return ""; 3848 } 3849 } 3850 3851 /** 3852 * Write the initial entry for the tests working time to the database 3853 * 3854 * @param integer $user_id The database id of the user working with the test 3855 * @access public 3856 */ 3857 public function startWorkingTime($active_id, $pass) 3858 { 3859 global $DIC; 3860 $ilDB = $DIC['ilDB']; 3861 3862 $next_id = $ilDB->nextId('tst_times'); 3863 $affectedRows = $ilDB->manipulateF( 3864 "INSERT INTO tst_times (times_id, active_fi, started, finished, pass, tstamp) VALUES (%s, %s, %s, %s, %s, %s)", 3865 array('integer', 'integer', 'timestamp', 'timestamp', 'integer', 'integer'), 3866 array($next_id, $active_id, strftime("%Y-%m-%d %H:%M:%S"), strftime("%Y-%m-%d %H:%M:%S"), $pass, time()) 3867 ); 3868 return $next_id; 3869 } 3870 3871 /** 3872 * Update the working time of a test when a question is answered 3873 * 3874 * @param integer $times_id The database id of a working time entry 3875 * @access public 3876 */ 3877 public function updateWorkingTime($times_id) 3878 { 3879 global $DIC; 3880 $ilDB = $DIC['ilDB']; 3881 3882 $affectedRows = $ilDB->manipulateF( 3883 "UPDATE tst_times SET finished = %s, tstamp = %s WHERE times_id = %s", 3884 array('timestamp', 'integer', 'integer'), 3885 array(strftime("%Y-%m-%d %H:%M:%S"), time(), $times_id) 3886 ); 3887 } 3888 3889 /** 3890 * Gets the id's of all questions a user already worked through 3891 * 3892 * @return array The question id's of the questions already worked through 3893 * @access public 3894 */ 3895 public function &getWorkedQuestions($active_id, $pass = null) 3896 { 3897 global $DIC; 3898 $ilUser = $DIC['ilUser']; 3899 $ilDB = $DIC['ilDB']; 3900 3901 if (is_null($pass)) { 3902 $result = $ilDB->queryF( 3903 "SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi", 3904 array('integer','integer'), 3905 array($active_id, 0) 3906 ); 3907 } else { 3908 $result = $ilDB->queryF( 3909 "SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi", 3910 array('integer','integer'), 3911 array($active_id, $pass) 3912 ); 3913 } 3914 $result_array = array(); 3915 while ($row = $ilDB->fetchAssoc($result)) { 3916 array_push($result_array, $row["question_fi"]); 3917 } 3918 return $result_array; 3919 } 3920 3921 /** 3922 * Returns true if an active user completed a test pass and did not start a new pass 3923 * 3924 * @param integer $active_id The active id of the user 3925 * @param integer $currentpass The current test pass of the user 3926 * @return boolean true if an active user completed a test pass and did not start a new pass, false otherwise 3927 * @access public 3928 */ 3929 public function isTestFinishedToViewResults($active_id, $currentpass) 3930 { 3931 $num = ilObjTest::lookupPassResultsUpdateTimestamp($active_id, $currentpass); 3932 return ((($currentpass > 0) && ($num == 0)) || $this->isTestFinished($active_id)) ? true : false; 3933 } 3934 3935 /** 3936 * Returns all questions of a test in test order 3937 * 3938 * @return array An array containing the id's as keys and the database row objects as values 3939 * @access public 3940 */ 3941 public function &getAllQuestions($pass = null) 3942 { 3943 global $DIC; 3944 $ilUser = $DIC['ilUser']; 3945 $ilDB = $DIC['ilDB']; 3946 3947 $result_array = array(); 3948 if ($this->isRandomTest()) { 3949 $active_id = $this->getActiveIdOfUser($ilUser->getId()); 3950 $this->loadQuestions($active_id, $pass); 3951 if (count($this->questions) == 0) { 3952 return $result_array; 3953 } 3954 if (is_null($pass)) { 3955 $pass = self::_getPass($active_id); 3956 } 3957 $result = $ilDB->queryF( 3958 "SELECT qpl_questions.* FROM qpl_questions, tst_test_rnd_qst WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id AND tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.pass = %s AND " . $ilDB->in('qpl_questions.question_id', $this->questions, false, 'integer'), 3959 array('integer','integer'), 3960 array($active_id, $pass) 3961 ); 3962 } else { 3963 if (count($this->questions) == 0) { 3964 return $result_array; 3965 } 3966 $result = $ilDB->query("SELECT qpl_questions.* FROM qpl_questions, tst_test_question WHERE tst_test_question.question_fi = qpl_questions.question_id AND " . $ilDB->in('qpl_questions.question_id', $this->questions, false, 'integer')); 3967 } 3968 while ($row = $ilDB->fetchAssoc($result)) { 3969 $result_array[$row["question_id"]] = $row; 3970 } 3971 return $result_array; 3972 } 3973 3974 /** 3975 * Gets the active id of a given user 3976 * 3977 * @param integer $user_id The database id of the user 3978 * @param string $anonymous_id The anonymous id if the test is an anonymized test 3979 * @return integer The active ID 3980 * @access public 3981 */ 3982 public function getActiveIdOfUser($user_id = "", $anonymous_id = "") 3983 { 3984 global $DIC; 3985 $ilDB = $DIC['ilDB']; 3986 $ilUser = $DIC['ilUser']; 3987 3988 if (!$user_id) { 3989 $user_id = $ilUser->getId(); 3990 } 3991 if (($GLOBALS['DIC']['ilUser']->getId() == ANONYMOUS_USER_ID) && (strlen($_SESSION["tst_access_code"][$this->getTestId()]))) { 3992 $result = $ilDB->queryF( 3993 "SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s", 3994 array('integer','integer','text'), 3995 array($user_id, $this->test_id, $_SESSION["tst_access_code"][$this->getTestId()]) 3996 ); 3997 } elseif (strlen($anonymous_id)) { 3998 $result = $ilDB->queryF( 3999 "SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s", 4000 array('integer','integer','text'), 4001 array($user_id, $this->test_id, $anonymous_id) 4002 ); 4003 } else { 4004 if ($GLOBALS['DIC']['ilUser']->getId() == ANONYMOUS_USER_ID) { 4005 return null; 4006 } 4007 $result = $ilDB->queryF( 4008 "SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s", 4009 array('integer','integer'), 4010 array($user_id, $this->test_id) 4011 ); 4012 } 4013 if ($result->numRows()) { 4014 $row = $ilDB->fetchAssoc($result); 4015 return $row["active_id"]; 4016 } else { 4017 return 0; 4018 } 4019 } 4020 4021 /** 4022 * Gets the active id of the tst_active table for the active user 4023 * 4024 * @param integer $user_id The database id of the user 4025 * @param integer $test_id The database id of the test 4026 * @return object The database row of the tst_active table 4027 * @access public 4028 */ 4029 public static function _getActiveIdOfUser($user_id = "", $test_id = "") 4030 { 4031 global $DIC; 4032 $ilDB = $DIC['ilDB']; 4033 $ilUser = $DIC['ilUser']; 4034 4035 if (!$user_id) { 4036 $user_id = $ilUser->id; 4037 } 4038 if (!$test_id) { 4039 return ""; 4040 } 4041 $result = $ilDB->queryF( 4042 "SELECT tst_active.active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s", 4043 array('integer', 'integer'), 4044 array($user_id, $test_id) 4045 ); 4046 if ($result->numRows()) { 4047 $row = $ilDB->fetchAssoc($result); 4048 return $row["active_id"]; 4049 } else { 4050 return ""; 4051 } 4052 } 4053 4054 /** 4055 * Shuffles the values of a given array 4056 * 4057 * @param array $array An array which should be shuffled 4058 * @access public 4059 */ 4060 public function pcArrayShuffle($array) 4061 { 4062 $keys = array_keys($array); 4063 shuffle($keys); 4064 $result = array(); 4065 foreach ($keys as $key) { 4066 $result[$key] = $array[$key]; 4067 } 4068 return $result; 4069 } 4070 4071 /** 4072 * Calculates the results of a test for a given user 4073 * and returns an array with all test results 4074 * 4075 * @return array An array containing the test results for the given user 4076 * @access public 4077 */ 4078 public function &getTestResult($active_id, $pass = null, $ordered_sequence = false, $considerHiddenQuestions = true, $considerOptionalQuestions = true) 4079 { 4080 global $DIC; 4081 $tree = $DIC['tree']; 4082 $ilDB = $DIC['ilDB']; 4083 $lng = $DIC['lng']; 4084 $ilPluginAdmin = $DIC['ilPluginAdmin']; 4085 4086 $results = $this->getResultsForActiveId($active_id); 4087 4088 if (is_null($pass)) { 4089 $pass = $results['pass']; 4090 } 4091 4092 require_once 'Modules/Test/classes/class.ilTestSessionFactory.php'; 4093 $testSessionFactory = new ilTestSessionFactory($this); 4094 $testSession = $testSessionFactory->getSession($active_id); 4095 4096 require_once 'Modules/Test/classes/class.ilTestSequenceFactory.php'; 4097 $testSequenceFactory = new ilTestSequenceFactory($ilDB, $lng, $ilPluginAdmin, $this); 4098 $testSequence = $testSequenceFactory->getSequenceByActiveIdAndPass($active_id, $pass); 4099 4100 if ($this->isDynamicTest()) { 4101 require_once 'Modules/Test/classes/class.ilObjTestDynamicQuestionSetConfig.php'; 4102 $dynamicQuestionSetConfig = new ilObjTestDynamicQuestionSetConfig($tree, $ilDB, $ilPluginAdmin, $this); 4103 $dynamicQuestionSetConfig->loadFromDb(); 4104 4105 $testSequence->loadFromDb($dynamicQuestionSetConfig); 4106 $testSequence->loadQuestions($dynamicQuestionSetConfig, new ilTestDynamicQuestionSetFilterSelection()); 4107 4108 $sequence = $testSequence->getUserSequenceQuestions(); 4109 } else { 4110 $testSequence->setConsiderHiddenQuestionsEnabled($considerHiddenQuestions); 4111 $testSequence->setConsiderOptionalQuestionsEnabled($considerOptionalQuestions); 4112 4113 $testSequence->loadFromDb(); 4114 $testSequence->loadQuestions(); 4115 4116 if ($ordered_sequence) { 4117 $sequence = $testSequence->getOrderedSequenceQuestions(); 4118 } else { 4119 $sequence = $testSequence->getUserSequenceQuestions(); 4120 } 4121 } 4122 4123 $arrResults = array(); 4124 4125 $query = " 4126 SELECT tst_test_result.question_fi, 4127 tst_test_result.points reached, 4128 tst_test_result.hint_count requested_hints, 4129 tst_test_result.hint_points hint_points, 4130 tst_test_result.answered answered 4131 4132 FROM tst_test_result 4133 4134 LEFT JOIN tst_solutions 4135 ON tst_solutions.active_fi = tst_test_result.active_fi 4136 AND tst_solutions.question_fi = tst_test_result.question_fi 4137 4138 WHERE tst_test_result.active_fi = %s 4139 AND tst_test_result.pass = %s 4140 "; 4141 4142 $solutionresult = $ilDB->queryF( 4143 $query, 4144 array('integer', 'integer'), 4145 array($active_id, $pass) 4146 ); 4147 4148 while ($row = $ilDB->fetchAssoc($solutionresult)) { 4149 $arrResults[ $row['question_fi'] ] = $row; 4150 } 4151 4152 $numWorkedThrough = count($arrResults); 4153 4154 require_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 4155 4156 $IN_question_ids = $ilDB->in('qpl_questions.question_id', $sequence, false, 'integer'); 4157 4158 $query = " 4159 SELECT qpl_questions.*, 4160 qpl_qst_type.type_tag, 4161 qpl_sol_sug.question_fi has_sug_sol 4162 4163 FROM qpl_qst_type, 4164 qpl_questions 4165 4166 LEFT JOIN qpl_sol_sug 4167 ON qpl_sol_sug.question_fi = qpl_questions.question_id 4168 4169 WHERE qpl_qst_type.question_type_id = qpl_questions.question_type_fi 4170 AND $IN_question_ids 4171 "; 4172 4173 $result = $ilDB->query($query); 4174 4175 $unordered = array(); 4176 4177 $key = 1; 4178 4179 $obligationsAnswered = true; 4180 4181 while ($row = $ilDB->fetchAssoc($result)) { 4182 $percentvalue = ( 4183 $row['points'] ? $arrResults[ $row['question_id'] ]['reached'] / $row['points'] : 0 4184 ); 4185 4186 if ($percentvalue < 0) { 4187 $percentvalue = 0.0; 4188 } 4189 4190 $data = array( 4191 "nr" => "$key", 4192 "title" => ilUtil::prepareFormOutput($row['title']), 4193 "max" => round($row['points'], 2), 4194 "reached" => round($arrResults[$row['question_id']]['reached'], 2), 4195 'requested_hints' => $arrResults[$row['question_id']]['requested_hints'], 4196 'hint_points' => $arrResults[$row['question_id']]['hint_points'], 4197 "percent" => sprintf("%2.2f ", ($percentvalue) * 100) . "%", 4198 "solution" => ($row['has_sug_sol']) ? assQuestion::_getSuggestedSolutionOutput($row['question_id']) : '', 4199 "type" => $row["type_tag"], 4200 "qid" => $row['question_id'], 4201 "original_id" => $row["original_id"], 4202 "workedthrough" => isset($arrResults[$row['question_id']]) ? 1 : 0, 4203 'answered' => $arrResults[$row['question_id']]['answered'] 4204 ); 4205 4206 if (!$arrResults[ $row['question_id'] ]['answered']) { 4207 $obligationsAnswered = false; 4208 } 4209 4210 $unordered[ $row['question_id'] ] = $data; 4211 4212 $key++; 4213 } 4214 4215 $numQuestionsTotal = count($unordered); 4216 4217 $pass_max = 0; 4218 $pass_reached = 0; 4219 $pass_requested_hints = 0; 4220 $pass_hint_points = 0; 4221 $key = 1; 4222 4223 $found = array(); 4224 4225 foreach ($sequence as $qid) { 4226 // building pass point sums based on prepared data 4227 // for question that exists in users qst sequence 4228 $pass_max += round($unordered[$qid]['max'], 2); 4229 $pass_reached += round($unordered[$qid]['reached'], 2); 4230 $pass_requested_hints += $unordered[$qid]['requested_hints']; 4231 $pass_hint_points += $unordered[$qid]['hint_points']; 4232 4233 // pickup prepared data for question 4234 // that exists in users qst sequence 4235 $unordered[$qid]['nr'] = $key; 4236 array_push($found, $unordered[$qid]); 4237 4238 // increment key counter 4239 $key++; 4240 } 4241 4242 $unordered = null; 4243 4244 if ($this->getScoreCutting() == 1) { 4245 if ($results['reached_points'] < 0) { 4246 $results['reached_points'] = 0; 4247 } 4248 4249 if ($pass_reached < 0) { 4250 $pass_reached = 0; 4251 } 4252 } 4253 4254 $found['pass']['total_max_points'] = $pass_max; 4255 $found['pass']['total_reached_points'] = $pass_reached; 4256 $found['pass']['total_requested_hints'] = $pass_requested_hints; 4257 $found['pass']['total_hint_points'] = $pass_hint_points; 4258 $found['pass']['percent'] = ($pass_max > 0) ? $pass_reached / $pass_max : 0; 4259 $found['pass']['obligationsAnswered'] = $obligationsAnswered; 4260 $found['pass']['num_workedthrough'] = $numWorkedThrough; 4261 $found['pass']['num_questions_total'] = $numQuestionsTotal; 4262 4263 $found["test"]["total_max_points"] = $results['max_points']; 4264 $found["test"]["total_reached_points"] = $results['reached_points']; 4265 $found["test"]["total_requested_hints"] = $results['hint_count']; 4266 $found["test"]["total_hint_points"] = $results['hint_points']; 4267 $found["test"]["result_pass"] = $results['pass']; 4268 $found['test']['result_tstamp'] = $results['tstamp']; 4269 $found['test']['obligations_answered'] = $results['obligations_answered']; 4270 4271 if ((!$total_reached_points) or (!$total_max_points)) { 4272 $percentage = 0.0; 4273 } else { 4274 $percentage = ($total_reached_points / $total_max_points) * 100.0; 4275 4276 if ($percentage < 0) { 4277 $percentage = 0.0; 4278 } 4279 } 4280 4281 $found["test"]["passed"] = $results['passed']; 4282 4283 return $found; 4284 } 4285 4286 /** 4287 * Returns the number of persons who started the test 4288 * 4289 * @return integer The number of persons who started the test 4290 * @access public 4291 */ 4292 public function evalTotalPersons() 4293 { 4294 global $DIC; 4295 $ilDB = $DIC['ilDB']; 4296 4297 $result = $ilDB->queryF( 4298 "SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s", 4299 array('integer'), 4300 array($this->getTestId()) 4301 ); 4302 $row = $ilDB->fetchAssoc($result); 4303 return $row["total"]; 4304 } 4305 4306 /** 4307 * Returns the complete working time in seconds a user worked on the test 4308 * 4309 * @return integer The working time in seconds 4310 * @access public 4311 */ 4312 public function getCompleteWorkingTime($user_id) 4313 { 4314 global $DIC; 4315 $ilDB = $DIC['ilDB']; 4316 4317 $result = $ilDB->queryF( 4318 "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi AND tst_active.user_fi = %s", 4319 array('integer','integer'), 4320 array($this->getTestId(), $user_id) 4321 ); 4322 $time = 0; 4323 while ($row = $ilDB->fetchAssoc($result)) { 4324 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches); 4325 $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4326 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches); 4327 $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4328 $time += ($epoch_2 - $epoch_1); 4329 } 4330 return $time; 4331 } 4332 4333 /** 4334 * Returns the complete working time in seconds for all test participants 4335 * 4336 * @return array An array containing the working time in seconds for all test participants 4337 * @access public 4338 */ 4339 public function &getCompleteWorkingTimeOfParticipants() 4340 { 4341 return $this->_getCompleteWorkingTimeOfParticipants($this->getTestId()); 4342 } 4343 4344 /** 4345 * Returns the complete working time in seconds for all test participants 4346 * 4347 * @param integer $test_id The database ID of the test 4348 * @return array An array containing the working time in seconds for all test participants 4349 * @access public 4350 */ 4351 public function &_getCompleteWorkingTimeOfParticipants($test_id) 4352 { 4353 global $DIC; 4354 $ilDB = $DIC['ilDB']; 4355 4356 $result = $ilDB->queryF( 4357 "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi ORDER BY tst_times.active_fi, tst_times.started", 4358 array('integer'), 4359 array($test_id) 4360 ); 4361 $time = 0; 4362 $times = array(); 4363 while ($row = $ilDB->fetchAssoc($result)) { 4364 if (!array_key_exists($row["active_fi"], $times)) { 4365 $times[$row["active_fi"]] = 0; 4366 } 4367 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches); 4368 $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4369 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches); 4370 $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4371 $times[$row["active_fi"]] += ($epoch_2 - $epoch_1); 4372 } 4373 return $times; 4374 } 4375 4376 /** 4377 * Returns the complete working time in seconds for a test participant 4378 * 4379 * @return integer The working time in seconds for the test participant 4380 * @access public 4381 */ 4382 public function getCompleteWorkingTimeOfParticipant($active_id) 4383 { 4384 global $DIC; 4385 $ilDB = $DIC['ilDB']; 4386 4387 $result = $ilDB->queryF( 4388 "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi AND tst_active.active_id = %s ORDER BY tst_times.active_fi, tst_times.started", 4389 array('integer','integer'), 4390 array($this->getTestId(), $active_id) 4391 ); 4392 $time = 0; 4393 while ($row = $ilDB->fetchAssoc($result)) { 4394 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches); 4395 $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4396 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches); 4397 $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4398 $time += ($epoch_2 - $epoch_1); 4399 } 4400 return $time; 4401 } 4402 4403 /** 4404 * Returns the complete working time in seconds for a test participant 4405 * 4406 * @return integer The working time in seconds for the test participant 4407 * @access public 4408 */ 4409 public static function _getWorkingTimeOfParticipantForPass($active_id, $pass) 4410 { 4411 global $DIC; 4412 $ilDB = $DIC['ilDB']; 4413 4414 $result = $ilDB->queryF( 4415 "SELECT * FROM tst_times WHERE active_fi = %s AND pass = %s ORDER BY started", 4416 array('integer','integer'), 4417 array($active_id, $pass) 4418 ); 4419 $time = 0; 4420 while ($row = $ilDB->fetchAssoc($result)) { 4421 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches); 4422 $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4423 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches); 4424 $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4425 $time += ($epoch_2 - $epoch_1); 4426 } 4427 return $time; 4428 } 4429 4430 /** 4431 * Returns the first and last visit of a participant 4432 * 4433 * @param integer $active_id The active ID of the participant 4434 * @return array The first and last visit of a participant 4435 * @access public 4436 */ 4437 public function getVisitTimeOfParticipant($active_id) 4438 { 4439 return ilObjTest::_getVisitTimeOfParticipant($this->getTestId(), $active_id); 4440 } 4441 4442 /** 4443 * Returns the first and last visit of a participant 4444 * 4445 * @param integer $test_id The database ID of the test 4446 * @param integer $active_id The active ID of the participant 4447 * @return array The first and last visit of a participant 4448 * @access public 4449 */ 4450 public function _getVisitTimeOfParticipant($test_id, $active_id) 4451 { 4452 global $DIC; 4453 $ilDB = $DIC['ilDB']; 4454 4455 $result = $ilDB->queryF( 4456 "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi AND tst_active.active_id = %s ORDER BY tst_times.started", 4457 array('integer','integer'), 4458 array($test_id, $active_id) 4459 ); 4460 $firstvisit = 0; 4461 $lastvisit = 0; 4462 while ($row = $ilDB->fetchAssoc($result)) { 4463 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches); 4464 $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4465 if ($firstvisit == 0 || $epoch_1 < $firstvisit) { 4466 $firstvisit = $epoch_1; 4467 } 4468 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches); 4469 $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4470 if ($epoch_2 > $lastvisit) { 4471 $lastvisit = $epoch_2; 4472 } 4473 } 4474 return array("firstvisit" => $firstvisit, "lastvisit" => $lastvisit); 4475 } 4476 4477 /** 4478 * Returns the statistical evaluation of the test for a specified user 4479 * 4480 * @return arrary The statistical evaluation array of the test 4481 * @access public 4482 */ 4483 public function &evalStatistical($active_id) 4484 { 4485 global $DIC; 4486 $ilDB = $DIC['ilDB']; 4487 // $ilBench = $DIC['ilBench']; 4488 $pass = ilObjTest::_getResultPass($active_id); 4489 $test_result = &$this->getTestResult($active_id, $pass); 4490 $result = $ilDB->queryF( 4491 "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.active_id = %s AND tst_active.active_id = tst_times.active_fi", 4492 array('integer'), 4493 array($active_id) 4494 ); 4495 $times = array(); 4496 $first_visit = 0; 4497 $last_visit = 0; 4498 while ($row = $ilDB->fetchObject($result)) { 4499 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches); 4500 $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4501 if (!$first_visit) { 4502 $first_visit = $epoch_1; 4503 } 4504 if ($epoch_1 < $first_visit) { 4505 $first_visit = $epoch_1; 4506 } 4507 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches); 4508 $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 4509 if (!$last_visit) { 4510 $last_visit = $epoch_2; 4511 } 4512 if ($epoch_2 > $last_visit) { 4513 $last_visit = $epoch_2; 4514 } 4515 $times[$row->active_fi] += ($epoch_2 - $epoch_1); 4516 } 4517 $max_time = 0; 4518 foreach ($times as $key => $value) { 4519 $max_time += $value; 4520 } 4521 if ((!$test_result["test"]["total_reached_points"]) or (!$test_result["test"]["total_max_points"])) { 4522 $percentage = 0.0; 4523 } else { 4524 $percentage = ($test_result["test"]["total_reached_points"] / $test_result["test"]["total_max_points"]) * 100.0; 4525 if ($percentage < 0) { 4526 $percentage = 0.0; 4527 } 4528 } 4529 $mark_obj = $this->mark_schema->getMatchingMark($percentage); 4530 $first_date = getdate($first_visit); 4531 $last_date = getdate($last_visit); 4532 $qworkedthrough = 0; 4533 foreach ($test_result as $key => $value) { 4534 if (preg_match("/\d+/", $key)) { 4535 $qworkedthrough += $value["workedthrough"]; 4536 } 4537 } 4538 if (!$qworkedthrough) { 4539 $atimeofwork = 0; 4540 } else { 4541 $atimeofwork = $max_time / $qworkedthrough; 4542 } 4543 4544 $obligationsAnswered = $test_result["test"]["obligations_answered"]; 4545 4546 $result_mark = ""; 4547 $passed = ""; 4548 4549 if ($mark_obj) { 4550 $result_mark = $mark_obj->getShortName(); 4551 4552 if ($mark_obj->getPassed() && $obligationsAnswered) { 4553 $passed = 1; 4554 } else { 4555 $passed = 0; 4556 } 4557 } 4558 $percent_worked_through = 0; 4559 if (count($this->questions)) { 4560 $percent_worked_through = $qworkedthrough / count($this->questions); 4561 } 4562 $result_array = array( 4563 "qworkedthrough" => $qworkedthrough, 4564 "qmax" => count($this->questions), 4565 "pworkedthrough" => $percent_worked_through, 4566 "timeofwork" => $max_time, 4567 "atimeofwork" => $atimeofwork, 4568 "firstvisit" => $first_date, 4569 "lastvisit" => $last_date, 4570 "resultspoints" => $test_result["test"]["total_reached_points"], 4571 "maxpoints" => $test_result["test"]["total_max_points"], 4572 "resultsmarks" => $result_mark, 4573 "passed" => $passed, 4574 "distancemedian" => "0" 4575 ); 4576 foreach ($test_result as $key => $value) { 4577 if (preg_match("/\d+/", $key)) { 4578 $result_array[$key] = $value; 4579 } 4580 } 4581 return $result_array; 4582 } 4583 4584 /** 4585 * Returns an array with the total points of all users who passed the test 4586 * This array could be used for statistics 4587 * 4588 * @return array The total point values 4589 * @access public 4590 */ 4591 public function &getTotalPointsPassedArray() 4592 { 4593 $totalpoints_array = array(); 4594 $all_users = &$this->evalTotalParticipantsArray(); 4595 foreach ($all_users as $active_id => $user_name) { 4596 $test_result = &$this->getTestResult($active_id); 4597 $reached = $test_result["test"]["total_reached_points"]; 4598 $total = $test_result["test"]["total_max_points"]; 4599 $percentage = $total != 0 ? $reached / $total : 0; 4600 $mark = $this->mark_schema->getMatchingMark($percentage * 100.0); 4601 4602 $obligationsAnswered = $test_result["test"]["obligations_answered"]; 4603 4604 if ($mark) { 4605 if ($mark->getPassed() && $obligationsAnswered) { 4606 array_push($totalpoints_array, $test_result["test"]["total_reached_points"]); 4607 } 4608 } 4609 } 4610 return $totalpoints_array; 4611 } 4612 4613 /** 4614 * Returns all persons who started the test 4615 * 4616 * @return array The active ids, names and logins of the persons who started the test 4617 */ 4618 public function &getParticipants() 4619 { 4620 global $DIC; 4621 $ilDB = $DIC['ilDB']; 4622 $result = $ilDB->queryF( 4623 "SELECT tst_active.active_id, usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login FROM tst_active LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id WHERE tst_active.test_fi = %s ORDER BY usr_data.lastname ASC", 4624 array('integer'), 4625 array($this->getTestId()) 4626 ); 4627 $persons_array = array(); 4628 while ($row = $ilDB->fetchAssoc($result)) { 4629 $name = $this->lng->txt("anonymous"); 4630 $fullname = $this->lng->txt("anonymous"); 4631 $login = ""; 4632 if (!$this->getAnonymity()) { 4633 if (strlen($row["firstname"] . $row["lastname"] . $row["title"]) == 0) { 4634 $name = $this->lng->txt("deleted_user"); 4635 $fullname = $this->lng->txt("deleted_user"); 4636 $login = $this->lng->txt("unknown"); 4637 } else { 4638 $login = $row["login"]; 4639 if ($row["user_fi"] == ANONYMOUS_USER_ID) { 4640 $name = $this->lng->txt("anonymous"); 4641 $fullname = $this->lng->txt("anonymous"); 4642 } else { 4643 $name = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]); 4644 $fullname = trim($row["title"] . " " . $row["firstname"] . " " . $row["lastname"]); 4645 } 4646 } 4647 } 4648 $persons_array[$row["active_id"]] = array( 4649 "name" => $name, 4650 "fullname" => $fullname, 4651 "login" => $login 4652 ); 4653 } 4654 return $persons_array; 4655 } 4656 4657 /** 4658 * Returns all persons who started the test 4659 * 4660 * @return arrary The user id's and names of the persons who started the test 4661 * @access public 4662 */ 4663 public function &evalTotalPersonsArray($name_sort_order = "asc") 4664 { 4665 global $DIC; 4666 $ilDB = $DIC['ilDB']; 4667 $result = $ilDB->queryF( 4668 "SELECT tst_active.active_id, usr_data.firstname, usr_data.lastname, usr_data.title FROM tst_active LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id WHERE tst_active.test_fi = %s ORDER BY usr_data.lastname " . strtoupper($name_sort_order), 4669 array('integer'), 4670 array($this->getTestId()) 4671 ); 4672 $persons_array = array(); 4673 while ($row = $ilDB->fetchAssoc($result)) { 4674 if ($this->getAccessFilteredParticipantList() && !$this->getAccessFilteredParticipantList()->isActiveIdInList($row["active_id"])) { 4675 continue; 4676 } 4677 4678 if ($this->getAnonymity()) { 4679 $persons_array[$row["active_id"]] = $this->lng->txt("anonymous"); 4680 } else { 4681 if (strlen($row["firstname"] . $row["lastname"] . $row["title"]) == 0) { 4682 $persons_array[$row["active_id"]] = $this->lng->txt("deleted_user"); 4683 } else { 4684 if ($row["user_fi"] == ANONYMOUS_USER_ID) { 4685 $persons_array[$row["active_id"]] = $row["lastname"]; 4686 } else { 4687 $persons_array[$row["active_id"]] = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]); 4688 } 4689 } 4690 } 4691 } 4692 return $persons_array; 4693 } 4694 4695 /** 4696 * Returns all participants who started the test 4697 * 4698 * @return arrary The active user id's and names of the persons who started the test 4699 * @access public 4700 */ 4701 public function &evalTotalParticipantsArray($name_sort_order = "asc") 4702 { 4703 global $DIC; 4704 $ilDB = $DIC['ilDB']; 4705 $result = $ilDB->queryF( 4706 "SELECT tst_active.active_id, usr_data.login, usr_data.firstname, usr_data.lastname, usr_data.title FROM tst_active LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id WHERE tst_active.test_fi = %s ORDER BY usr_data.lastname " . strtoupper($name_sort_order), 4707 array('integer'), 4708 array($this->getTestId()) 4709 ); 4710 $persons_array = array(); 4711 while ($row = $ilDB->fetchAssoc($result)) { 4712 if ($this->getAnonymity()) { 4713 $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("anonymous")); 4714 } else { 4715 if (strlen($row["firstname"] . $row["lastname"] . $row["title"]) == 0) { 4716 $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("deleted_user")); 4717 } else { 4718 if ($row["user_fi"] == ANONYMOUS_USER_ID) { 4719 $persons_array[$row["active_id"]] = array("name" => $row["lastname"]); 4720 } else { 4721 $persons_array[$row["active_id"]] = array("name" => trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]), "login" => $row["login"]); 4722 } 4723 } 4724 } 4725 } 4726 return $persons_array; 4727 } 4728 4729 /** 4730 * Retrieves all the assigned questions for all test passes of a test participant 4731 * 4732 * @return array An associated array containing the questions 4733 * @access public 4734 */ 4735 public function &getQuestionsOfTest($active_id) 4736 { 4737 global $DIC; 4738 $ilDB = $DIC['ilDB']; 4739 if ($this->isRandomTest()) { 4740 $ilDB->setLimit($this->getQuestionCount(), 0); 4741 $result = $ilDB->queryF( 4742 "SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " . 4743 "tst_test_rnd_qst.pass, qpl_questions.points " . 4744 "FROM tst_test_rnd_qst, qpl_questions " . 4745 "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " . 4746 "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence", 4747 array('integer'), 4748 array($active_id) 4749 ); 4750 } else { 4751 $result = $ilDB->queryF( 4752 "SELECT tst_test_question.sequence, tst_test_question.question_fi, " . 4753 "qpl_questions.points " . 4754 "FROM tst_test_question, tst_active, qpl_questions " . 4755 "WHERE tst_test_question.question_fi = qpl_questions.question_id " . 4756 "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi", 4757 array('integer'), 4758 array($active_id) 4759 ); 4760 } 4761 $qtest = array(); 4762 if ($result->numRows()) { 4763 while ($row = $ilDB->fetchAssoc($result)) { 4764 array_push($qtest, $row); 4765 } 4766 } 4767 return $qtest; 4768 } 4769 4770 /** 4771 * Retrieves all the assigned questions for a test participant in a given test pass 4772 * 4773 * @return array An associated array containing the questions 4774 * @access public 4775 */ 4776 public function &getQuestionsOfPass($active_id, $pass) 4777 { 4778 global $DIC; 4779 $ilDB = $DIC['ilDB']; 4780 if ($this->isRandomTest()) { 4781 $ilDB->setLimit($this->getQuestionCount(), 0); 4782 $result = $ilDB->queryF( 4783 "SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " . 4784 "qpl_questions.points " . 4785 "FROM tst_test_rnd_qst, qpl_questions " . 4786 "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " . 4787 "AND tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.pass = %s " . 4788 "ORDER BY tst_test_rnd_qst.sequence", 4789 array('integer', 'integer'), 4790 array($active_id, $pass) 4791 ); 4792 } else { 4793 $result = $ilDB->queryF( 4794 "SELECT tst_test_question.sequence, tst_test_question.question_fi, " . 4795 "qpl_questions.points " . 4796 "FROM tst_test_question, tst_active, qpl_questions " . 4797 "WHERE tst_test_question.question_fi = qpl_questions.question_id " . 4798 "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi", 4799 array('integer'), 4800 array($active_id) 4801 ); 4802 } 4803 $qpass = array(); 4804 if ($result->numRows()) { 4805 while ($row = $ilDB->fetchAssoc($result)) { 4806 array_push($qpass, $row); 4807 } 4808 } 4809 return $qpass; 4810 } 4811 4812 /** 4813 * @var ilTestParticipantList 4814 */ 4815 protected $accessFilteredParticipantList; 4816 4817 /** 4818 * @return ilTestParticipantList 4819 */ 4820 public function getAccessFilteredParticipantList() 4821 { 4822 return $this->accessFilteredParticipantList; 4823 } 4824 4825 /** 4826 * @param ilTestParticipantList $accessFilteredParticipantList 4827 */ 4828 public function setAccessFilteredParticipantList($accessFilteredParticipantList) 4829 { 4830 $this->accessFilteredParticipantList = $accessFilteredParticipantList; 4831 } 4832 4833 /** 4834 * @return ilTestParticipantList 4835 */ 4836 public function buildStatisticsAccessFilteredParticipantList() 4837 { 4838 require_once 'Modules/Test/classes/class.ilTestParticipantList.php'; 4839 require_once 'Modules/Test/classes/class.ilTestParticipantAccessFilter.php'; 4840 4841 $list = new ilTestParticipantList($this); 4842 $list->initializeFromDbRows($this->getTestParticipants()); 4843 4844 $list = $list->getAccessFilteredList( 4845 ilTestParticipantAccessFilter::getAccessStatisticsUserFilter($this->getRefId()) 4846 ); 4847 4848 return $list; 4849 } 4850 4851 public function getUnfilteredEvaluationData() 4852 { 4853 /** @var $DIC ILIAS\DI\Container */ 4854 global $DIC; 4855 4856 $ilDB = $DIC->database(); 4857 4858 include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php"; 4859 include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php"; 4860 include_once "./Modules/Test/classes/class.ilTestEvaluationData.php"; 4861 4862 $data = new ilTestEvaluationData($this); 4863 4864 $query = " 4865 SELECT tst_test_result.*, 4866 qpl_questions.original_id, 4867 qpl_questions.title questiontitle, 4868 qpl_questions.points maxpoints 4869 4870 FROM tst_test_result, qpl_questions, tst_active 4871 4872 WHERE tst_active.active_id = tst_test_result.active_fi 4873 AND qpl_questions.question_id = tst_test_result.question_fi 4874 AND tst_active.test_fi = %s 4875 4876 ORDER BY tst_active.active_id ASC, tst_test_result.pass ASC, tst_test_result.tstamp DESC 4877 "; 4878 4879 $result = $ilDB->queryF( 4880 $query, 4881 array('integer'), 4882 array($this->getTestId()) 4883 ); 4884 4885 $pass = null; 4886 $checked = array(); 4887 $datasets = 0; 4888 $questionData = []; 4889 4890 while ($row = $ilDB->fetchAssoc($result)) { 4891 $participantObject = $data->getParticipant($row["active_fi"]); 4892 4893 if (!($participantObject instanceof ilTestEvaluationUserData)) { 4894 continue; 4895 } 4896 4897 $passObject = $participantObject->getPass($row["pass"]); 4898 4899 if (!($passObject instanceof ilTestEvaluationPassData)) { 4900 continue; 4901 } 4902 4903 $passObject->addAnsweredQuestion( 4904 $row["question_fi"], 4905 $row["maxpoints"], 4906 $row["points"], 4907 $row['answered'], 4908 null, 4909 $row['manual'] 4910 ); 4911 } 4912 4913 foreach (array_keys($data->getParticipants()) as $active_id) { 4914 if ($this->isRandomTest()) { 4915 for ($testpass = 0; $testpass <= $data->getParticipant($active_id)->getLastPass(); $testpass++) { 4916 $ilDB->setLimit($this->getQuestionCount(), 0); 4917 4918 $query = " 4919 SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, qpl_questions.original_id, 4920 tst_test_rnd_qst.pass, qpl_questions.points, qpl_questions.title 4921 FROM tst_test_rnd_qst, qpl_questions 4922 WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id 4923 AND tst_test_rnd_qst.pass = %s 4924 AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence 4925 "; 4926 4927 $result = $ilDB->queryF( 4928 $query, 4929 array('integer','integer'), 4930 array($testpass, $active_id) 4931 ); 4932 4933 if ($result->numRows()) { 4934 while ($row = $ilDB->fetchAssoc($result)) { 4935 $tpass = array_key_exists("pass", $row) ? $row["pass"] : 0; 4936 4937 $data->getParticipant($active_id)->addQuestion( 4938 $row["original_id"], 4939 $row["question_fi"], 4940 $row["points"], 4941 $row["sequence"], 4942 $tpass 4943 ); 4944 4945 $data->addQuestionTitle($row["question_fi"], $row["title"]); 4946 } 4947 } 4948 } 4949 } elseif ($this->isDynamicTest()) { 4950 $lastPass = $data->getParticipant($active_id)->getLastPass(); 4951 for ($testpass = 0; $testpass <= $lastPass; $testpass++) { 4952 require_once 'Modules/Test/classes/class.ilObjTestDynamicQuestionSetConfig.php'; 4953 $dynamicQuestionSetConfig = new ilObjTestDynamicQuestionSetConfig( 4954 $DIC->repositoryTree(), 4955 $DIC->database(), 4956 $DIC['ilPluginAdmin'], 4957 $this 4958 ); 4959 $dynamicQuestionSetConfig->loadFromDb(); 4960 4961 require_once 'Modules/Test/classes/class.ilTestSequenceFactory.php'; 4962 $testSequenceFactory = new ilTestSequenceFactory($DIC->database(), $DIC->language(), $DIC['ilPluginAdmin'], $this); 4963 $testSequence = $testSequenceFactory->getSequenceByActiveIdAndPass($active_id, $testpass); 4964 4965 $testSequence->loadFromDb($dynamicQuestionSetConfig); 4966 $testSequence->loadQuestions($dynamicQuestionSetConfig, new ilTestDynamicQuestionSetFilterSelection()); 4967 4968 $sequence = (array) $testSequence->getUserSequenceQuestions(); 4969 4970 $questionsIdsToRequest = array_diff(array_values($sequence), array_values($questionData)); 4971 if (count($questionsIdsToRequest) > 0) { 4972 $questionIdsCondition = ' ' . $DIC->database()->in('question_id', array_values($questionsIdsToRequest), false, 'integer') . ' '; 4973 4974 $res = $DIC->database()->queryF( 4975 " 4976 SELECT * 4977 FROM qpl_questions 4978 WHERE {$questionIdsCondition}", 4979 array('integer'), 4980 array($active_id) 4981 ); 4982 while ($row = $DIC->database()->fetchAssoc($res)) { 4983 $questionData[$row['question_id']] = $row; 4984 $data->addQuestionTitle($row['question_id'], $row['title']); 4985 } 4986 } 4987 4988 foreach ($sequence as $questionId) { 4989 if (!isset($questionData[$questionId])) { 4990 continue; 4991 } 4992 4993 $row = $questionData[$questionId]; 4994 4995 $data->getParticipant( 4996 $active_id 4997 )->addQuestion( 4998 $row['original_id'], 4999 $row['question_id'], 5000 $row['points'], 5001 null, 5002 $testpass 5003 ); 5004 } 5005 } 5006 } else { 5007 $query = " 5008 SELECT tst_test_question.sequence, tst_test_question.question_fi, 5009 qpl_questions.points, qpl_questions.title, qpl_questions.original_id 5010 FROM tst_test_question, tst_active, qpl_questions 5011 WHERE tst_test_question.question_fi = qpl_questions.question_id 5012 AND tst_active.active_id = %s 5013 AND tst_active.test_fi = tst_test_question.test_fi 5014 ORDER BY tst_test_question.sequence 5015 "; 5016 5017 $result = $ilDB->queryF( 5018 $query, 5019 array('integer'), 5020 array($active_id) 5021 ); 5022 5023 if ($result->numRows()) { 5024 $questionsbysequence = array(); 5025 5026 while ($row = $ilDB->fetchAssoc($result)) { 5027 $questionsbysequence[$row["sequence"]] = $row; 5028 } 5029 5030 $seqresult = $ilDB->queryF( 5031 "SELECT * FROM tst_sequence WHERE active_fi = %s", 5032 array('integer'), 5033 array($active_id) 5034 ); 5035 5036 while ($seqrow = $ilDB->fetchAssoc($seqresult)) { 5037 $questionsequence = unserialize($seqrow["sequence"]); 5038 5039 foreach ($questionsequence as $sidx => $seq) { 5040 $data->getParticipant($active_id)->addQuestion( 5041 $questionsbysequence[$seq]["original_id"], 5042 $questionsbysequence[$seq]["question_fi"], 5043 $questionsbysequence[$seq]["points"], 5044 $sidx + 1, 5045 $seqrow["pass"] 5046 ); 5047 5048 $data->addQuestionTitle( 5049 $questionsbysequence[$seq]["question_fi"], 5050 $questionsbysequence[$seq]["title"] 5051 ); 5052 } 5053 } 5054 } 5055 } 5056 } 5057 5058 if ($this->getECTSOutput()) { 5059 $passed_array = &$this->getTotalPointsPassedArray(); 5060 } 5061 5062 foreach (array_keys($data->getParticipants()) as $active_id) { 5063 $tstUserData = $data->getParticipant($active_id); 5064 5065 $percentage = $tstUserData->getReachedPointsInPercent(); 5066 5067 $obligationsAnswered = $tstUserData->areObligationsAnswered(); 5068 5069 $mark = $this->mark_schema->getMatchingMark($percentage); 5070 5071 if (is_object($mark)) { 5072 $tstUserData->setMark($mark->getShortName()); 5073 $tstUserData->setMarkOfficial($mark->getOfficialName()); 5074 5075 $tstUserData->setPassed( 5076 $mark->getPassed() && $tstUserData->areObligationsAnswered() 5077 ); 5078 } 5079 5080 if ($this->getECTSOutput()) { 5081 $ects_mark = $this->getECTSGrade( 5082 $passed_array, 5083 $tstUserData->getReached(), 5084 $tstUserData->getMaxPoints() 5085 ); 5086 5087 $tstUserData->setECTSMark($ects_mark); 5088 } 5089 5090 $visitingTime = &$this->getVisitTimeOfParticipant($active_id); 5091 5092 $tstUserData->setFirstVisit($visitingTime["firstvisit"]); 5093 $tstUserData->setLastVisit($visitingTime["lastvisit"]); 5094 } 5095 5096 return $data; 5097 } 5098 5099 public static function _getQuestionCountAndPointsForPassOfParticipant($active_id, $pass) 5100 { 5101 global $DIC; 5102 $ilDB = $DIC['ilDB']; 5103 5104 $questionSetType = ilObjTest::lookupQuestionSetTypeByActiveId($active_id); 5105 5106 switch ($questionSetType) { 5107 case ilObjTest::QUESTION_SET_TYPE_DYNAMIC: 5108 5109 $res = $ilDB->queryF( 5110 " 5111 SELECT COUNT(qpl_questions.question_id) qcount, 5112 SUM(qpl_questions.points) qsum 5113 FROM tst_active 5114 INNER JOIN tst_tests 5115 ON tst_tests.test_id = tst_active.test_fi 5116 INNER JOIN tst_dyn_quest_set_cfg 5117 ON tst_dyn_quest_set_cfg.test_fi = tst_tests.test_id 5118 INNER JOIN qpl_questions 5119 ON qpl_questions.obj_fi = tst_dyn_quest_set_cfg.source_qpl_fi 5120 AND qpl_questions.original_id IS NULL 5121 AND qpl_questions.complete = %s 5122 WHERE tst_active.active_id = %s 5123 ", 5124 array('integer', 'integer'), 5125 array(1, $active_id) 5126 ); 5127 5128 break; 5129 5130 case ilObjTest::QUESTION_SET_TYPE_RANDOM: 5131 5132 $res = $ilDB->queryF( 5133 " 5134 SELECT tst_test_rnd_qst.pass, 5135 COUNT(tst_test_rnd_qst.question_fi) qcount, 5136 SUM(qpl_questions.points) qsum 5137 5138 FROM tst_test_rnd_qst, 5139 qpl_questions 5140 5141 WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id 5142 AND tst_test_rnd_qst.active_fi = %s 5143 AND pass = %s 5144 5145 GROUP BY tst_test_rnd_qst.active_fi, 5146 tst_test_rnd_qst.pass 5147 ", 5148 array('integer', 'integer'), 5149 array($active_id, $pass) 5150 ); 5151 5152 break; 5153 5154 case ilObjTest::QUESTION_SET_TYPE_FIXED: 5155 5156 $res = $ilDB->queryF( 5157 " 5158 SELECT COUNT(tst_test_question.question_fi) qcount, 5159 SUM(qpl_questions.points) qsum 5160 5161 FROM tst_test_question, 5162 qpl_questions, 5163 tst_active 5164 5165 WHERE tst_test_question.question_fi = qpl_questions.question_id 5166 AND tst_test_question.test_fi = tst_active.test_fi 5167 AND tst_active.active_id = %s 5168 5169 GROUP BY tst_test_question.test_fi 5170 ", 5171 array('integer'), 5172 array($active_id) 5173 ); 5174 5175 break; 5176 5177 default: 5178 5179 throw new ilTestException("not supported question set type: $questionSetType"); 5180 } 5181 5182 $row = $ilDB->fetchAssoc($res); 5183 5184 if (is_array($row)) { 5185 return array("count" => $row["qcount"], "points" => $row["qsum"]); 5186 } 5187 5188 return array("count" => 0, "points" => 0); 5189 } 5190 5191 public function &getCompleteEvaluationData($withStatistics = true, $filterby = "", $filtertext = "") 5192 { 5193 include_once "./Modules/Test/classes/class.ilTestEvaluationData.php"; 5194 include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php"; 5195 include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php"; 5196 $data = $this->getUnfilteredEvaluationData(); 5197 if ($withStatistics) { 5198 $data->calculateStatistics(); 5199 } 5200 $data->setFilter($filterby, $filtertext); 5201 return $data; 5202 } 5203 5204 /** 5205 * Creates an associated array with the results of all participants of a test 5206 * 5207 * @return array An associated array containing the results 5208 * @access public 5209 */ 5210 public function &evalResultsOverview() 5211 { 5212 return $this->_evalResultsOverview($this->getTestId()); 5213 } 5214 5215 /** 5216 * Creates an associated array with the results of all participants of a test 5217 * 5218 * @return array An associated array containing the results 5219 * @access public 5220 */ 5221 public function &_evalResultsOverview($test_id) 5222 { 5223 global $DIC; 5224 $ilDB = $DIC['ilDB']; 5225 5226 $result = $ilDB->queryF( 5227 "SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " . 5228 "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " . 5229 "qpl_questions.points maxpoints " . 5230 "FROM tst_test_result, qpl_questions, tst_active " . 5231 "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " . 5232 "WHERE tst_active.active_id = tst_test_result.active_fi " . 5233 "AND qpl_questions.question_id = tst_test_result.question_fi " . 5234 "AND tst_active.test_fi = %s " . 5235 "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp", 5236 array('integer'), 5237 array($test_id) 5238 ); 5239 $overview = array(); 5240 while ($row = $ilDB->fetchAssoc($result)) { 5241 if (!array_key_exists($row["active_fi"], $overview)) { 5242 $overview[$row["active_fi"]] = array(); 5243 $overview[$row["active_fi"]]["firstname"] = $row["firstname"]; 5244 $overview[$row["active_fi"]]["lastname"] = $row["lastname"]; 5245 $overview[$row["active_fi"]]["title"] = $row["title"]; 5246 $overview[$row["active_fi"]]["login"] = $row["login"]; 5247 $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"]; 5248 $overview[$row["active_fi"]]["started"] = $row["started"]; 5249 $overview[$row["active_fi"]]["finished"] = $row["finished"]; 5250 } 5251 if (!array_key_exists($row["pass"], $overview[$row["active_fi"]])) { 5252 $overview[$row["active_fi"]][$row["pass"]] = array(); 5253 $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0; 5254 $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"]; 5255 } 5256 array_push($overview[$row["active_fi"]][$row["pass"]], $row); 5257 $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"]; 5258 } 5259 return $overview; 5260 } 5261 5262 /** 5263 * Creates an associated array with the results for a given participant of a test 5264 * 5265 * @param integer $active_id The active id of the participant 5266 * @return array An associated array containing the results 5267 * @access public 5268 */ 5269 public function &evalResultsOverviewOfParticipant($active_id) 5270 { 5271 global $DIC; 5272 $ilDB = $DIC['ilDB']; 5273 5274 $result = $ilDB->queryF( 5275 "SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " . 5276 "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " . 5277 "qpl_questions.points maxpoints " . 5278 "FROM tst_test_result, qpl_questions, tst_active " . 5279 "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " . 5280 "WHERE tst_active.active_id = tst_test_result.active_fi " . 5281 "AND qpl_questions.question_id = tst_test_result.question_fi " . 5282 "AND tst_active.test_fi = %s AND tst_active.active_id = %s" . 5283 "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp", 5284 array('integer', 'integer'), 5285 array($this->getTestId(), $active_id) 5286 ); 5287 $overview = array(); 5288 while ($row = $ilDB->fetchAssoc($result)) { 5289 if (!array_key_exists($row["active_fi"], $overview)) { 5290 $overview[$row["active_fi"]] = array(); 5291 $overview[$row["active_fi"]]["firstname"] = $row["firstname"]; 5292 $overview[$row["active_fi"]]["lastname"] = $row["lastname"]; 5293 $overview[$row["active_fi"]]["title"] = $row["title"]; 5294 $overview[$row["active_fi"]]["login"] = $row["login"]; 5295 $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"]; 5296 $overview[$row["active_fi"]]["started"] = $row["started"]; 5297 $overview[$row["active_fi"]]["finished"] = $row["finished"]; 5298 } 5299 if (!array_key_exists($row["pass"], $overview[$row["active_fi"]])) { 5300 $overview[$row["active_fi"]][$row["pass"]] = array(); 5301 $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0; 5302 $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"]; 5303 } 5304 array_push($overview[$row["active_fi"]][$row["pass"]], $row); 5305 $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"]; 5306 } 5307 return $overview; 5308 } 5309 5310 /** 5311 * Builds a user name for the output depending on test type and existence of 5312 * the user 5313 * 5314 * @param int $user_id The database ID of the user 5315 * @param string $firstname The first name of the user 5316 * @param string $lastname The last name of the user 5317 * @param string $title The title of the user 5318 * @return string The output name of the user 5319 * @access public 5320 */ 5321 public function buildName($user_id, $firstname, $lastname, $title) 5322 { 5323 $name = ""; 5324 if (strlen($firstname . $lastname . $title) == 0) { 5325 $name = $this->lng->txt("deleted_user"); 5326 } else { 5327 if ($user_id == ANONYMOUS_USER_ID) { 5328 $name = $lastname; 5329 } else { 5330 $name = trim($lastname . ", " . $firstname . " " . $title); 5331 } 5332 if ($this->getAnonymity()) { 5333 $name = $this->lng->txt("anonymous"); 5334 } 5335 } 5336 return $name; 5337 } 5338 5339 /** 5340 * Builds a user name for the output depending on test type and existence of 5341 * the user 5342 * 5343 * @param boolean $is_anonymous Indicates if it is an anonymized test or not 5344 * @param int $user_id The database ID of the user 5345 * @param string $firstname The first name of the user 5346 * @param string $lastname The last name of the user 5347 * @param string $title The title of the user 5348 * @return string The output name of the user 5349 * @access public 5350 */ 5351 public function _buildName($is_anonymous, $user_id, $firstname, $lastname, $title) 5352 { 5353 global $DIC; 5354 $lng = $DIC['lng']; 5355 $name = ""; 5356 if (strlen($firstname . $lastname . $title) == 0) { 5357 $name = $lng->txt("deleted_user"); 5358 } else { 5359 if ($user_id == ANONYMOUS_USER_ID) { 5360 $name = $lastname; 5361 } else { 5362 $name = trim($lastname . ", " . $firstname . " " . $title); 5363 } 5364 if ($is_anonymous) { 5365 $name = $lng->txt("anonymous"); 5366 } 5367 } 5368 return $name; 5369 } 5370 5371 /** 5372 * Returns the average processing time for all started tests 5373 * 5374 * @return integer The average processing time for all started tests 5375 * @access public 5376 */ 5377 public function evalTotalStartedAverageTime($activeIdsFilter = null) 5378 { 5379 global $DIC; /* @var ILIAS\DI\Container $DIC */ 5380 5381 $query = "SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi"; 5382 5383 if (is_array($activeIdsFilter) && count($activeIdsFilter)) { 5384 $query .= " AND " . $DIC->database()->in('active_id', $activeIdsFilter, false, 'integer'); 5385 } 5386 5387 $result = $DIC->database()->queryF($query, array('integer'), array($this->getTestId())); 5388 $times = array(); 5389 while ($row = $DIC->database()->fetchObject($result)) { 5390 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches); 5391 $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 5392 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches); 5393 $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 5394 $times[$row->active_fi] += ($epoch_2 - $epoch_1); 5395 } 5396 $max_time = 0; 5397 $counter = 0; 5398 foreach ($times as $key => $value) { 5399 $max_time += $value; 5400 $counter++; 5401 } 5402 if ($counter) { 5403 $average_time = round($max_time / $counter); 5404 } else { 5405 $average_time = 0; 5406 } 5407 return $average_time; 5408 } 5409 5410 /** 5411 * Returns the available question pools for the active user 5412 * 5413 * @return array The available question pools 5414 * @access public 5415 */ 5416 public function &getAvailableQuestionpools($use_object_id = false, $equal_points = false, $could_be_offline = false, $show_path = false, $with_questioncount = false, $permission = "read") 5417 { 5418 include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php"; 5419 return ilObjQuestionPool::_getAvailableQuestionpools($use_object_id, $equal_points, $could_be_offline, $show_path, $with_questioncount, $permission); 5420 } 5421 5422 /** 5423 * Returns the estimated working time for the test calculated from the working time of the contained questions 5424 * 5425 * @return array An associative array containing the working time. array["h"] = hours, array["m"] = minutes, array["s"] = seconds 5426 * @access public 5427 */ 5428 public function getEstimatedWorkingTime() 5429 { 5430 $time_in_seconds = 0; 5431 foreach ($this->questions as $question_id) { 5432 $question = &ilObjTest::_instanciateQuestion($question_id); 5433 $est_time = $question->getEstimatedWorkingTime(); 5434 $time_in_seconds += $est_time["h"] * 3600 + $est_time["m"] * 60 + $est_time["s"]; 5435 } 5436 $hours = (int) ($time_in_seconds / 3600) ; 5437 $time_in_seconds = $time_in_seconds - ($hours * 3600); 5438 $minutes = (int) ($time_in_seconds / 60); 5439 $time_in_seconds = $time_in_seconds - ($minutes * 60); 5440 $result = array("hh" => $hours, "mm" => $minutes, "ss" => $time_in_seconds); 5441 return $result; 5442 } 5443 5444 /** 5445 * Returns the image path for web accessable images of a test 5446 * The image path is under the CLIENT_WEB_DIR in assessment/REFERENCE_ID_OF_TEST/images 5447 * 5448 * @access public 5449 */ 5450 public function getImagePath() 5451 { 5452 return CLIENT_WEB_DIR . "/assessment/" . $this->getId() . "/images/"; 5453 } 5454 5455 /** 5456 * Returns the web image path for web accessable images of a test 5457 * The image path is under the web accessable data dir in assessment/REFERENCE_ID_OF_TEST/images 5458 * 5459 * @access public 5460 */ 5461 public function getImagePathWeb() 5462 { 5463 include_once "./Services/Utilities/classes/class.ilUtil.php"; 5464 $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/" . $this->getId() . "/images/"; 5465 return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir); 5466 } 5467 5468 /** 5469 * Creates a question GUI instance of a given question type 5470 * 5471 * @param integer $question_type The question type of the question 5472 * @param integer $question_id The question id of the question, if available 5473 * @return assQuestionGUI $questionGUI The question GUI instance 5474 * @access public 5475 */ 5476 public function &createQuestionGUI($question_type, $question_id = -1) 5477 { 5478 if ((!$question_type) and ($question_id > 0)) { 5479 $question_type = $this->getQuestionType($question_id); 5480 } 5481 5482 if (!strlen($question_type)) { 5483 return null; 5484 } 5485 5486 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 5487 assQuestion::_includeClass($question_type, 1); 5488 5489 $question_type_gui = assQuestion::getGuiClassNameByQuestionType($question_type); 5490 $question = new $question_type_gui(); 5491 5492 if ($question_id > 0) { 5493 $question->object->loadFromDb($question_id); 5494 5495 global $DIC; 5496 $ilCtrl = $DIC['ilCtrl']; 5497 $ilDB = $DIC['ilDB']; 5498 $ilUser = $DIC['ilUser']; 5499 $lng = $DIC['lng']; 5500 5501 $feedbackObjectClassname = assQuestion::getFeedbackClassNameByQuestionType($question_type); 5502 $question->object->feedbackOBJ = new $feedbackObjectClassname($question->object, $ilCtrl, $ilDB, $lng); 5503 5504 $assSettings = new ilSetting('assessment'); 5505 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionProcessLockerFactory.php'; 5506 $processLockerFactory = new ilAssQuestionProcessLockerFactory($assSettings, $ilDB); 5507 $processLockerFactory->setQuestionId($question->object->getId()); 5508 $processLockerFactory->setUserId($ilUser->getId()); 5509 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php"); 5510 $processLockerFactory->setAssessmentLogEnabled(ilObjAssessmentFolder::_enabledAssessmentLogging()); 5511 $question->object->setProcessLocker($processLockerFactory->getLocker()); 5512 } 5513 5514 return $question; 5515 } 5516 5517 /** 5518 * Creates an instance of a question with a given question id 5519 * 5520 * @param integer $question_id The question id 5521 * @return object The question instance 5522 * @access public 5523 * 5524 * @deprecated use assQuestion::_instanciateQuestion($question_id) instead 5525 */ 5526 public static function _instanciateQuestion($question_id) 5527 { 5528 if (strcmp($question_id, "") != 0) { 5529 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 5530 return assQuestion::_instanciateQuestion($question_id); 5531 } 5532 } 5533 5534 /** 5535 * Move questions to another position 5536 * 5537 * @param array $move_questions An array with the question id's of the questions to move 5538 * @param integer $target_index The question id of the target position 5539 * @param integer $insert_mode 0, if insert before the target position, 1 if insert after the target position 5540 * @access public 5541 */ 5542 public function moveQuestions($move_questions, $target_index, $insert_mode) 5543 { 5544 $this->questions = array_values($this->questions); 5545 $array_pos = array_search($target_index, $this->questions); 5546 if ($insert_mode == 0) { 5547 $part1 = array_slice($this->questions, 0, $array_pos); 5548 $part2 = array_slice($this->questions, $array_pos); 5549 } elseif ($insert_mode == 1) { 5550 $part1 = array_slice($this->questions, 0, $array_pos + 1); 5551 $part2 = array_slice($this->questions, $array_pos + 1); 5552 } 5553 foreach ($move_questions as $question_id) { 5554 if (!(array_search($question_id, $part1) === false)) { 5555 unset($part1[array_search($question_id, $part1)]); 5556 } 5557 if (!(array_search($question_id, $part2) === false)) { 5558 unset($part2[array_search($question_id, $part2)]); 5559 } 5560 } 5561 $part1 = array_values($part1); 5562 $part2 = array_values($part2); 5563 $new_array = array_values(array_merge($part1, $move_questions, $part2)); 5564 $this->questions = array(); 5565 $counter = 1; 5566 foreach ($new_array as $question_id) { 5567 $this->questions[$counter] = $question_id; 5568 $counter++; 5569 } 5570 $this->saveQuestionsToDb(); 5571 } 5572 5573 5574 /** 5575 * Returns true if the starting time of a test is reached 5576 * A starting time is not available for self assessment tests 5577 * 5578 * @return boolean true if the starting time is reached, otherwise false 5579 * @access public 5580 */ 5581 public function startingTimeReached() 5582 { 5583 if ($this->isStartingTimeEnabled() && $this->getStartingTime() != 0) { 5584 $now = time(); 5585 if ($now < $this->getStartingTime()) { 5586 return false; 5587 } 5588 } 5589 return true; 5590 } 5591 5592 /** 5593 * Returns true if the ending time of a test is reached 5594 * An ending time is not available for self assessment tests 5595 * 5596 * @return boolean true if the ending time is reached, otherwise false 5597 * @access public 5598 */ 5599 public function endingTimeReached() 5600 { 5601 if ($this->isEndingTimeEnabled() && $this->getEndingTime() != 0) { 5602 $now = time(); 5603 if ($now > $this->getEndingTime()) { 5604 return true; 5605 } 5606 } 5607 return false; 5608 } 5609 5610 /** 5611 * Calculates the available questions for a test 5612 * 5613 * @access public 5614 */ 5615 public function getAvailableQuestions($arrFilter, $completeonly = 0) 5616 { 5617 global $DIC; 5618 $pluginAdmin = $DIC['ilPluginAdmin']; 5619 $lng = $DIC['lng']; 5620 $ilUser = $DIC['ilUser']; 5621 $ilDB = $DIC['ilDB']; 5622 5623 include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php"; 5624 $available_pools = array_keys(ilObjQuestionPool::_getAvailableQuestionpools($use_object_id = true, $equal_points = false, $could_be_offline = false, $showPath = false, $with_questioncount = false)); 5625 $available = ""; 5626 if (count($available_pools)) { 5627 $available = " AND " . $ilDB->in('qpl_questions.obj_fi', $available_pools, false, 'integer'); 5628 } else { 5629 return array(); 5630 } 5631 if ($completeonly) { 5632 $available .= " AND qpl_questions.complete = " . $ilDB->quote("1", 'text'); 5633 } 5634 5635 $where = ""; 5636 if (is_array($arrFilter)) { 5637 if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title'])) { 5638 $where .= " AND " . $ilDB->like('qpl_questions.title', 'text', "%%" . $arrFilter['title'] . "%%"); 5639 } 5640 if (array_key_exists('description', $arrFilter) && strlen($arrFilter['description'])) { 5641 $where .= " AND " . $ilDB->like('qpl_questions.description', 'text', "%%" . $arrFilter['description'] . "%%"); 5642 } 5643 if (array_key_exists('author', $arrFilter) && strlen($arrFilter['author'])) { 5644 $where .= " AND " . $ilDB->like('qpl_questions.author', 'text', "%%" . $arrFilter['author'] . "%%"); 5645 } 5646 if (array_key_exists('type', $arrFilter) && strlen($arrFilter['type'])) { 5647 $where .= " AND qpl_qst_type.type_tag = " . $ilDB->quote($arrFilter['type'], 'text'); 5648 } 5649 if (array_key_exists('qpl', $arrFilter) && strlen($arrFilter['qpl'])) { 5650 $where .= " AND " . $ilDB->like('object_data.title', 'text', "%%" . $arrFilter['qpl'] . "%%"); 5651 } 5652 } 5653 5654 $original_ids = &$this->getExistingQuestions(); 5655 $original_clause = " qpl_questions.original_id IS NULL"; 5656 if (count($original_ids)) { 5657 $original_clause = " qpl_questions.original_id IS NULL AND " . $ilDB->in('qpl_questions.question_id', $original_ids, true, 'integer'); 5658 } 5659 5660 $query_result = $ilDB->query(" 5661 SELECT qpl_questions.*, qpl_questions.tstamp, 5662 qpl_qst_type.type_tag, qpl_qst_type.plugin, qpl_qst_type.plugin_name, 5663 object_data.title parent_title 5664 FROM qpl_questions, qpl_qst_type, object_data 5665 WHERE $original_clause $available 5666 AND object_data.obj_id = qpl_questions.obj_fi 5667 AND qpl_questions.tstamp > 0 5668 AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id 5669 $where 5670 "); 5671 $rows = array(); 5672 $types = $this->getQuestionTypeTranslations(); 5673 if ($query_result->numRows()) { 5674 while ($row = $ilDB->fetchAssoc($query_result)) { 5675 $row = ilAssQuestionType::completeMissingPluginName($row); 5676 5677 if (!$row['plugin']) { 5678 $row[ 'ttype' ] = $lng->txt($row[ "type_tag" ]); 5679 5680 $rows[] = $row; 5681 continue; 5682 } 5683 5684 if (!$pluginAdmin->isActive(IL_COMP_MODULE, 'TestQuestionPool', 'qst', $row['plugin_name'])) { 5685 continue; 5686 } 5687 5688 $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, 'TestQuestionPool', 'qst', $row['plugin_name']); 5689 $row[ 'ttype' ] = $pl->getQuestionTypeTranslation(); 5690 5691 $rows[] = $row; 5692 } 5693 } 5694 return $rows; 5695 } 5696 5697 /** 5698 * Receives parameters from a QTI parser and creates a valid ILIAS test object 5699 * @param ilQTIAssessment $assessment 5700 */ 5701 public function fromXML(ilQTIAssessment $assessment) 5702 { 5703 unset($_SESSION["import_mob_xhtml"]); 5704 5705 $this->setDescription($assessment->getComment()); 5706 $this->setTitle($assessment->getTitle()); 5707 5708 $this->setIntroductionEnabled(false); 5709 foreach ($assessment->objectives as $objectives) { 5710 foreach ($objectives->materials as $material) { 5711 $intro = $this->QTIMaterialToString($material); 5712 $this->setIntroduction($intro); 5713 $this->setIntroductionEnabled(strlen($intro) > 0); 5714 } 5715 } 5716 5717 if ( 5718 $assessment->getPresentationMaterial() && 5719 $assessment->getPresentationMaterial()->getFlowMat(0) && 5720 $assessment->getPresentationMaterial()->getFlowMat(0)->getMaterial(0) 5721 ) { 5722 $this->setFinalStatement($this->QTIMaterialToString($assessment->getPresentationMaterial()->getFlowMat(0)->getMaterial(0))); 5723 } 5724 5725 foreach ($assessment->assessmentcontrol as $assessmentcontrol) { 5726 switch ($assessmentcontrol->getSolutionswitch()) { 5727 case "Yes": 5728 $this->setInstantFeedbackSolution(1); 5729 break; 5730 default: 5731 $this->setInstantFeedbackSolution(0); 5732 break; 5733 } 5734 } 5735 5736 $this->setStartingTimeEnabled(false); 5737 $this->setEndingTimeEnabled(false); 5738 $this->setPasswordEnabled(false); 5739 $this->setLimitUsersEnabled(false); 5740 5741 foreach ($assessment->qtimetadata as $metadata) { 5742 switch ($metadata["label"]) { 5743 case "test_type": 5744 // for old tests with a test type 5745 $type = $metadata["entry"]; 5746 switch ($type) { 5747 case 1: 5748 // assessment 5749 $this->setAnonymity(1); 5750 break; 5751 case 2: 5752 // self assessment 5753 break; 5754 case 4: 5755 // online exam 5756 $this->setFixedParticipants(1); 5757 $this->setListOfQuestionsSettings(7); 5758 $this->setShowSolutionPrintview(1); 5759 break; 5760 case 5: 5761 // varying random test 5762 break; 5763 } 5764 break; 5765 case "sequence_settings": 5766 $this->setSequenceSettings($metadata["entry"]); 5767 break; 5768 case "solution_details": 5769 $this->setShowSolutionDetails((int) $metadata["entry"]); 5770 break; 5771 case "print_bs_with_res": 5772 $this->setPrintBestSolutionWithResult((int) $metadata["entry"]); 5773 break; 5774 case "author": 5775 $this->setAuthor($metadata["entry"]); 5776 break; 5777 case "nr_of_tries": 5778 $this->setNrOfTries($metadata["entry"]); 5779 break; 5780 case 'block_after_passed': 5781 $this->setBlockPassesAfterPassedEnabled((bool) $metadata['entry']); 5782 break; 5783 case "pass_waiting": 5784 $this->setPassWaiting($metadata["entry"]); 5785 break; 5786 case "kiosk": 5787 $this->setKiosk($metadata["entry"]); 5788 break; 5789 case "showfinalstatement": 5790 $this->setShowFinalStatement($metadata["entry"]); 5791 break; 5792 case "showinfo": 5793 $this->setShowInfo($metadata["entry"]); 5794 break; 5795 case "forcejs": 5796 $this->setForceJS($metadata["entry"]); 5797 break; 5798 case "customstyle": 5799 $this->setCustomStyle($metadata["entry"]); 5800 break; 5801 5802 case "highscore_enabled": 5803 $this->setHighscoreEnabled($metadata["entry"]); 5804 break; 5805 5806 case "highscore_anon": 5807 $this->setHighscoreAnon($metadata["entry"]); 5808 break; 5809 5810 case "highscore_achieved_ts": 5811 $this->setHighscoreAchievedTS($metadata["entry"]); 5812 break; 5813 5814 case "highscore_score": 5815 $this->setHighscoreScore($metadata["entry"]); 5816 break; 5817 5818 case "highscore_percentage": 5819 $this->setHighscorePercentage($metadata["entry"]); 5820 break; 5821 5822 case "highscore_hints": 5823 $this->setHighscoreHints($metadata["entry"]); 5824 break; 5825 5826 case "highscore_wtime": 5827 $this->setHighscoreWTime($metadata["entry"]); 5828 break; 5829 5830 case "highscore_own_table": 5831 $this->setHighscoreOwnTable($metadata["entry"]); 5832 break; 5833 5834 case "highscore_top_table": 5835 $this->setHighscoreTopTable($metadata["entry"]); 5836 break; 5837 5838 case "highscore_top_num": 5839 $this->setHighscoreTopNum($metadata["entry"]); 5840 break; 5841 5842 case "hide_previous_results": 5843 if ($metadata["entry"] == 0) { 5844 $this->setUsePreviousAnswers(1); 5845 } else { 5846 $this->setUsePreviousAnswers(0); 5847 } 5848 break; 5849 case "use_previous_answers": 5850 $this->setUsePreviousAnswers($metadata["entry"]); 5851 break; 5852 case "answer_feedback": 5853 $this->setAnswerFeedback($metadata["entry"]); 5854 break; 5855 case "hide_title_points": 5856 $this->setTitleOutput($metadata["entry"]); 5857 break; 5858 case "title_output": 5859 $this->setTitleOutput($metadata["entry"]); 5860 break; 5861 case "question_set_type": 5862 $this->setQuestionSetType($metadata["entry"]); 5863 break; 5864 case "random_test": 5865 if ($metadata["entry"]) { 5866 $this->setQuestionSetType(self::QUESTION_SET_TYPE_RANDOM); 5867 } else { 5868 $this->setQuestionSetType(self::QUESTION_SET_TYPE_FIXED); 5869 } 5870 break; 5871 case "results_presentation": 5872 $this->setResultsPresentation($metadata["entry"]); 5873 break; 5874 case "reset_processing_time": 5875 $this->setResetProcessingTime($metadata["entry"]); 5876 break; 5877 case "instant_verification": 5878 $this->setInstantFeedbackSolution($metadata["entry"]); 5879 break; 5880 case "follow_qst_answer_fixation": 5881 $this->setFollowupQuestionAnswerFixationEnabled((bool) $metadata["entry"]); 5882 break; 5883 case "instant_feedback_answer_fixation": 5884 $this->setInstantFeedbackAnswerFixationEnabled((bool) $metadata["entry"]); 5885 break; 5886 case "force_instant_feedback": 5887 $this->setForceInstantFeedbackEnabled((bool) $metadata["entry"]); 5888 break; 5889 case "answer_feedback_points": 5890 $this->setAnswerFeedbackPoints($metadata["entry"]); 5891 break; 5892 case "anonymity": 5893 $this->setAnonymity($metadata["entry"]); 5894 break; 5895 case "use_pool": 5896 $this->setPoolUsage((int) $metadata["entry"]); 5897 break; 5898 case "show_cancel": 5899 $this->setShowCancel($metadata["entry"]); 5900 break; 5901 case "show_marker": 5902 $this->setShowMarker($metadata["entry"]); 5903 break; 5904 case "fixed_participants": 5905 $this->setFixedParticipants($metadata["entry"]); 5906 break; 5907 case "score_reporting": 5908 $this->setScoreReporting($metadata["entry"]); 5909 break; 5910 case "shuffle_questions": 5911 $this->setShuffleQuestions($metadata["entry"]); 5912 break; 5913 case "count_system": 5914 $this->setCountSystem($metadata["entry"]); 5915 break; 5916 case "mc_scoring": 5917 $this->setMCScoring($metadata["entry"]); 5918 break; 5919 case "mailnotification": 5920 $this->setMailNotification($metadata["entry"]); 5921 break; 5922 case "mailnottype": 5923 $this->setMailNotificationType($metadata["entry"]); 5924 break; 5925 case "exportsettings": 5926 $this->setExportSettings($metadata['entry']); 5927 break; 5928 case "score_cutting": 5929 $this->setScoreCutting($metadata["entry"]); 5930 break; 5931 case "password": 5932 $this->setPassword($metadata["entry"]); 5933 $this->setPasswordEnabled(strlen($metadata["entry"]) > 0); 5934 break; 5935 case "allowedUsers": 5936 $this->setAllowedUsers($metadata["entry"]); 5937 $this->setLimitUsersEnabled((int) $metadata["entry"] > 0); 5938 break; 5939 case "allowedUsersTimeGap": 5940 $this->setAllowedUsersTimeGap($metadata["entry"]); 5941 break; 5942 case "pass_scoring": 5943 $this->setPassScoring($metadata["entry"]); 5944 break; 5945 case 'pass_deletion_allowed': 5946 $this->setPassDeletionAllowed((int) $metadata['entry']); 5947 break; 5948 case "show_summary": 5949 $this->setListOfQuestionsSettings($metadata["entry"]); 5950 break; 5951 case "reporting_date": 5952 $iso8601period = $metadata["entry"]; 5953 if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches)) { 5954 $this->setReportingDate(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6])); 5955 } 5956 break; 5957 case 'enable_processing_time': 5958 $this->setEnableProcessingTime($metadata['entry']); 5959 break; 5960 case "processing_time": 5961 $this->setProcessingTime($metadata['entry']); 5962 break; 5963 case "starting_time": 5964 $iso8601period = $metadata["entry"]; 5965 if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches)) { 5966 $date_time = new ilDateTime(sprintf("%02d-%02d-%02d %02d:%02d:%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]), IL_CAL_DATETIME); 5967 $this->setStartingTime($date_time->get(IL_CAL_UNIX)); 5968 $this->setStartingTimeEnabled(true); 5969 } 5970 break; 5971 case "ending_time": 5972 $iso8601period = $metadata["entry"]; 5973 if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches)) { 5974 $date_time = new ilDateTime(sprintf("%02d-%02d-%02d %02d:%02d:%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]), IL_CAL_DATETIME); 5975 $this->setEndingTime($date_time->get(IL_CAL_UNIX)); 5976 $this->setEndingTimeEnabled(true); 5977 } 5978 break; 5979 case "enable_examview": 5980 $this->setEnableExamview($metadata["entry"]); 5981 break; 5982 case 'show_examview_html': 5983 $this->setShowExamviewHtml($metadata['entry']); 5984 break; 5985 case 'show_examview_pdf': 5986 $this->setShowExamviewPdf($metadata['entry']); 5987 break; 5988 case 'redirection_mode': 5989 $this->setRedirectionMode($metadata['entry']); 5990 break; 5991 case 'redirection_url': 5992 $this->setRedirectionUrl($metadata['entry']); 5993 break; 5994 case 'examid_in_kiosk': 5995 case 'examid_in_test_pass': 5996 $this->setShowExamIdInTestPassEnabled($metadata['entry']); 5997 break; 5998 case 'show_exam_id': 5999 case 'examid_in_test_res': 6000 $this->setShowExamIdInTestResultsEnabled($metadata['entry']); 6001 break; 6002 case 'enable_archiving': 6003 $this->setEnableArchiving($metadata['entry']); 6004 break; 6005 case 'sign_submission': 6006 $this->setSignSubmission($metadata['entry']); 6007 break; 6008 case 'char_selector_availability': 6009 $this->setCharSelectorAvailability($metadata['entry']); 6010 break; 6011 case 'char_selector_definition': 6012 $this->setCharSelectorDefinition($metadata['entry']); 6013 break; 6014 case 'skill_service': 6015 $this->setSkillServiceEnabled((bool) $metadata['entry']); 6016 break; 6017 case 'result_tax_filters': 6018 $this->setResultFilterTaxIds(strlen($metadata['entry']) ? unserialize($metadata['entry']) : array()); 6019 break; 6020 case 'show_grading_status': 6021 $this->setShowGradingStatusEnabled((bool) $metadata['entry']); 6022 break; 6023 case 'show_grading_mark': 6024 $this->setShowGradingMarkEnabled((bool) $metadata['entry']); 6025 break; 6026 case 'activation_limited': 6027 $this->setActivationLimited($metadata['entry']); 6028 break; 6029 case 'activation_start_time': 6030 $this->setActivationStartingTime($metadata['entry']); 6031 break; 6032 case 'activation_end_time': 6033 $this->setActivationEndingTime($metadata['entry']); 6034 break; 6035 case 'activation_visibility': 6036 $this->setActivationVisibility($metadata['entry']); 6037 break; 6038 case 'autosave': 6039 $this->setAutosave($metadata['entry']); 6040 break; 6041 case 'autosave_ival': 6042 $this->setAutosaveIval($metadata['entry']); 6043 break; 6044 case 'offer_question_hints': 6045 $this->setOfferingQuestionHintsEnabled($metadata['entry']); 6046 break; 6047 case 'instant_feedback_specific': 6048 $this->setSpecificAnswerFeedback($metadata['entry']); 6049 break; 6050 case 'obligations_enabled': 6051 $this->setObligationsEnabled($metadata['entry']); 6052 break; 6053 } 6054 if (preg_match("/mark_step_\d+/", $metadata["label"])) { 6055 $xmlmark = $metadata["entry"]; 6056 preg_match("/<short>(.*?)<\/short>/", $xmlmark, $matches); 6057 $mark_short = $matches[1]; 6058 preg_match("/<official>(.*?)<\/official>/", $xmlmark, $matches); 6059 $mark_official = $matches[1]; 6060 preg_match("/<percentage>(.*?)<\/percentage>/", $xmlmark, $matches); 6061 $mark_percentage = $matches[1]; 6062 preg_match("/<passed>(.*?)<\/passed>/", $xmlmark, $matches); 6063 $mark_passed = $matches[1]; 6064 $this->mark_schema->addMarkStep($mark_short, $mark_official, $mark_percentage, $mark_passed); 6065 } 6066 } 6067 // handle the import of media objects in XHTML code 6068 if (is_array($_SESSION["import_mob_xhtml"])) { 6069 include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php"; 6070 include_once "./Services/RTE/classes/class.ilRTE.php"; 6071 include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php"; 6072 foreach ($_SESSION["import_mob_xhtml"] as $mob) { 6073 $importfile = ilObjTest::_getImportDirectory() . '/' . $_SESSION["tst_import_subdir"] . '/' . $mob["uri"]; 6074 if (file_exists($importfile)) { 6075 $media_object = &ilObjMediaObject::_saveTempFileAsMediaObject(basename($importfile), $importfile, false); 6076 ilObjMediaObject::_saveUsage($media_object->getId(), "tst:html", $this->getId()); 6077 $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getIntroduction()), 1)); 6078 $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getFinalStatement()), 1)); 6079 } else { 6080 global $DIC; 6081 $ilLog = $DIC['ilLog']; 6082 $ilLog->write("Error: Could not open XHTML mob file for test introduction during test import. File $importfile does not exist!"); 6083 } 6084 } 6085 $this->saveToDb(); 6086 } 6087 } 6088 6089 /** 6090 * Returns a QTI xml representation of the test 6091 * 6092 * @return string The QTI xml representation of the test 6093 */ 6094 public function toXML() 6095 { 6096 include_once("./Services/Xml/classes/class.ilXmlWriter.php"); 6097 $a_xml_writer = new ilXmlWriter; 6098 // set xml header 6099 $a_xml_writer->xmlHeader(); 6100 $a_xml_writer->xmlSetDtdDef("<!DOCTYPE questestinterop SYSTEM \"ims_qtiasiv1p2p1.dtd\">"); 6101 $a_xml_writer->xmlStartTag("questestinterop"); 6102 6103 $attrs = array( 6104 "ident" => "il_" . IL_INST_ID . "_tst_" . $this->getTestId(), 6105 "title" => $this->getTitle() 6106 ); 6107 $a_xml_writer->xmlStartTag("assessment", $attrs); 6108 // add qti comment 6109 $a_xml_writer->xmlElement("qticomment", null, $this->getDescription()); 6110 6111 // add qti duration 6112 if ($this->enable_processing_time) { 6113 preg_match("/(\d+):(\d+):(\d+)/", $this->processing_time, $matches); 6114 $a_xml_writer->xmlElement("duration", null, sprintf("P0Y0M0DT%dH%dM%dS", $matches[1], $matches[2], $matches[3])); 6115 } 6116 6117 // add the rest of the preferences in qtimetadata tags, because there is no correspondent definition in QTI 6118 $a_xml_writer->xmlStartTag("qtimetadata"); 6119 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6120 $a_xml_writer->xmlElement("fieldlabel", null, "ILIAS_VERSION"); 6121 $a_xml_writer->xmlElement("fieldentry", null, $this->ilias->getSetting("ilias_version")); 6122 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6123 6124 // anonymity 6125 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6126 $a_xml_writer->xmlElement("fieldlabel", null, "anonymity"); 6127 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getAnonymity())); 6128 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6129 6130 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6131 $a_xml_writer->xmlElement("fieldlabel", null, "use_pool"); 6132 $a_xml_writer->xmlElement("fieldentry", null, $this->getPoolUsage() ? 1 : 0); 6133 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6134 6135 // question set type (fixed, random, dynamic, ...) 6136 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6137 $a_xml_writer->xmlElement("fieldlabel", null, "question_set_type"); 6138 $a_xml_writer->xmlElement("fieldentry", null, $this->getQuestionSetType()); 6139 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6140 6141 // sequence settings 6142 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6143 $a_xml_writer->xmlElement("fieldlabel", null, "sequence_settings"); 6144 $a_xml_writer->xmlElement("fieldentry", null, $this->getSequenceSettings()); 6145 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6146 6147 // author 6148 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6149 $a_xml_writer->xmlElement("fieldlabel", null, "author"); 6150 $a_xml_writer->xmlElement("fieldentry", null, $this->getAuthor()); 6151 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6152 6153 // reset processing time 6154 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6155 $a_xml_writer->xmlElement("fieldlabel", null, "reset_processing_time"); 6156 $a_xml_writer->xmlElement("fieldentry", null, $this->getResetProcessingTime()); 6157 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6158 6159 // count system 6160 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6161 $a_xml_writer->xmlElement("fieldlabel", null, "count_system"); 6162 $a_xml_writer->xmlElement("fieldentry", null, $this->getCountSystem()); 6163 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6164 6165 // multiple choice scoring 6166 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6167 $a_xml_writer->xmlElement("fieldlabel", null, "mc_scoring"); 6168 $a_xml_writer->xmlElement("fieldentry", null, $this->getMCScoring()); 6169 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6170 6171 // multiple choice scoring 6172 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6173 $a_xml_writer->xmlElement("fieldlabel", null, "score_cutting"); 6174 $a_xml_writer->xmlElement("fieldentry", null, $this->getScoreCutting()); 6175 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6176 6177 // multiple choice scoring 6178 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6179 $a_xml_writer->xmlElement("fieldlabel", null, "password"); 6180 $a_xml_writer->xmlElement("fieldentry", null, $this->getPassword()); 6181 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6182 6183 // allowed users 6184 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6185 $a_xml_writer->xmlElement("fieldlabel", null, "allowedUsers"); 6186 $a_xml_writer->xmlElement("fieldentry", null, $this->getAllowedUsers()); 6187 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6188 6189 // allowed users time gap 6190 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6191 $a_xml_writer->xmlElement("fieldlabel", null, "allowedUsersTimeGap"); 6192 $a_xml_writer->xmlElement("fieldentry", null, $this->getAllowedUsersTimeGap()); 6193 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6194 6195 // pass scoring 6196 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6197 $a_xml_writer->xmlElement("fieldlabel", null, "pass_scoring"); 6198 $a_xml_writer->xmlElement("fieldentry", null, $this->getPassScoring()); 6199 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6200 6201 $a_xml_writer->xmlStartTag('qtimetadatafield'); 6202 $a_xml_writer->xmlElement('fieldlabel', null, 'pass_deletion_allowed'); 6203 $a_xml_writer->xmlElement('fieldentry', null, (int) $this->isPassDeletionAllowed()); 6204 $a_xml_writer->xmlEndTag('qtimetadatafield'); 6205 6206 // score reporting date 6207 if ($this->getReportingDate()) { 6208 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6209 $a_xml_writer->xmlElement("fieldlabel", null, "reporting_date"); 6210 preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->reporting_date, $matches); 6211 $a_xml_writer->xmlElement("fieldentry", null, sprintf("P%dY%dM%dDT%dH%dM%dS", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6])); 6212 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6213 } 6214 // number of tries 6215 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6216 $a_xml_writer->xmlElement("fieldlabel", null, "nr_of_tries"); 6217 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getNrOfTries())); 6218 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6219 6220 // number of tries 6221 $a_xml_writer->xmlStartTag('qtimetadatafield'); 6222 $a_xml_writer->xmlElement('fieldlabel', null, 'block_after_passed'); 6223 $a_xml_writer->xmlElement('fieldentry', null, (int) $this->isBlockPassesAfterPassedEnabled()); 6224 $a_xml_writer->xmlEndTag('qtimetadatafield'); 6225 6226 // pass_waiting 6227 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6228 $a_xml_writer->xmlElement("fieldlabel", null, "pass_waiting"); 6229 $a_xml_writer->xmlElement("fieldentry", null, $this->getPassWaiting()); 6230 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6231 6232 // kiosk 6233 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6234 $a_xml_writer->xmlElement("fieldlabel", null, "kiosk"); 6235 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getKiosk())); 6236 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6237 6238 6239 //redirection_mode 6240 $a_xml_writer->xmlStartTag('qtimetadatafield'); 6241 $a_xml_writer->xmlElement("fieldlabel", null, "redirection_mode"); 6242 $a_xml_writer->xmlElement("fieldentry", null, $this->getRedirectionMode()); 6243 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6244 6245 //redirection_url 6246 $a_xml_writer->xmlStartTag('qtimetadatafield'); 6247 $a_xml_writer->xmlElement("fieldlabel", null, "redirection_url"); 6248 $a_xml_writer->xmlElement("fieldentry", null, $this->getRedirectionUrl()); 6249 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6250 6251 // use previous answers 6252 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6253 $a_xml_writer->xmlElement("fieldlabel", null, "use_previous_answers"); 6254 $a_xml_writer->xmlElement("fieldentry", null, $this->getUsePreviousAnswers()); 6255 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6256 6257 // hide title points 6258 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6259 $a_xml_writer->xmlElement("fieldlabel", null, "title_output"); 6260 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getTitleOutput())); 6261 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6262 6263 // results presentation 6264 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6265 $a_xml_writer->xmlElement("fieldlabel", null, "results_presentation"); 6266 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getResultsPresentation())); 6267 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6268 6269 // examid in test pass 6270 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6271 $a_xml_writer->xmlElement("fieldlabel", null, "examid_in_test_pass"); 6272 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->isShowExamIdInTestPassEnabled())); 6273 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6274 6275 // examid in kiosk 6276 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6277 $a_xml_writer->xmlElement("fieldlabel", null, "examid_in_test_res"); 6278 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->isShowExamIdInTestResultsEnabled())); 6279 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6280 6281 // solution details 6282 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6283 $a_xml_writer->xmlElement("fieldlabel", null, "show_summary"); 6284 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getListOfQuestionsSettings())); 6285 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6286 6287 // solution details 6288 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6289 $a_xml_writer->xmlElement("fieldlabel", null, "score_reporting"); 6290 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getScoreReporting())); 6291 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6292 6293 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6294 $a_xml_writer->xmlElement("fieldlabel", null, "solution_details"); 6295 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getShowSolutionDetails()); 6296 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6297 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6298 $a_xml_writer->xmlElement("fieldlabel", null, "print_bs_with_res"); 6299 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getShowSolutionDetails() ? (int) $this->isBestSolutionPrintedWithResult() : 0); 6300 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6301 6302 // solution details 6303 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6304 $a_xml_writer->xmlElement("fieldlabel", null, "instant_verification"); 6305 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getInstantFeedbackSolution())); 6306 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6307 6308 // answer specific feedback 6309 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6310 $a_xml_writer->xmlElement("fieldlabel", null, "answer_feedback"); 6311 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getAnswerFeedback())); 6312 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6313 6314 // answer specific feedback of reached points 6315 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6316 $a_xml_writer->xmlElement("fieldlabel", null, "answer_feedback_points"); 6317 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getAnswerFeedbackPoints())); 6318 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6319 6320 // followup question previous answer freezing 6321 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6322 $a_xml_writer->xmlElement("fieldlabel", null, "follow_qst_answer_fixation"); 6323 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->isFollowupQuestionAnswerFixationEnabled()); 6324 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6325 6326 // instant response answer freezing 6327 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6328 $a_xml_writer->xmlElement("fieldlabel", null, "instant_feedback_answer_fixation"); 6329 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->isInstantFeedbackAnswerFixationEnabled()); 6330 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6331 6332 // instant response forced 6333 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6334 $a_xml_writer->xmlElement("fieldlabel", null, "force_instant_feedback"); 6335 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->isForceInstantFeedbackEnabled()); 6336 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6337 6338 6339 // highscore 6340 $highscore_metadata = array( 6341 'highscore_enabled' => array('value' => $this->getHighscoreEnabled()), 6342 'highscore_anon' => array('value' => $this->getHighscoreAnon()), 6343 'highscore_achieved_ts' => array('value' => $this->getHighscoreAchievedTS()), 6344 'highscore_score' => array('value' => $this->getHighscoreScore()), 6345 'highscore_percentage' => array('value' => $this->getHighscorePercentage()), 6346 'highscore_hints' => array('value' => $this->getHighscoreHints()), 6347 'highscore_wtime' => array('value' => $this->getHighscoreWTime()), 6348 'highscore_own_table' => array('value' => $this->getHighscoreOwnTable()), 6349 'highscore_top_table' => array('value' => $this->getHighscoreTopTable()), 6350 'highscore_top_num' => array('value' => $this->getHighscoreTopNum()), 6351 ); 6352 foreach ($highscore_metadata as $label => $data) { 6353 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6354 $a_xml_writer->xmlElement("fieldlabel", null, $label); 6355 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $data['value'])); 6356 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6357 } 6358 6359 // show cancel 6360 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6361 $a_xml_writer->xmlElement("fieldlabel", null, "show_cancel"); 6362 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getShowCancel())); 6363 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6364 6365 // show marker 6366 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6367 $a_xml_writer->xmlElement("fieldlabel", null, "show_marker"); 6368 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getShowMarker())); 6369 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6370 6371 // fixed participants 6372 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6373 $a_xml_writer->xmlElement("fieldlabel", null, "fixed_participants"); 6374 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getFixedParticipants())); 6375 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6376 6377 // show final statement 6378 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6379 $a_xml_writer->xmlElement("fieldlabel", null, "showfinalstatement"); 6380 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", (($this->getShowFinalStatement()) ? "1" : "0"))); 6381 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6382 6383 // show introduction only 6384 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6385 $a_xml_writer->xmlElement("fieldlabel", null, "showinfo"); 6386 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", (($this->getShowInfo()) ? "1" : "0"))); 6387 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6388 6389 // mail notification 6390 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6391 $a_xml_writer->xmlElement("fieldlabel", null, "mailnotification"); 6392 $a_xml_writer->xmlElement("fieldentry", null, $this->getMailNotification()); 6393 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6394 6395 // mail notification type 6396 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6397 $a_xml_writer->xmlElement("fieldlabel", null, "mailnottype"); 6398 $a_xml_writer->xmlElement("fieldentry", null, $this->getMailNotificationType()); 6399 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6400 6401 // export settings 6402 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6403 $a_xml_writer->xmlElement("fieldlabel", null, "exportsettings"); 6404 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getExportSettings()); 6405 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6406 6407 // force JavaScript 6408 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6409 $a_xml_writer->xmlElement("fieldlabel", null, "forcejs"); 6410 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", (($this->getForceJS()) ? "1" : "0"))); 6411 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6412 6413 // custom style 6414 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6415 $a_xml_writer->xmlElement("fieldlabel", null, "customstyle"); 6416 $a_xml_writer->xmlElement("fieldentry", null, $this->getCustomStyle()); 6417 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6418 6419 // shuffle questions 6420 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6421 $a_xml_writer->xmlElement("fieldlabel", null, "shuffle_questions"); 6422 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getShuffleQuestions())); 6423 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6424 6425 // processing time 6426 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6427 $a_xml_writer->xmlElement("fieldlabel", null, "processing_time"); 6428 $a_xml_writer->xmlElement("fieldentry", null, $this->getProcessingTime()); 6429 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6430 6431 // enable_examview 6432 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6433 $a_xml_writer->xmlElement("fieldlabel", null, "enable_examview"); 6434 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getEnableExamview()); 6435 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6436 6437 // show_examview_html 6438 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6439 $a_xml_writer->xmlElement("fieldlabel", null, "show_examview_html"); 6440 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getShowExamviewHtml()); 6441 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6442 6443 // show_examview_pdf 6444 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6445 $a_xml_writer->xmlElement("fieldlabel", null, "show_examview_pdf"); 6446 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getShowExamviewPdf()); 6447 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6448 6449 // enable_archiving 6450 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6451 $a_xml_writer->xmlElement("fieldlabel", null, "enable_archiving"); 6452 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getEnableArchiving()); 6453 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6454 6455 // sign_submission 6456 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6457 $a_xml_writer->xmlElement("fieldlabel", null, "sign_submission"); 6458 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getSignSubmission()); 6459 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6460 6461 // char_selector_availability 6462 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6463 $a_xml_writer->xmlElement("fieldlabel", null, "char_selector_availability"); 6464 $a_xml_writer->xmlElement("fieldentry", null, sprintf("%d", $this->getCharSelectorAvailability())); 6465 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6466 6467 // char_selector_definition 6468 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6469 $a_xml_writer->xmlElement("fieldlabel", null, "char_selector_definition"); 6470 $a_xml_writer->xmlElement("fieldentry", null, $this->getCharSelectorDefinition()); 6471 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6472 6473 // skill_service 6474 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6475 $a_xml_writer->xmlElement("fieldlabel", null, "skill_service"); 6476 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->isSkillServiceEnabled()); 6477 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6478 6479 // result_tax_filters 6480 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6481 $a_xml_writer->xmlElement("fieldlabel", null, "result_tax_filters"); 6482 $a_xml_writer->xmlElement("fieldentry", null, serialize((array) $this->getResultFilterTaxIds())); 6483 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6484 6485 // show_grading_status 6486 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6487 $a_xml_writer->xmlElement("fieldlabel", null, "show_grading_status"); 6488 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->isShowGradingStatusEnabled()); 6489 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6490 6491 // show_grading_mark 6492 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6493 $a_xml_writer->xmlElement("fieldlabel", null, "show_grading_mark"); 6494 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->isShowGradingMarkEnabled()); 6495 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6496 6497 6498 // starting time 6499 if ($this->getStartingTime()) { 6500 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6501 $a_xml_writer->xmlElement("fieldlabel", null, "starting_time"); 6502 $backward_compatibility_format = $this->buildIso8601PeriodFromUnixtimeForExportCompatibility($this->starting_time); 6503 $a_xml_writer->xmlElement("fieldentry", null, $backward_compatibility_format); 6504 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6505 } 6506 // ending time 6507 if ($this->getEndingTime()) { 6508 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6509 $a_xml_writer->xmlElement("fieldlabel", null, "ending_time"); 6510 $backward_compatibility_format = $this->buildIso8601PeriodFromUnixtimeForExportCompatibility($this->ending_time); 6511 $a_xml_writer->xmlElement("fieldentry", null, $backward_compatibility_format); 6512 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6513 } 6514 6515 6516 //activation_limited 6517 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6518 $a_xml_writer->xmlElement("fieldlabel", null, "activation_limited"); 6519 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->isActivationLimited()); 6520 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6521 6522 //activation_start_time 6523 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6524 $a_xml_writer->xmlElement("fieldlabel", null, "activation_start_time"); 6525 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getActivationStartingTime()); 6526 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6527 6528 //activation_end_time 6529 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6530 $a_xml_writer->xmlElement("fieldlabel", null, "activation_end_time"); 6531 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getActivationEndingTime()); 6532 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6533 6534 //activation_visibility 6535 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6536 $a_xml_writer->xmlElement("fieldlabel", null, "activation_visibility"); 6537 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getActivationVisibility()); 6538 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6539 6540 // autosave 6541 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6542 $a_xml_writer->xmlElement("fieldlabel", null, "autosave"); 6543 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getAutosave()); 6544 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6545 6546 // autosave_ival 6547 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6548 $a_xml_writer->xmlElement("fieldlabel", null, "autosave_ival"); 6549 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getAutosaveIval()); 6550 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6551 6552 //offer_question_hints 6553 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6554 $a_xml_writer->xmlElement("fieldlabel", null, "offer_question_hints"); 6555 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->isOfferingQuestionHintsEnabled()); 6556 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6557 6558 //instant_feedback_specific 6559 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6560 $a_xml_writer->xmlElement("fieldlabel", null, "instant_feedback_specific"); 6561 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getSpecificAnswerFeedback()); 6562 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6563 6564 //instant_feedback_answer_fixation 6565 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6566 $a_xml_writer->xmlElement("fieldlabel", null, "instant_feedback_answer_fixation"); 6567 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->isInstantFeedbackAnswerFixationEnabled()); 6568 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6569 6570 //obligations_enabled 6571 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6572 $a_xml_writer->xmlElement("fieldlabel", null, "obligations_enabled"); 6573 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->areObligationsEnabled()); 6574 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6575 6576 //enable_processing_time 6577 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6578 $a_xml_writer->xmlElement("fieldlabel", null, "enable_processing_time"); 6579 $a_xml_writer->xmlElement("fieldentry", null, (int) $this->getEnableProcessingTime()); 6580 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6581 6582 foreach ($this->mark_schema->mark_steps as $index => $mark) { 6583 // mark steps 6584 $a_xml_writer->xmlStartTag("qtimetadatafield"); 6585 $a_xml_writer->xmlElement("fieldlabel", null, "mark_step_$index"); 6586 $a_xml_writer->xmlElement("fieldentry", null, sprintf( 6587 "<short>%s</short><official>%s</official><percentage>%.2f</percentage><passed>%d</passed>", 6588 $mark->getShortName(), 6589 $mark->getOfficialName(), 6590 $mark->getMinimumLevel(), 6591 $mark->getPassed() 6592 )); 6593 $a_xml_writer->xmlEndTag("qtimetadatafield"); 6594 } 6595 $a_xml_writer->xmlEndTag("qtimetadata"); 6596 6597 // add qti objectives 6598 $a_xml_writer->xmlStartTag("objectives"); 6599 $this->addQTIMaterial($a_xml_writer, $this->getIntroduction()); 6600 $a_xml_writer->xmlEndTag("objectives"); 6601 6602 // add qti assessmentcontrol 6603 if ($this->getInstantFeedbackSolution() == 1) { 6604 $attrs = array( 6605 "solutionswitch" => "Yes" 6606 ); 6607 } else { 6608 $attrs = null; 6609 } 6610 $a_xml_writer->xmlElement("assessmentcontrol", $attrs, null); 6611 6612 if (strlen($this->getFinalStatement())) { 6613 // add qti presentation_material 6614 $a_xml_writer->xmlStartTag("presentation_material"); 6615 $a_xml_writer->xmlStartTag("flow_mat"); 6616 $this->addQTIMaterial($a_xml_writer, $this->getFinalStatement()); 6617 $a_xml_writer->xmlEndTag("flow_mat"); 6618 $a_xml_writer->xmlEndTag("presentation_material"); 6619 } 6620 6621 $attrs = array( 6622 "ident" => "1" 6623 ); 6624 $a_xml_writer->xmlElement("section", $attrs, null); 6625 $a_xml_writer->xmlEndTag("assessment"); 6626 $a_xml_writer->xmlEndTag("questestinterop"); 6627 6628 $xml = $a_xml_writer->xmlDumpMem(false); 6629 return $xml; 6630 } 6631 6632 /** 6633 * @param $unix_timestamp 6634 * @return string 6635 */ 6636 protected function buildIso8601PeriodFromUnixtimeForExportCompatibility($unix_timestamp) 6637 { 6638 $date_time_unix = new ilDateTime($unix_timestamp, IL_CAL_UNIX); 6639 $date_time = $date_time_unix->get(IL_CAL_DATETIME); 6640 preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $date_time, $matches); 6641 $iso8601_period = sprintf("P%dY%dM%dDT%dH%dM%dS", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]); 6642 return $iso8601_period; 6643 } 6644 6645 /** 6646 * export pages of test to xml (see ilias_co.dtd) 6647 * 6648 * @param object $a_xml_writer ilXmlWriter object that receives the 6649 * xml data 6650 */ 6651 public function exportPagesXML(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog) 6652 { 6653 global $DIC; 6654 $ilBench = $DIC['ilBench']; 6655 6656 $this->mob_ids = array(); 6657 $this->file_ids = array(); 6658 6659 // MetaData 6660 $this->exportXMLMetaData($a_xml_writer); 6661 6662 // PageObjects 6663 $expLog->write(date("[y-m-d H:i:s] ") . "Start Export Page Objects"); 6664 $ilBench->start("ContentObjectExport", "exportPageObjects"); 6665 $this->exportXMLPageObjects($a_xml_writer, $a_inst, $expLog); 6666 $ilBench->stop("ContentObjectExport", "exportPageObjects"); 6667 $expLog->write(date("[y-m-d H:i:s] ") . "Finished Export Page Objects"); 6668 6669 // MediaObjects 6670 $expLog->write(date("[y-m-d H:i:s] ") . "Start Export Media Objects"); 6671 $ilBench->start("ContentObjectExport", "exportMediaObjects"); 6672 $this->exportXMLMediaObjects($a_xml_writer, $a_inst, $a_target_dir, $expLog); 6673 $ilBench->stop("ContentObjectExport", "exportMediaObjects"); 6674 $expLog->write(date("[y-m-d H:i:s] ") . "Finished Export Media Objects"); 6675 6676 // FileItems 6677 $expLog->write(date("[y-m-d H:i:s] ") . "Start Export File Items"); 6678 $ilBench->start("ContentObjectExport", "exportFileItems"); 6679 $this->exportFileItems($a_target_dir, $expLog); 6680 $ilBench->stop("ContentObjectExport", "exportFileItems"); 6681 $expLog->write(date("[y-m-d H:i:s] ") . "Finished Export File Items"); 6682 } 6683 6684 /** 6685 * export content objects meta data to xml (see ilias_co.dtd) 6686 * 6687 * @param object $a_xml_writer ilXmlWriter object that receives the 6688 * xml data 6689 */ 6690 public function exportXMLMetaData(&$a_xml_writer) 6691 { 6692 include_once "./Services/MetaData/classes/class.ilMD2XML.php"; 6693 $md2xml = new ilMD2XML($this->getId(), 0, $this->getType()); 6694 $md2xml->setExportMode(true); 6695 $md2xml->startExport(); 6696 $a_xml_writer->appendXML($md2xml->getXML()); 6697 } 6698 6699 /** 6700 * Returns the installation id for a given identifier 6701 * 6702 * @access private 6703 */ 6704 public function modifyExportIdentifier($a_tag, $a_param, $a_value) 6705 { 6706 if ($a_tag == "Identifier" && $a_param == "Entry") { 6707 include_once "./Services/Utilities/classes/class.ilUtil.php"; 6708 $a_value = ilUtil::insertInstIntoID($a_value); 6709 } 6710 6711 return $a_value; 6712 } 6713 6714 6715 /** 6716 * export page objects to xml (see ilias_co.dtd) 6717 * 6718 * @param object $a_xml_writer ilXmlWriter object that receives the 6719 * xml data 6720 */ 6721 public function exportXMLPageObjects(&$a_xml_writer, $a_inst, &$expLog) 6722 { 6723 global $DIC; 6724 $ilBench = $DIC['ilBench']; 6725 6726 include_once "./Modules/LearningModule/classes/class.ilLMPageObject.php"; 6727 6728 foreach ($this->questions as $question_id) { 6729 $ilBench->start("ContentObjectExport", "exportPageObject"); 6730 $expLog->write(date("[y-m-d H:i:s] ") . "Page Object " . $question_id); 6731 6732 $attrs = array(); 6733 $a_xml_writer->xmlStartTag("PageObject", $attrs); 6734 6735 6736 // export xml to writer object 6737 $ilBench->start("ContentObjectExport", "exportPageObject_XML"); 6738 include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php"; 6739 $page_object = new ilAssQuestionPage($question_id); 6740 $page_object->buildDom(); 6741 $page_object->insertInstIntoIDs($a_inst); 6742 $mob_ids = $page_object->collectMediaObjects(false); 6743 require_once 'Services/COPage/classes/class.ilPCFileList.php'; 6744 $file_ids = ilPCFileList::collectFileItems($page_object, $page_object->getDomDoc()); 6745 $xml = $page_object->getXMLFromDom(false, false, false, "", true); 6746 $xml = str_replace("&", "&", $xml); 6747 $a_xml_writer->appendXML($xml); 6748 $page_object->freeDom(); 6749 unset($page_object); 6750 6751 $ilBench->stop("ContentObjectExport", "exportPageObject_XML"); 6752 6753 // collect media objects 6754 $ilBench->start("ContentObjectExport", "exportPageObject_CollectMedia"); 6755 //$mob_ids = $page_obj->getMediaObjectIDs(); 6756 foreach ($mob_ids as $mob_id) { 6757 $this->mob_ids[$mob_id] = $mob_id; 6758 } 6759 $ilBench->stop("ContentObjectExport", "exportPageObject_CollectMedia"); 6760 6761 // collect all file items 6762 $ilBench->start("ContentObjectExport", "exportPageObject_CollectFileItems"); 6763 //$file_ids = $page_obj->getFileItemIds(); 6764 foreach ($file_ids as $file_id) { 6765 $this->file_ids[$file_id] = $file_id; 6766 } 6767 $ilBench->stop("ContentObjectExport", "exportPageObject_CollectFileItems"); 6768 6769 $a_xml_writer->xmlEndTag("PageObject"); 6770 //unset($page_obj); 6771 6772 $ilBench->stop("ContentObjectExport", "exportPageObject"); 6773 } 6774 } 6775 6776 /** 6777 * export media objects to xml (see ilias_co.dtd) 6778 * 6779 * @param object $a_xml_writer ilXmlWriter object that receives the 6780 * xml data 6781 */ 6782 public function exportXMLMediaObjects(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog) 6783 { 6784 include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php"; 6785 6786 foreach ($this->mob_ids as $mob_id) { 6787 $expLog->write(date("[y-m-d H:i:s] ") . "Media Object " . $mob_id); 6788 if (ilObjMediaObject::_exists($mob_id)) { 6789 $media_obj = new ilObjMediaObject($mob_id); 6790 $media_obj->exportXML($a_xml_writer, $a_inst); 6791 $media_obj->exportFiles($a_target_dir); 6792 unset($media_obj); 6793 } 6794 } 6795 } 6796 6797 /** 6798 * export files of file itmes 6799 * 6800 */ 6801 public function exportFileItems($a_target_dir, &$expLog) 6802 { 6803 include_once "./Modules/File/classes/class.ilObjFile.php"; 6804 6805 foreach ($this->file_ids as $file_id) { 6806 $expLog->write(date("[y-m-d H:i:s] ") . "File Item " . $file_id); 6807 $file_obj = new ilObjFile($file_id, false); 6808 $file_obj->export($a_target_dir); 6809 unset($file_obj); 6810 } 6811 } 6812 6813 /** 6814 * get array of (two) new created questions for 6815 * import id 6816 */ 6817 public function getImportMapping() 6818 { 6819 if (!is_array($this->import_mapping)) { 6820 return array(); 6821 } else { 6822 return $this->import_mapping; 6823 } 6824 } 6825 6826 /** 6827 * {@inheritdoc} 6828 */ 6829 public function canEditEctsGrades() 6830 { 6831 return $this->canShowEctsGrades() && $this->canEditMarks(); 6832 } 6833 6834 /** 6835 * {@inheritdoc} 6836 */ 6837 public function canShowEctsGrades() 6838 { 6839 return $this->getReportingDate(); 6840 } 6841 6842 /** 6843 * {@inheritdoc} 6844 */ 6845 public function getECTSGrade($passed_array, $reached_points, $max_points) 6846 { 6847 return self::_getECTSGrade($passed_array, $reached_points, $max_points, $this->ects_grades["A"], $this->ects_grades["B"], $this->ects_grades["C"], $this->ects_grades["D"], $this->ects_grades["E"], $this->ects_fx); 6848 } 6849 6850 /** 6851 * {@inheritdoc} 6852 */ 6853 public static function _getECTSGrade($points_passed, $reached_points, $max_points, $a, $b, $c, $d, $e, $fx) 6854 { 6855 include_once "./Modules/Test/classes/class.ilStatistics.php"; 6856 // calculate the median 6857 $passed_statistics = new ilStatistics(); 6858 $passed_statistics->setData($points_passed); 6859 $ects_percentiles = array( 6860 "A" => $passed_statistics->quantile($a), 6861 "B" => $passed_statistics->quantile($b), 6862 "C" => $passed_statistics->quantile($c), 6863 "D" => $passed_statistics->quantile($d), 6864 "E" => $passed_statistics->quantile($e) 6865 ); 6866 if (count($points_passed) && ($reached_points >= $ects_percentiles["A"])) { 6867 return "A"; 6868 } elseif (count($points_passed) && ($reached_points >= $ects_percentiles["B"])) { 6869 return "B"; 6870 } elseif (count($points_passed) && ($reached_points >= $ects_percentiles["C"])) { 6871 return "C"; 6872 } elseif (count($points_passed) && ($reached_points >= $ects_percentiles["D"])) { 6873 return "D"; 6874 } elseif (count($points_passed) && ($reached_points >= $ects_percentiles["E"])) { 6875 return "E"; 6876 } elseif (strcmp($fx, "") != 0) { 6877 if ($max_points > 0) { 6878 $percentage = ($reached_points / $max_points) * 100.0; 6879 if ($percentage < 0) { 6880 $percentage = 0.0; 6881 } 6882 } else { 6883 $percentage = 0.0; 6884 } 6885 if ($percentage >= $fx) { 6886 return "FX"; 6887 } else { 6888 return "F"; 6889 } 6890 } else { 6891 return "F"; 6892 } 6893 } 6894 6895 /** 6896 * {@inheritdoc} 6897 */ 6898 public function checkMarks() 6899 { 6900 return $this->mark_schema->checkMarks(); 6901 } 6902 6903 /** 6904 * {@inheritdoc} 6905 */ 6906 public function getMarkSchema() 6907 { 6908 return $this->mark_schema; 6909 } 6910 6911 /** 6912 * {@inheritdoc} 6913 */ 6914 public function getMarkSchemaForeignId() 6915 { 6916 return $this->getTestId(); 6917 } 6918 6919 /** 6920 */ 6921 public function onMarkSchemaSaved() 6922 { 6923 /** 6924 * @var $tree ilTree 6925 * @var $ilDB ilDBInterface 6926 * @var $ilPluginAdmin ilPluginAdmin 6927 */ 6928 global $DIC; 6929 $ilDB = $DIC['ilDB']; 6930 $ilPluginAdmin = $DIC['ilPluginAdmin']; 6931 $tree = $DIC['tree']; 6932 6933 require_once 'Modules/Test/classes/class.ilTestQuestionSetConfigFactory.php'; 6934 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $ilPluginAdmin, $this); 6935 $this->saveCompleteStatus($testQuestionSetConfigFactory->getQuestionSetConfig()); 6936 6937 if ($this->participantDataExist()) { 6938 $this->recalculateScores(true); 6939 } 6940 } 6941 6942 /** 6943 * @return {@inheritdoc} 6944 */ 6945 public function canEditMarks() 6946 { 6947 $total = $this->evalTotalPersons(); 6948 if ($total > 0) { 6949 if ($this->getReportingDate()) { 6950 if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches)) { 6951 $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 6952 $now = time(); 6953 if ($now < $epoch_time) { 6954 return true; 6955 } 6956 } 6957 } 6958 return false; 6959 } else { 6960 return true; 6961 } 6962 } 6963 6964 /** 6965 * Sets the authors name of the ilObjTest object 6966 * 6967 * @param string $author A string containing the name of the test author 6968 * @access public 6969 * @see $author 6970 */ 6971 public function setAuthor($author = "") 6972 { 6973 $this->author = $author; 6974 } 6975 6976 /** 6977 * Saves an authors name into the lifecycle metadata if no lifecycle metadata exists 6978 * This will only be called for conversion of "old" tests where the author hasn't been 6979 * stored in the lifecycle metadata 6980 * 6981 * @param string $a_author A string containing the name of the test author 6982 * @access private 6983 * @see $author 6984 */ 6985 public function saveAuthorToMetadata($a_author = "") 6986 { 6987 $md = new ilMD($this->getId(), 0, $this->getType()); 6988 $md_life = &$md->getLifecycle(); 6989 if (!$md_life) { 6990 if (strlen($a_author) == 0) { 6991 global $DIC; 6992 $ilUser = $DIC['ilUser']; 6993 $a_author = $ilUser->getFullname(); 6994 } 6995 6996 $md_life = &$md->addLifecycle(); 6997 $md_life->save(); 6998 $con = &$md_life->addContribute(); 6999 $con->setRole("Author"); 7000 $con->save(); 7001 $ent = &$con->addEntity(); 7002 $ent->setEntity($a_author); 7003 $ent->save(); 7004 } 7005 } 7006 7007 /** 7008 * Create meta data entry 7009 * 7010 * @access public 7011 */ 7012 public function createMetaData() 7013 { 7014 parent::createMetaData(); 7015 $this->saveAuthorToMetadata(); 7016 } 7017 7018 /** 7019 * Gets the authors name of the ilObjTest object 7020 * 7021 * @return string The string containing the name of the test author 7022 * @access public 7023 * @see $author 7024 */ 7025 public function getAuthor() 7026 { 7027 $author = array(); 7028 include_once "./Services/MetaData/classes/class.ilMD.php"; 7029 $md = new ilMD($this->getId(), 0, $this->getType()); 7030 $md_life = &$md->getLifecycle(); 7031 if ($md_life) { 7032 $ids = &$md_life->getContributeIds(); 7033 foreach ($ids as $id) { 7034 $md_cont = &$md_life->getContribute($id); 7035 if (strcmp($md_cont->getRole(), "Author") == 0) { 7036 $entids = &$md_cont->getEntityIds(); 7037 foreach ($entids as $entid) { 7038 $md_ent = &$md_cont->getEntity($entid); 7039 array_push($author, $md_ent->getEntity()); 7040 } 7041 } 7042 } 7043 } 7044 return join(",", $author); 7045 } 7046 7047 /** 7048 * Gets the authors name of the ilObjTest object 7049 * 7050 * @return string The string containing the name of the test author 7051 * @access public 7052 * @see $author 7053 */ 7054 public static function _lookupAuthor($obj_id) 7055 { 7056 $author = array(); 7057 include_once "./Services/MetaData/classes/class.ilMD.php"; 7058 $md = new ilMD($obj_id, 0, "tst"); 7059 $md_life = &$md->getLifecycle(); 7060 if ($md_life) { 7061 $ids = &$md_life->getContributeIds(); 7062 foreach ($ids as $id) { 7063 $md_cont = &$md_life->getContribute($id); 7064 if (strcmp($md_cont->getRole(), "Author") == 0) { 7065 $entids = &$md_cont->getEntityIds(); 7066 foreach ($entids as $entid) { 7067 $md_ent = &$md_cont->getEntity($entid); 7068 array_push($author, $md_ent->getEntity()); 7069 } 7070 } 7071 } 7072 } 7073 return join(",", $author); 7074 } 7075 7076 /** 7077 * Returns the available tests for the active user 7078 * 7079 * @return array The available tests 7080 * @access public 7081 */ 7082 public static function _getAvailableTests($use_object_id = false) 7083 { 7084 global $DIC; 7085 $ilUser = $DIC['ilUser']; 7086 $ilDB = $DIC['ilDB']; 7087 7088 $result_array = array(); 7089 $tests = array_slice( 7090 array_reverse( 7091 ilUtil::_getObjectsByOperations("tst", "write", $ilUser->getId(), PHP_INT_MAX) 7092 ), 7093 0, 7094 10000 7095 ); 7096 7097 if (count($tests)) { 7098 $titles = ilObject::_prepareCloneSelection($tests, "tst"); 7099 foreach ($tests as $ref_id) { 7100 if ($use_object_id) { 7101 $obj_id = ilObject::_lookupObjId($ref_id); 7102 $result_array[$obj_id] = $titles[$ref_id]; 7103 } else { 7104 $result_array[$ref_id] = $titles[$ref_id]; 7105 } 7106 } 7107 } 7108 return $result_array; 7109 } 7110 7111 /** 7112 * Clone object 7113 * 7114 * @access public 7115 * @param int ref id of parent container 7116 * @param int copy id 7117 * @return object new test object 7118 */ 7119 public function cloneObject($a_target_id, $a_copy_id = 0, $a_omit_tree = false) 7120 { 7121 global $DIC; 7122 7123 $certificateLogger = $DIC->logger()->cert(); 7124 $tree = $DIC['tree']; 7125 $ilDB = $DIC->database(); 7126 $ilPluginAdmin = $DIC['ilPluginAdmin']; 7127 7128 $this->loadFromDb(); 7129 7130 // Copy settings 7131 /** @var $newObj ilObjTest */ 7132 $newObj = parent::cloneObject($a_target_id, $a_copy_id, $a_omit_tree); 7133 $newObj->setTmpCopyWizardCopyId($a_copy_id); 7134 $this->cloneMetaData($newObj); 7135 7136 // #27082 7137 $newObj->setOfflineStatus(true); 7138 $newObj->update(); 7139 7140 $newObj->setAnonymity($this->getAnonymity()); 7141 $newObj->setAnswerFeedback($this->getAnswerFeedback()); 7142 $newObj->setAnswerFeedbackPoints($this->getAnswerFeedbackPoints()); 7143 $newObj->setAuthor($this->getAuthor()); 7144 $newObj->setLimitUsersEnabled($this->isLimitUsersEnabled()); 7145 $newObj->setAllowedUsers($this->getAllowedUsers()); 7146 $newObj->setAllowedUsersTimeGap($this->getAllowedUsersTimeGap()); 7147 $newObj->setCountSystem($this->getCountSystem()); 7148 $newObj->setECTSFX($this->getECTSFX()); 7149 $newObj->setECTSGrades($this->getECTSGrades()); 7150 $newObj->setECTSOutput($this->getECTSOutput()); 7151 $newObj->setEnableProcessingTime($this->getEnableProcessingTime()); 7152 $newObj->setEndingTimeEnabled($this->isEndingTimeEnabled()); 7153 $newObj->setEndingTime($this->getEndingTime()); 7154 $newObj->setFixedParticipants($this->getFixedParticipants()); 7155 $newObj->setInstantFeedbackSolution($this->getInstantFeedbackSolution()); 7156 $newObj->setIntroductionEnabled($this->isIntroductionEnabled()); 7157 $newObj->setIntroduction($this->getIntroduction()); 7158 $newObj->setFinalStatement($this->getFinalStatement()); 7159 $newObj->setShowInfo($this->getShowInfo()); 7160 $newObj->setForceJS($this->getForceJS()); 7161 $newObj->setCustomStyle($this->getCustomStyle()); 7162 $newObj->setKiosk($this->getKiosk()); 7163 $newObj->setShowFinalStatement($this->getShowFinalStatement()); 7164 $newObj->setListOfQuestionsSettings($this->getListOfQuestionsSettings()); 7165 $newObj->setMCScoring($this->getMCScoring()); 7166 $newObj->setMailNotification($this->getMailNotification()); 7167 $newObj->setMailNotificationType($this->getMailNotificationType()); 7168 $newObj->setNrOfTries($this->getNrOfTries()); 7169 $newObj->setBlockPassesAfterPassedEnabled($this->isBlockPassesAfterPassedEnabled()); 7170 $newObj->setPassScoring($this->getPassScoring()); 7171 $newObj->setPasswordEnabled($this->isPasswordEnabled()); 7172 $newObj->setPassword($this->getPassword()); 7173 $newObj->setProcessingTime($this->getProcessingTime()); 7174 $newObj->setQuestionSetType($this->getQuestionSetType()); 7175 $newObj->setReportingDate($this->getReportingDate()); 7176 $newObj->setResetProcessingTime($this->getResetProcessingTime()); 7177 $newObj->setResultsPresentation($this->getResultsPresentation()); 7178 $newObj->setScoreCutting($this->getScoreCutting()); 7179 $newObj->setScoreReporting($this->getScoreReporting()); 7180 $newObj->setSequenceSettings($this->getSequenceSettings()); 7181 $newObj->setShowCancel($this->getShowCancel()); 7182 $newObj->setShowMarker($this->getShowMarker()); 7183 $newObj->setShuffleQuestions($this->getShuffleQuestions()); 7184 $newObj->setStartingTimeEnabled($this->isStartingTimeEnabled()); 7185 $newObj->setStartingTime($this->getStartingTime()); 7186 $newObj->setTitleOutput($this->getTitleOutput()); 7187 $newObj->setUsePreviousAnswers($this->getUsePreviousAnswers()); 7188 $newObj->setRedirectionMode($this->getRedirectionMode()); 7189 $newObj->setRedirectionUrl($this->getRedirectionUrl()); 7190 $newObj->setCertificateVisibility($this->getCertificateVisibility()); 7191 $newObj->mark_schema = clone $this->mark_schema; 7192 $newObj->setEnabledViewMode($this->getEnabledViewMode()); 7193 $newObj->setTemplate($this->getTemplate()); 7194 $newObj->setPoolUsage($this->getPoolUsage()); 7195 $newObj->setPrintBestSolutionWithResult($this->isBestSolutionPrintedWithResult()); 7196 $newObj->setShowExamIdInTestPassEnabled($this->isShowExamIdInTestPassEnabled()); 7197 $newObj->setShowExamIdInTestResultsEnabled($this->isShowExamIdInTestResultsEnabled()); 7198 $newObj->setEnableExamView($this->getEnableExamview()); 7199 $newObj->setShowExamViewHtml($this->getShowExamviewHtml()); 7200 $newObj->setShowExamViewPdf($this->getShowExamviewPdf()); 7201 $newObj->setEnableArchiving($this->getEnableArchiving()); 7202 $newObj->setSignSubmission($this->getSignSubmission()); 7203 $newObj->setCharSelectorAvailability((int) $this->getCharSelectorAvailability()); 7204 $newObj->setCharSelectorDefinition($this->getCharSelectorDefinition()); 7205 $newObj->setSkillServiceEnabled($this->isSkillServiceEnabled()); 7206 $newObj->setResultFilterTaxIds($this->getResultFilterTaxIds()); 7207 $newObj->setFollowupQuestionAnswerFixationEnabled($this->isFollowupQuestionAnswerFixationEnabled()); 7208 $newObj->setInstantFeedbackAnswerFixationEnabled($this->isInstantFeedbackAnswerFixationEnabled()); 7209 $newObj->setForceInstantFeedbackEnabled($this->isForceInstantFeedbackEnabled()); 7210 $newObj->setAutosave($this->getAutosave()); 7211 $newObj->setAutosaveIval($this->getAutosaveIval()); 7212 $newObj->setOfferingQuestionHintsEnabled($this->isOfferingQuestionHintsEnabled()); 7213 $newObj->setSpecificAnswerFeedback($this->getSpecificAnswerFeedback()); 7214 if ($this->isPassWaitingEnabled()) { 7215 $newObj->setPassWaiting($this->getPassWaiting()); 7216 } 7217 $newObj->setObligationsEnabled($this->areObligationsEnabled()); 7218 $newObj->saveToDb(); 7219 7220 // clone certificate 7221 $pathFactory = new ilCertificatePathFactory(); 7222 $templateRepository = new ilCertificateTemplateRepository($ilDB); 7223 7224 $cloneAction = new ilCertificateCloneAction( 7225 $ilDB, 7226 $pathFactory, 7227 $templateRepository, 7228 $DIC->filesystem()->web(), 7229 $certificateLogger, 7230 new ilCertificateObjectHelper() 7231 ); 7232 7233 $cloneAction->cloneCertificate($this, $newObj); 7234 7235 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $ilPluginAdmin, $this); 7236 $testQuestionSetConfigFactory->getQuestionSetConfig()->cloneQuestionSetRelatedData($newObj); 7237 7238 require_once 'Modules/Test/classes/class.ilTestSkillLevelThresholdList.php'; 7239 $skillLevelThresholdList = new ilTestSkillLevelThresholdList($ilDB); 7240 $skillLevelThresholdList->setTestId($this->getTestId()); 7241 $skillLevelThresholdList->loadFromDb(); 7242 $skillLevelThresholdList->cloneListForTest($newObj->getTestId()); 7243 7244 $newObj->saveToDb(); 7245 $newObj->updateMetaData();// #14467 7246 7247 include_once('./Services/Tracking/classes/class.ilLPObjSettings.php'); 7248 $obj_settings = new ilLPObjSettings($this->getId()); 7249 $obj_settings->cloneSettings($newObj->getId()); 7250 7251 return $newObj; 7252 } 7253 7254 /** 7255 * Returns the number of questions in the test 7256 * 7257 * @return integer The number of questions 7258 * @access public 7259 */ 7260 public function getQuestionCount() 7261 { 7262 $num = 0; 7263 7264 if ($this->isRandomTest()) { 7265 global $DIC; 7266 $tree = $DIC['tree']; 7267 $ilDB = $DIC['ilDB']; 7268 $ilPluginAdmin = $DIC['ilPluginAdmin']; 7269 7270 $questionSetConfig = new ilTestRandomQuestionSetConfig( 7271 $tree, 7272 $ilDB, 7273 $ilPluginAdmin, 7274 $this 7275 ); 7276 7277 $questionSetConfig->loadFromDb(); 7278 7279 if ($questionSetConfig->isQuestionAmountConfigurationModePerPool()) { 7280 require_once 'Modules/Test/classes/class.ilTestRandomQuestionSetSourcePoolDefinitionList.php'; 7281 require_once 'Modules/Test/classes/class.ilTestRandomQuestionSetBuilderWithAmountPerPool.php'; 7282 require_once 'Modules/Test/classes/class.ilTestRandomQuestionSetSourcePoolDefinitionFactory.php'; 7283 7284 $sourcePoolDefinitionList = new ilTestRandomQuestionSetSourcePoolDefinitionList( 7285 $ilDB, 7286 $this, 7287 new ilTestRandomQuestionSetSourcePoolDefinitionFactory($ilDB, $this) 7288 ); 7289 7290 $sourcePoolDefinitionList->loadDefinitions(); 7291 7292 $num = $sourcePoolDefinitionList->getQuestionAmount(); 7293 } else { 7294 $num = $questionSetConfig->getQuestionAmountPerTest(); 7295 } 7296 } else { 7297 $num = count($this->questions); 7298 } 7299 7300 return $num; 7301 } 7302 7303 /** 7304 * Logs an action into the Test&Assessment log 7305 * 7306 * @param string $logtext The log text 7307 * @param integer $question_id If given, saves the question id to the database 7308 * @access public 7309 */ 7310 public function logAction($logtext = "", $question_id = "") 7311 { 7312 global $DIC; 7313 $ilUser = $DIC['ilUser']; 7314 7315 $original_id = ""; 7316 if (strcmp($question_id, "") != 0) { 7317 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 7318 $original_id = assQuestion::_getOriginalId($question_id); 7319 } 7320 include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php"; 7321 ilObjAssessmentFolder::_addLog($ilUser->getId(), $this->getId(), $logtext, $question_id, $original_id, true, $this->getRefId()); 7322 } 7323 7324 /** 7325 * Returns the ILIAS test object id for a given test id 7326 * 7327 * @param integer $test_id The test id 7328 * @return mixed The ILIAS test object id or FALSE if the query was not successful 7329 * @access public 7330 */ 7331 public static function _getObjectIDFromTestID($test_id) 7332 { 7333 global $DIC; 7334 $ilDB = $DIC['ilDB']; 7335 $object_id = false; 7336 $result = $ilDB->queryF( 7337 "SELECT obj_fi FROM tst_tests WHERE test_id = %s", 7338 array('integer'), 7339 array($test_id) 7340 ); 7341 if ($result->numRows()) { 7342 $row = $ilDB->fetchAssoc($result); 7343 $object_id = $row["obj_fi"]; 7344 } 7345 return $object_id; 7346 } 7347 7348 /** 7349 * Returns the ILIAS test object id for a given active id 7350 * 7351 * @param integer $active_id The active id 7352 * @return mixed The ILIAS test object id or FALSE if the query was not successful 7353 * @access public 7354 */ 7355 public static function _getObjectIDFromActiveID($active_id) 7356 { 7357 global $DIC; 7358 $ilDB = $DIC['ilDB']; 7359 $object_id = false; 7360 $result = $ilDB->queryF( 7361 "SELECT tst_tests.obj_fi FROM tst_tests, tst_active WHERE tst_tests.test_id = tst_active.test_fi AND tst_active.active_id = %s", 7362 array('integer'), 7363 array($active_id) 7364 ); 7365 if ($result->numRows()) { 7366 $row = $ilDB->fetchAssoc($result); 7367 $object_id = $row["obj_fi"]; 7368 } 7369 return $object_id; 7370 } 7371 7372 /** 7373 * Returns the ILIAS test id for a given object id 7374 * 7375 * @param integer $object_id The object id 7376 * @return mixed The ILIAS test id or FALSE if the query was not successful 7377 * @access public 7378 */ 7379 public static function _getTestIDFromObjectID($object_id) 7380 { 7381 global $DIC; 7382 $ilDB = $DIC['ilDB']; 7383 $test_id = false; 7384 $result = $ilDB->queryF( 7385 "SELECT test_id FROM tst_tests WHERE obj_fi = %s", 7386 array('integer'), 7387 array($object_id) 7388 ); 7389 if ($result->numRows()) { 7390 $row = $ilDB->fetchAssoc($result); 7391 $test_id = $row["test_id"]; 7392 } 7393 return $test_id; 7394 } 7395 7396 /** 7397 * Returns the text answer of a given user for a given question 7398 * 7399 * @param integer $user_id The user id 7400 * @param integer $question_id The question id 7401 * @return string The answer text 7402 * @access public 7403 */ 7404 public function getTextAnswer($active_id, $question_id, $pass = null) 7405 { 7406 global $DIC; 7407 $ilDB = $DIC['ilDB']; 7408 7409 $res = ""; 7410 if (($active_id) && ($question_id)) { 7411 if (is_null($pass)) { 7412 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 7413 $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id); 7414 } 7415 $result = $ilDB->queryF( 7416 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s", 7417 array('integer', 'integer', 'integer'), 7418 array($active_id, $question_id, $pass) 7419 ); 7420 if ($result->numRows() == 1) { 7421 $row = $ilDB->fetchAssoc($result); 7422 $res = $row["value1"]; 7423 } 7424 } 7425 return $res; 7426 } 7427 7428 /** 7429 * Returns the question text for a given question 7430 * 7431 * @param integer $question_id The question id 7432 * @return string The question text 7433 * @access public 7434 */ 7435 public function getQuestiontext($question_id) 7436 { 7437 global $DIC; 7438 $ilDB = $DIC['ilDB']; 7439 7440 $res = ""; 7441 if ($question_id) { 7442 $result = $ilDB->queryF( 7443 "SELECT question_text FROM qpl_questions WHERE question_id = %s", 7444 array('integer'), 7445 array($question_id) 7446 ); 7447 if ($result->numRows() == 1) { 7448 $row = $ilDB->fetchAssoc($result); 7449 $res = $row["question_text"]; 7450 } 7451 } 7452 return $res; 7453 } 7454 7455 /** 7456 * @return ilTestParticipantList 7457 */ 7458 public function getInvitedParticipantList() 7459 { 7460 require_once 'Modules/Test/classes/class.ilTestParticipantList.php'; 7461 $participantList = new ilTestParticipantList($this); 7462 $participantList->initializeFromDbRows($this->getInvitedUsers()); 7463 7464 return $participantList; 7465 } 7466 7467 /** 7468 * @return ilTestParticipantList 7469 */ 7470 public function getActiveParticipantList() 7471 { 7472 require_once 'Modules/Test/classes/class.ilTestParticipantList.php'; 7473 $participantList = new ilTestParticipantList($this); 7474 $participantList->initializeFromDbRows($this->getTestParticipants()); 7475 7476 return $participantList; 7477 } 7478 7479 /** 7480 * Returns a list of all invited users in a test 7481 * 7482 * @return array array of invited users 7483 * @access public 7484 */ 7485 public function &getInvitedUsers($user_id = "", $order = "login, lastname, firstname") 7486 { 7487 global $DIC; 7488 $ilDB = $DIC['ilDB']; 7489 7490 $result_array = array(); 7491 7492 if ($this->getAnonymity()) { 7493 if (is_numeric($user_id)) { 7494 $result = $ilDB->queryF( 7495 "SELECT tst_active.active_id, tst_active.tries, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " . 7496 "tst_active.submitted test_finished, matriculation, COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes FROM usr_data, tst_invited_user " . 7497 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " . 7498 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " . 7499 "ORDER BY $order", 7500 array('text', 'text', 'text', 'integer', 'integer'), 7501 array("", $this->lng->txt("anonymous"), "", $this->getTestId(), $user_id) 7502 ); 7503 } else { 7504 $result = $ilDB->queryF( 7505 "SELECT tst_active.active_id, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " . 7506 "tst_active.submitted test_finished, matriculation, COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes FROM usr_data, tst_invited_user " . 7507 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " . 7508 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " . 7509 "ORDER BY $order", 7510 array('text', 'text', 'text', 'integer'), 7511 array("", $this->lng->txt("anonymous"), "", $this->getTestId()) 7512 ); 7513 } 7514 } else { 7515 if (is_numeric($user_id)) { 7516 $result = $ilDB->queryF( 7517 "SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " . 7518 "tst_active.submitted test_finished, matriculation, COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes FROM usr_data, tst_invited_user " . 7519 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " . 7520 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " . 7521 "ORDER BY $order", 7522 array('integer', 'integer'), 7523 array($this->getTestId(), $user_id) 7524 ); 7525 } else { 7526 $result = $ilDB->queryF( 7527 "SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " . 7528 "tst_active.submitted test_finished, matriculation, COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes FROM usr_data, tst_invited_user " . 7529 "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " . 7530 "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " . 7531 "ORDER BY $order", 7532 array('integer'), 7533 array($this->getTestId()) 7534 ); 7535 } 7536 } 7537 $result_array = array(); 7538 while ($row = $ilDB->fetchAssoc($result)) { 7539 $result_array[$row['usr_id']] = $row; 7540 } 7541 return $result_array; 7542 } 7543 7544 /** 7545 * Returns a list of all participants in a test 7546 * 7547 * @return array The user id's of the participants 7548 * @access public 7549 */ 7550 public function &getTestParticipants() 7551 { 7552 global $DIC; 7553 $ilDB = $DIC['ilDB']; 7554 7555 if ($this->getAnonymity()) { 7556 $query = " 7557 SELECT tst_active.active_id, 7558 tst_active.tries, 7559 tst_active.user_fi usr_id, 7560 %s login, 7561 %s lastname, 7562 %s firstname, 7563 tst_active.submitted test_finished, 7564 usr_data.matriculation, 7565 usr_data.active, 7566 tst_active.lastindex, 7567 COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes 7568 FROM tst_active 7569 LEFT JOIN usr_data 7570 ON tst_active.user_fi = usr_data.usr_id 7571 WHERE tst_active.test_fi = %s 7572 ORDER BY usr_data.lastname 7573 "; 7574 $result = $ilDB->queryF( 7575 $query, 7576 array('text', 'text', 'text', 'integer'), 7577 array("", $this->lng->txt("anonymous"), "", $this->getTestId()) 7578 ); 7579 } else { 7580 $query = " 7581 SELECT tst_active.active_id, 7582 tst_active.tries, 7583 tst_active.user_fi usr_id, 7584 usr_data.login, 7585 usr_data.lastname, 7586 usr_data.firstname, 7587 tst_active.submitted test_finished, 7588 usr_data.matriculation, 7589 usr_data.active, 7590 tst_active.lastindex, 7591 COALESCE(tst_active.last_finished_pass, -1) <> tst_active.last_started_pass unfinished_passes 7592 FROM tst_active 7593 LEFT JOIN usr_data 7594 ON tst_active.user_fi = usr_data.usr_id 7595 WHERE tst_active.test_fi = %s 7596 ORDER BY usr_data.lastname 7597 "; 7598 $result = $ilDB->queryF( 7599 $query, 7600 array('integer'), 7601 array($this->getTestId()) 7602 ); 7603 } 7604 $data = array(); 7605 while ($row = $ilDB->fetchAssoc($result)) { 7606 $data[$row['active_id']] = $row; 7607 } 7608 foreach ($data as $index => $participant) { 7609 if (strlen(trim($participant["firstname"] . $participant["lastname"])) == 0) { 7610 $data[$index]["lastname"] = $this->lng->txt("deleted_user"); 7611 } 7612 } 7613 return $data; 7614 } 7615 7616 public function getTestParticipantsForManualScoring($filter = null) 7617 { 7618 global $DIC; 7619 $ilDB = $DIC['ilDB']; 7620 7621 include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php"; 7622 $scoring = ilObjAssessmentFolder::_getManualScoring(); 7623 if (count($scoring) == 0) { 7624 return array(); 7625 } 7626 7627 $participants = &$this->getTestParticipants(); 7628 $filtered_participants = array(); 7629 foreach ($participants as $active_id => $participant) { 7630 $qstType_IN_manScoreableQstTypes = $ilDB->in('qpl_questions.question_type_fi', $scoring, false, 'integer'); 7631 7632 $queryString = " 7633 SELECT tst_test_result.manual 7634 7635 FROM tst_test_result 7636 7637 INNER JOIN qpl_questions 7638 ON tst_test_result.question_fi = qpl_questions.question_id 7639 7640 WHERE tst_test_result.active_fi = %s 7641 AND $qstType_IN_manScoreableQstTypes 7642 "; 7643 7644 $result = $ilDB->queryF( 7645 $queryString, 7646 array("integer"), 7647 array($active_id) 7648 ); 7649 7650 $count = $result->numRows(); 7651 7652 if ($count > 0) { 7653 switch ($filter) { 7654 case 1: // only active users 7655 if ($participant->active) { 7656 $filtered_participants[$active_id] = $participant; 7657 } 7658 break; 7659 case 2: // only inactive users 7660 if (!$participant->active) { 7661 $filtered_participants[$active_id] = $participant; 7662 } 7663 break; 7664 case 3: // all users 7665 $filtered_participants[$active_id] = $participant; 7666 break; 7667 case 4: 7668 // already scored participants 7669 //$found = 0; 7670 //while ($row = $ilDB->fetchAssoc($result)) 7671 //{ 7672 // if ($row["manual"]) $found++; 7673 //} 7674 //if ($found == $count) 7675 //{ 7676 //$filtered_participants[$active_id] = $participant; 7677 //} 7678 //else 7679 //{ 7680 $assessmentSetting = new ilSetting("assessment"); 7681 $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id); 7682 if ($manscoring_done) { 7683 $filtered_participants[$active_id] = $participant; 7684 } 7685 //} 7686 break; 7687 case 5: 7688 // unscored participants 7689 //$found = 0; 7690 //while ($row = $ilDB->fetchAssoc($result)) 7691 //{ 7692 // if ($row["manual"]) $found++; 7693 //} 7694 //if ($found == 0) 7695 //{ 7696 $assessmentSetting = new ilSetting("assessment"); 7697 $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id); 7698 if (!$manscoring_done) { 7699 $filtered_participants[$active_id] = $participant; 7700 } 7701 //} 7702 break; 7703 case 6: 7704 // partially scored participants 7705 $found = 0; 7706 while ($row = $ilDB->fetchAssoc($result)) { 7707 if ($row["manual"]) { 7708 $found++; 7709 } 7710 } 7711 if (($found > 0) && ($found < $count)) { 7712 $filtered_participants[$active_id] = $participant; 7713 } 7714 break; 7715 default: 7716 $filtered_participants[$active_id] = $participant; 7717 break; 7718 } 7719 } 7720 } 7721 return $filtered_participants; 7722 } 7723 7724 /** 7725 * Returns a data of all users specified by id list 7726 * 7727 * @param $usr_ids kommaseparated list of ids 7728 * @return array The user data "usr_id, login, lastname, firstname, clientip" of the users with id as key 7729 * @access public 7730 */ 7731 public function &getUserData($ids) 7732 { 7733 global $DIC; 7734 $ilDB = $DIC['ilDB']; 7735 7736 if (!is_array($ids) || count($ids) == 0) { 7737 return array(); 7738 } 7739 7740 if ($this->getAnonymity()) { 7741 $result = $ilDB->queryF( 7742 "SELECT usr_id, %s login, %s lastname, %s firstname, client_ip clientip FROM usr_data WHERE " . $ilDB->in('usr_id', $ids, false, 'integer') . " ORDER BY login", 7743 array('text', 'text', 'text'), 7744 array("", $this->lng->txt("anonymous"), "") 7745 ); 7746 } else { 7747 $result = $ilDB->query("SELECT usr_id, login, lastname, firstname, client_ip clientip FROM usr_data WHERE " . $ilDB->in('usr_id', $ids, false, 'integer') . " ORDER BY login"); 7748 } 7749 7750 $result_array = array(); 7751 while ($row = $ilDB->fetchAssoc($result)) { 7752 $result_array[$row["usr_id"]] = $row; 7753 } 7754 return $result_array; 7755 } 7756 7757 public function &getGroupData($ids) 7758 { 7759 if (!is_array($ids) || count($ids) == 0) { 7760 return array(); 7761 } 7762 $result = array(); 7763 foreach ($ids as $ref_id) { 7764 $obj_id = ilObject::_lookupObjId($ref_id); 7765 $result[$ref_id] = array("ref_id" => $ref_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id)); 7766 } 7767 return $result; 7768 } 7769 7770 public function &getRoleData($ids) 7771 { 7772 if (!is_array($ids) || count($ids) == 0) { 7773 return array(); 7774 } 7775 $result = array(); 7776 foreach ($ids as $obj_id) { 7777 $result[$obj_id] = array("obj_id" => $obj_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id)); 7778 } 7779 return $result; 7780 } 7781 7782 7783 /** 7784 * Invites all users of a group to a test 7785 * 7786 * @param integer $group_id The database id of the invited group 7787 * @access public 7788 */ 7789 public function inviteGroup($group_id) 7790 { 7791 include_once "./Modules/Group/classes/class.ilObjGroup.php"; 7792 $group = new ilObjGroup($group_id); 7793 $members = $group->getGroupMemberIds(); 7794 include_once './Services/User/classes/class.ilObjUser.php'; 7795 foreach ($members as $user_id) { 7796 $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id)); 7797 } 7798 } 7799 7800 /** 7801 * Invites all users of a role to a test 7802 * 7803 * @param integer $group_id The database id of the invited group 7804 * @access public 7805 */ 7806 public function inviteRole($role_id) 7807 { 7808 global $DIC; 7809 $rbacreview = $DIC['rbacreview']; 7810 $members = $rbacreview->assignedUsers($role_id); 7811 include_once './Services/User/classes/class.ilObjUser.php'; 7812 foreach ($members as $user_id) { 7813 $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id)); 7814 } 7815 } 7816 7817 7818 7819 /** 7820 * Disinvites a user from a test 7821 * 7822 * @param integer $user_id The database id of the disinvited user 7823 * @access public 7824 */ 7825 public function disinviteUser($user_id) 7826 { 7827 global $DIC; 7828 $ilDB = $DIC['ilDB']; 7829 7830 $affectedRows = $ilDB->manipulateF( 7831 "DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s", 7832 array('integer', 'integer'), 7833 array($this->getTestId(), $user_id) 7834 ); 7835 } 7836 7837 /** 7838 * Invites a user to a test 7839 * 7840 * @param integer $user_id The database id of the invited user 7841 * @access public 7842 */ 7843 public function inviteUser($user_id, $client_ip = "") 7844 { 7845 global $DIC; 7846 $ilDB = $DIC['ilDB']; 7847 7848 $affectedRows = $ilDB->manipulateF( 7849 "DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s", 7850 array('integer', 'integer'), 7851 array($this->getTestId(), $user_id) 7852 ); 7853 $affectedRows = $ilDB->manipulateF( 7854 "INSERT INTO tst_invited_user (test_fi, user_fi, clientip, tstamp) VALUES (%s, %s, %s, %s)", 7855 array('integer', 'integer', 'text', 'integer'), 7856 array($this->getTestId(), $user_id, (strlen($client_ip)) ? $client_ip : null, time()) 7857 ); 7858 } 7859 7860 7861 public function setClientIP($user_id, $client_ip) 7862 { 7863 global $DIC; 7864 $ilDB = $DIC['ilDB']; 7865 7866 $affectedRows = $ilDB->manipulateF( 7867 "UPDATE tst_invited_user SET clientip = %s, tstamp = %s WHERE test_fi=%s and user_fi=%s", 7868 array('text', 'integer', 'integer', 'integer'), 7869 array((strlen($client_ip)) ? $client_ip : null, time(), $this->getTestId(), $user_id) 7870 ); 7871 } 7872 7873 /** 7874 * get solved questions 7875 * 7876 * @return array of int containing all question ids which have been set solved for the given user and test 7877 */ 7878 public static function _getSolvedQuestions($active_id, $question_fi = null) 7879 { 7880 global $DIC; 7881 $ilDB = $DIC['ilDB']; 7882 if (is_numeric($question_fi)) { 7883 $result = $ilDB->queryF( 7884 "SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s AND question_fi=%s", 7885 array('integer', 'integer'), 7886 array($active_id, $question_fi) 7887 ); 7888 } else { 7889 $result = $ilDB->queryF( 7890 "SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s", 7891 array('integer'), 7892 array($active_id) 7893 ); 7894 } 7895 $result_array = array(); 7896 while ($row = $ilDB->fetchAssoc($result)) { 7897 $result_array[$row["question_fi"]] = $row; 7898 } 7899 return $result_array; 7900 } 7901 7902 7903 /** 7904 * sets question solved state to value for given user_id 7905 */ 7906 public function setQuestionSetSolved($value, $question_id, $user_id) 7907 { 7908 global $DIC; 7909 $ilDB = $DIC['ilDB']; 7910 7911 $active_id = $this->getActiveIdOfUser($user_id); 7912 $affectedRows = $ilDB->manipulateF( 7913 "DELETE FROM tst_qst_solved WHERE active_fi = %s AND question_fi = %s", 7914 array('integer', 'integer'), 7915 array($active_id, $question_id) 7916 ); 7917 $affectedRows = $ilDB->manipulateF( 7918 "INSERT INTO tst_qst_solved (solved, question_fi, active_fi) VALUES (%s, %s, %s)", 7919 array('integer', 'integer', 'integer'), 7920 array($value, $question_id, $active_id) 7921 ); 7922 } 7923 7924 /** 7925 * returns if the active for user_id has been submitted 7926 */ 7927 public function isTestFinished($active_id) 7928 { 7929 global $DIC; 7930 $ilDB = $DIC['ilDB']; 7931 7932 $result = $ilDB->queryF( 7933 "SELECT submitted FROM tst_active WHERE active_id=%s AND submitted=%s", 7934 array('integer', 'integer'), 7935 array($active_id, 1) 7936 ); 7937 return $result->numRows() == 1; 7938 } 7939 7940 /** 7941 * returns if the active for user_id has been submitted 7942 */ 7943 public function isActiveTestSubmitted($user_id = null) 7944 { 7945 global $DIC; 7946 $ilUser = $DIC['ilUser']; 7947 $ilDB = $DIC['ilDB']; 7948 7949 if (!is_numeric($user_id)) { 7950 $user_id = $ilUser->getId(); 7951 } 7952 7953 $result = $ilDB->queryF( 7954 "SELECT submitted FROM tst_active WHERE test_fi=%s AND user_fi=%s AND submitted=%s", 7955 array('integer', 'integer', 'integer'), 7956 array($this->getTestId(), $user_id, 1) 7957 ); 7958 return $result->numRows() == 1; 7959 } 7960 7961 /** 7962 * returns if the numbers of tries have to be checked 7963 */ 7964 public function hasNrOfTriesRestriction() 7965 { 7966 return $this->getNrOfTries() != 0; 7967 } 7968 7969 7970 /** 7971 * returns if number of tries are reached 7972 * @deprecated: tries field differs per situation, outside a pass it's the number of tries, inside a pass it's the current pass number. 7973 */ 7974 7975 public function isNrOfTriesReached($tries) 7976 { 7977 return $tries >= (int) $this->getNrOfTries(); 7978 } 7979 7980 7981 /** 7982 * returns all test results for all participants 7983 * 7984 * @param array $partipants array of user ids 7985 * @param boolean if true, the result will be prepared for csv output (see processCSVRow) 7986 * 7987 * @return array of fields, see code for column titles 7988 */ 7989 public function getAllTestResults($participants, $prepareForCSV = true) 7990 { 7991 $results = array(); 7992 $row = array( 7993 "user_id" => $this->lng->txt("user_id"), 7994 "matriculation" => $this->lng->txt("matriculation"), 7995 "lastname" => $this->lng->txt("lastname"), 7996 "firstname" => $this->lng->txt("firstname"), 7997 "login" => $this->lng->txt("login"), 7998 "reached_points" => $this->lng->txt("tst_reached_points"), 7999 "max_points" => $this->lng->txt("tst_maximum_points"), 8000 "percent_value" => $this->lng->txt("tst_percent_solved"), 8001 "mark" => $this->lng->txt("tst_mark"), 8002 "ects" => $this->lng->txt("ects_grade") 8003 ); 8004 $results[] = $row; 8005 if (count($participants)) { 8006 if ($this->getECTSOutput()) { 8007 $passed_array = &$this->getTotalPointsPassedArray(); 8008 } 8009 foreach ($participants as $active_id => $user_rec) { 8010 $mark = $ects_mark = ''; 8011 $row = array(); 8012 $reached_points = 0; 8013 $max_points = 0; 8014 foreach ($this->questions as $value) { 8015 $question = &ilObjTest::_instanciateQuestion($value); 8016 if (is_object($question)) { 8017 $max_points += $question->getMaximumPoints(); 8018 $reached_points += $question->getReachedPoints($active_id); 8019 } 8020 } 8021 if ($max_points > 0) { 8022 $percentvalue = $reached_points / $max_points; 8023 if ($percentvalue < 0) { 8024 $percentvalue = 0.0; 8025 } 8026 } else { 8027 $percentvalue = 0; 8028 } 8029 $mark_obj = $this->mark_schema->getMatchingMark($percentvalue * 100); 8030 $passed = ""; 8031 if ($mark_obj) { 8032 $mark = $mark_obj->getOfficialName(); 8033 if ($this->getECTSOutput()) { 8034 $ects_mark = $this->getECTSGrade($passed_array, $reached_points, $max_points); 8035 } 8036 } 8037 if ($this->getAnonymity()) { 8038 $user_rec['firstname'] = ""; 8039 $user_rec['lastname'] = $this->lng->txt("anonymous"); 8040 } 8041 $row = array( 8042 "user_id" => $user_rec['usr_id'], 8043 "matriculation" => $user_rec['matriculation'], 8044 "lastname" => $user_rec['lastname'], 8045 "firstname" => $user_rec['firstname'], 8046 "login" => $user_rec['login'], 8047 "reached_points" => $reached_points, 8048 "max_points" => $max_points, 8049 "percent_value" => $percentvalue, 8050 "mark" => $mark, 8051 "ects" => $ects_mark 8052 ); 8053 $results[] = $prepareForCSV ? $this->processCSVRow($row, true) : $row; 8054 } 8055 } 8056 return $results; 8057 } 8058 8059 /** 8060 * Processes an array as a CSV row and converts the array values to correct CSV 8061 * values. The "converted" array is returned 8062 * 8063 * @param array $row The array containing the values for a CSV row 8064 * @param string $quoteAll Indicates to quote every value (=TRUE) or only values containing quotes and separators (=FALSE, default) 8065 * @param string $separator The value separator in the CSV row (used for quoting) (; = default) 8066 * @return array The converted array ready for CSV use 8067 * @access public 8068 */ 8069 public function &processCSVRow($row, $quoteAll = false, $separator = ";") 8070 { 8071 $resultarray = array(); 8072 foreach ($row as $rowindex => $entry) { 8073 $surround = false; 8074 if ($quoteAll) { 8075 $surround = true; 8076 } 8077 if (strpos($entry, "\"") !== false) { 8078 $entry = str_replace("\"", "\"\"", $entry); 8079 $surround = true; 8080 } 8081 if (strpos($entry, $separator) !== false) { 8082 $surround = true; 8083 } 8084 // replace all CR LF with LF (for Excel for Windows compatibility 8085 $entry = str_replace(chr(13) . chr(10), chr(10), $entry); 8086 8087 if ($surround) { 8088 $entry = "\"" . $entry . "\""; 8089 } 8090 8091 $resultarray[$rowindex] = $entry; 8092 } 8093 return $resultarray; 8094 } 8095 8096 /** 8097 * Retrieves the actual pass of a given user for a given test 8098 * 8099 * @param integer $user_id The user id 8100 * @param integer $test_id The test id 8101 * @return integer The pass of the user for the given test 8102 * @access public 8103 */ 8104 public static function _getPass($active_id) 8105 { 8106 global $DIC; 8107 $ilDB = $DIC['ilDB']; 8108 $result = $ilDB->queryF( 8109 "SELECT tries FROM tst_active WHERE active_id = %s", 8110 array('integer'), 8111 array($active_id) 8112 ); 8113 if ($result->numRows()) { 8114 $row = $ilDB->fetchAssoc($result); 8115 return $row["tries"]; 8116 } else { 8117 return 0; 8118 } 8119 } 8120 8121 /** 8122 * Retrieves the maximum pass of a given user for a given test 8123 * in which the user answered at least one question 8124 * 8125 * @param integer $user_id The user id 8126 * @param integer $test_id The test id 8127 * @return integer The pass of the user for the given test 8128 * @access public 8129 */ 8130 public static function _getMaxPass($active_id) 8131 { 8132 global $DIC; 8133 $ilDB = $DIC['ilDB']; 8134 $result = $ilDB->queryF( 8135 "SELECT MAX(pass) maxpass FROM tst_pass_result WHERE active_fi = %s", 8136 array('integer'), 8137 array($active_id) 8138 ); 8139 if ($result->numRows()) { 8140 $row = $ilDB->fetchAssoc($result); 8141 $max = $row["maxpass"]; 8142 } else { 8143 $max = null; 8144 } 8145 return $max; 8146 } 8147 8148 /** 8149 * Retrieves the best pass of a given user for a given test 8150 * @param int $active_id 8151 * @return int|mixed 8152 */ 8153 public static function _getBestPass($active_id) 8154 { 8155 global $DIC; 8156 $ilDB = $DIC['ilDB']; 8157 8158 $result = $ilDB->queryF( 8159 "SELECT * FROM tst_pass_result WHERE active_fi = %s", 8160 array('integer'), 8161 array($active_id) 8162 ); 8163 if ($result->numRows()) { 8164 $bestrow = null; 8165 $bestfactor = 0; 8166 while ($row = $ilDB->fetchAssoc($result)) { 8167 if ($row["maxpoints"] > 0) { 8168 $factor = $row["points"] / $row["maxpoints"]; 8169 } else { 8170 $factor = 0; 8171 } 8172 8173 if ($factor > $bestfactor) { 8174 $bestrow = $row; 8175 $bestfactor = $factor; 8176 } 8177 } 8178 if (is_array($bestrow)) { 8179 return $bestrow["pass"]; 8180 } else { 8181 return 0; 8182 } 8183 } else { 8184 return 0; 8185 } 8186 } 8187 8188 /** 8189 * Retrieves the pass number that should be counted for a given user 8190 * 8191 * @param integer $user_id The user id 8192 * @param integer $test_id The test id 8193 * @return integer The result pass of the user for the given test 8194 * @access public 8195 */ 8196 public static function _getResultPass($active_id) 8197 { 8198 $counted_pass = null; 8199 if (ilObjTest::_getPassScoring($active_id) == SCORE_BEST_PASS) { 8200 $counted_pass = ilObjTest::_getBestPass($active_id); 8201 } else { 8202 $counted_pass = ilObjTest::_getMaxPass($active_id); 8203 } 8204 return $counted_pass; 8205 } 8206 8207 /** 8208 * Retrieves the number of answered questions for a given user in a given test 8209 * 8210 * @param integer $user_id The user id 8211 * @param integer $test_id The test id 8212 * @param integer $pass The pass of the test (optional) 8213 * @return integer The number of answered questions 8214 * @access public 8215 */ 8216 public function getAnsweredQuestionCount($active_id, $pass = null) 8217 { 8218 if ($this->isDynamicTest()) { 8219 global $DIC; 8220 $tree = $DIC['tree']; 8221 $ilDB = $DIC['ilDB']; 8222 $lng = $DIC['lng']; 8223 $ilPluginAdmin = $DIC['ilPluginAdmin']; 8224 8225 require_once 'Modules/Test/classes/class.ilTestSessionFactory.php'; 8226 $testSessionFactory = new ilTestSessionFactory($this); 8227 $testSession = $testSessionFactory->getSession($active_id); 8228 8229 require_once 'Modules/Test/classes/class.ilTestSequenceFactory.php'; 8230 $testSequenceFactory = new ilTestSequenceFactory($ilDB, $lng, $ilPluginAdmin, $this); 8231 $testSequence = $testSequenceFactory->getSequenceByTestSession($testSession); 8232 8233 require_once 'Modules/Test/classes/class.ilObjTestDynamicQuestionSetConfig.php'; 8234 $dynamicQuestionSetConfig = new ilObjTestDynamicQuestionSetConfig($tree, $ilDB, $ilPluginAdmin, $this); 8235 $dynamicQuestionSetConfig->loadFromDb(); 8236 8237 $testSequence->loadFromDb($dynamicQuestionSetConfig); 8238 $testSequence->loadQuestions($dynamicQuestionSetConfig, new ilTestDynamicQuestionSetFilterSelection()); 8239 8240 return $testSequence->getTrackedQuestionCount(); 8241 } 8242 8243 if ($this->isRandomTest()) { 8244 $this->loadQuestions($active_id, $pass); 8245 } 8246 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 8247 $workedthrough = 0; 8248 foreach ($this->questions as $value) { 8249 if (assQuestion::_isWorkedThrough($active_id, $value, $pass)) { 8250 $workedthrough += 1; 8251 } 8252 } 8253 return $workedthrough; 8254 } 8255 8256 /** 8257 * @param int $active_id 8258 * @param int $pass 8259 * 8260 * @return int 8261 */ 8262 public static function lookupPassResultsUpdateTimestamp($active_id, $pass) 8263 { 8264 global $DIC; 8265 $ilDB = $DIC['ilDB']; 8266 8267 if (is_null($pass)) { 8268 $pass = 0; 8269 } 8270 8271 $query = " 8272 SELECT tst_pass_result.tstamp pass_res_tstamp, 8273 tst_test_result.tstamp quest_res_tstamp 8274 8275 FROM tst_pass_result 8276 8277 LEFT JOIN tst_test_result 8278 ON tst_test_result.active_fi = tst_pass_result.active_fi 8279 AND tst_test_result.pass = tst_pass_result.pass 8280 8281 WHERE tst_pass_result.active_fi = %s 8282 AND tst_pass_result.pass = %s 8283 8284 ORDER BY tst_test_result.tstamp DESC 8285 "; 8286 8287 $result = $ilDB->queryF( 8288 $query, 8289 array('integer', 'integer'), 8290 array($active_id, $pass) 8291 ); 8292 8293 while ($row = $ilDB->fetchAssoc($result)) { 8294 if ($row['qres_tstamp']) { 8295 return $row['quest_res_tstamp']; 8296 } 8297 8298 return $row['pass_res_tstamp']; 8299 } 8300 8301 return 0; 8302 } 8303 8304 /** 8305 * Checks if the test is executable by the given user 8306 * 8307 * @param ilTestSession|ilTestSessionDynamicQuestionSet 8308 * @param integer $user_id The user id 8309 * @return array Result array 8310 * @access public 8311 */ 8312 public function isExecutable($testSession, $user_id, $allowPassIncrease = false) 8313 { 8314 $result = array( 8315 "executable" => true, 8316 "errormessage" => "" 8317 ); 8318 if (!$this->startingTimeReached()) { 8319 $result["executable"] = false; 8320 $result["errormessage"] = sprintf($this->lng->txt("detail_starting_time_not_reached"), ilDatePresentation::formatDate(new ilDateTime($this->getStartingTime(), IL_CAL_UNIX))); 8321 return $result; 8322 } 8323 if ($this->endingTimeReached()) { 8324 $result["executable"] = false; 8325 $result["errormessage"] = sprintf($this->lng->txt("detail_ending_time_reached"), ilDatePresentation::formatDate(new ilDateTime($this->getEndingTime(), IL_CAL_UNIX))); 8326 return $result; 8327 } 8328 8329 $active_id = $this->getActiveIdOfUser($user_id); 8330 8331 if ($this->getEnableProcessingTime()) { 8332 if ($active_id > 0) { 8333 $starting_time = $this->getStartingTimeOfUser($active_id); 8334 if ($starting_time !== false) { 8335 if ($this->isMaxProcessingTimeReached($starting_time, $active_id)) { 8336 if ($allowPassIncrease && $this->getResetProcessingTime() && (($this->getNrOfTries() == 0) || ($this->getNrOfTries() > (self::_getPass($active_id) + 1)))) { 8337 // a test pass was quitted because the maximum processing time was reached, but the time 8338 // will be resetted for future passes, so if there are more passes allowed, the participant may 8339 // start the test again. 8340 // This code block is only called when $allowPassIncrease is TRUE which only happens when 8341 // the test info page is opened. Otherwise this will lead to unexpected results! 8342 $testSession->increasePass(); 8343 $testSession->setLastSequence(0); 8344 $testSession->saveToDb(); 8345 } else { 8346 $result["executable"] = false; 8347 $result["errormessage"] = $this->lng->txt("detail_max_processing_time_reached"); 8348 } 8349 return $result; 8350 } 8351 } 8352 } 8353 } 8354 global $DIC; 8355 require_once 'Modules/Test/classes/class.ilTestPassesSelector.php'; 8356 $testPassesSelector = new ilTestPassesSelector($DIC['ilDB'], $this); 8357 $testPassesSelector->setActiveId($active_id); 8358 $testPassesSelector->setLastFinishedPass($testSession->getLastFinishedPass()); 8359 8360 if ($this->hasNrOfTriesRestriction() && ($active_id > 0)) { 8361 $closedPasses = $testPassesSelector->getClosedPasses(); 8362 8363 if (count($closedPasses) >= $this->getNrOfTries()) { 8364 $result["executable"] = false; 8365 $result["errormessage"] = $this->lng->txt("maximum_nr_of_tries_reached"); 8366 return $result; 8367 } 8368 8369 if ($this->isBlockPassesAfterPassedEnabled() && !$testPassesSelector->openPassExists()) { 8370 if (ilObjTestAccess::_isPassed($user_id, $this->getId())) { 8371 $result['executable'] = false; 8372 $result['errormessage'] = $this->lng->txt("tst_addit_passes_blocked_after_passed_msg"); 8373 return $result; 8374 } 8375 } 8376 } 8377 if ($this->isPassWaitingEnabled() && $testPassesSelector->getLastFinishedPass() !== null) { 8378 $lastPass = $testPassesSelector->getLastFinishedPassTimestamp(); 8379 if ($lastPass && strlen($this->getPassWaiting())) { 8380 $pass_waiting_string = $this->getPassWaiting(); 8381 $time_values = explode(":", $pass_waiting_string); 8382 $next_pass_allowed = strtotime('+ ' . $time_values[0] . ' Months + ' . $time_values[1] . ' Days + ' . $time_values[2] . ' Hours' . $time_values[3] . ' Minutes', $lastPass); 8383 8384 if (time() < $next_pass_allowed) { 8385 $date = ilDatePresentation::formatDate(new ilDateTime($next_pass_allowed, IL_CAL_UNIX)); 8386 8387 $result["executable"] = false; 8388 $result["errormessage"] = sprintf($this->lng->txt('wait_for_next_pass_hint_msg'), $date); 8389 return $result; 8390 } 8391 } 8392 } 8393 return $result; 8394 } 8395 8396 8397 public function canShowTestResults(ilTestSession $testSession) 8398 { 8399 global $DIC; /* @var ILIAS\DI\Container $DIC */ 8400 8401 require_once 'Modules/Test/classes/class.ilTestPassesSelector.php'; 8402 $passSelector = new ilTestPassesSelector($DIC->database(), $this); 8403 8404 $passSelector->setActiveId($testSession->getActiveId()); 8405 $passSelector->setLastFinishedPass($testSession->getLastFinishedPass()); 8406 8407 return $passSelector->hasReportablePasses(); 8408 } 8409 8410 public function hasAnyTestResult(ilTestSession $testSession) 8411 { 8412 global $DIC; /* @var ILIAS\DI\Container $DIC */ 8413 8414 require_once 'Modules/Test/classes/class.ilTestPassesSelector.php'; 8415 $passSelector = new ilTestPassesSelector($DIC->database(), $this); 8416 8417 $passSelector->setActiveId($testSession->getActiveId()); 8418 $passSelector->setLastFinishedPass($testSession->getLastFinishedPass()); 8419 8420 return $passSelector->hasExistingPasses(); 8421 } 8422 8423 /** 8424 * Returns the unix timestamp of the time a user started a test 8425 * 8426 * @param integer $active_id The active id of the user 8427 * @return mixed The unix timestamp if the user started the test, FALSE otherwise 8428 * @access public 8429 */ 8430 public function getStartingTimeOfUser($active_id, $pass = null) 8431 { 8432 global $DIC; 8433 $ilDB = $DIC['ilDB']; 8434 8435 if ($active_id < 1) { 8436 return false; 8437 } 8438 if ($pass === null) { 8439 $pass = ($this->getResetProcessingTime()) ? self::_getPass($active_id) : 0; 8440 } 8441 $result = $ilDB->queryF( 8442 "SELECT tst_times.started FROM tst_times WHERE tst_times.active_fi = %s AND tst_times.pass = %s ORDER BY tst_times.started", 8443 array('integer', 'integer'), 8444 array($active_id, $pass) 8445 ); 8446 if ($result->numRows()) { 8447 $row = $ilDB->fetchAssoc($result); 8448 if (preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches)) { 8449 return mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]); 8450 } else { 8451 return time(); 8452 } 8453 } else { 8454 return time(); 8455 } 8456 } 8457 8458 /** 8459 * Returns whether the maximum processing time for a test is reached or not 8460 * 8461 * @param long $starting_time The unix timestamp of the starting time of the test 8462 * @return boolean TRUE if the maxium processing time is reached, FALSE if the 8463 * maximum processing time is not reached or no maximum processing time is given 8464 * @access public 8465 */ 8466 public function isMaxProcessingTimeReached($starting_time, $active_id) 8467 { 8468 if ($this->getEnableProcessingTime()) { 8469 $processing_time = $this->getProcessingTimeInSeconds($active_id); 8470 $now = time(); 8471 if ($now > ($starting_time + $processing_time)) { 8472 return true; 8473 } else { 8474 return false; 8475 } 8476 } else { 8477 return false; 8478 } 8479 } 8480 8481 public function &getTestQuestions() 8482 { 8483 global $DIC; 8484 $ilDB = $DIC['ilDB']; 8485 8486 $query = " 8487 SELECT questions.*, 8488 questtypes.type_tag, 8489 tstquest.sequence, 8490 tstquest.obligatory, 8491 origquest.obj_fi orig_obj_fi 8492 8493 FROM qpl_questions questions 8494 8495 INNER JOIN qpl_qst_type questtypes 8496 ON questtypes.question_type_id = questions.question_type_fi 8497 8498 INNER JOIN tst_test_question tstquest 8499 ON tstquest.question_fi = questions.question_id 8500 8501 LEFT JOIN qpl_questions origquest 8502 ON origquest.question_id = questions.original_id 8503 8504 WHERE tstquest.test_fi = %s 8505 8506 ORDER BY tstquest.sequence 8507 "; 8508 8509 $query_result = $ilDB->queryF( 8510 $query, 8511 array('integer'), 8512 array($this->getTestId()) 8513 ); 8514 8515 $questions = array(); 8516 8517 while ($row = $ilDB->fetchAssoc($query_result)) { 8518 $question = $row; 8519 8520 $question['obligationPossible'] = self::isQuestionObligationPossible($row['question_id']); 8521 8522 $questions[] = $question; 8523 } 8524 8525 return $questions; 8526 } 8527 8528 /** 8529 * @param int $questionId 8530 * @return bool 8531 */ 8532 public function isTestQuestion($questionId) 8533 { 8534 foreach ($this->getTestQuestions() as $questionData) { 8535 if ($questionData['question_id'] != $questionId) { 8536 continue; 8537 } 8538 8539 return true; 8540 } 8541 8542 return false; 8543 } 8544 8545 public function checkQuestionParent($questionId) 8546 { 8547 global $DIC; /* @var ILIAS\DI\Container $DIC */ 8548 8549 $row = $DIC->database()->fetchAssoc($DIC->database()->queryF( 8550 "SELECT COUNT(question_id) cnt FROM qpl_questions WHERE question_id = %s AND obj_fi = %s", 8551 array('integer', 'integer'), 8552 array($questionId, $this->getId()) 8553 )); 8554 8555 return (bool) $row['cnt']; 8556 } 8557 8558 /** 8559 * @return float 8560 */ 8561 public function getFixedQuestionSetTotalPoints() 8562 { 8563 $points = 0; 8564 8565 foreach ($this->getTestQuestions() as $questionData) { 8566 $points += $questionData['points']; 8567 } 8568 8569 return $points; 8570 } 8571 8572 /** 8573 * @return string 8574 */ 8575 public function getFixedQuestionSetTotalWorkingTime() 8576 { 8577 $totalWorkingTime = '00:00:00'; 8578 8579 foreach ($this->getTestQuestions() as $questionData) { 8580 $totalWorkingTime = assQuestion::sumTimesInISO8601FormatH_i_s_Extended( 8581 $totalWorkingTime, 8582 $questionData['working_time'] 8583 ); 8584 } 8585 8586 return $totalWorkingTime; 8587 } 8588 8589 /** 8590 * @return array 8591 */ 8592 public function getPotentialRandomTestQuestions() 8593 { 8594 /** 8595 * @var $ilDB ilDBInterface 8596 */ 8597 global $DIC; 8598 $ilDB = $DIC['ilDB']; 8599 8600 $query = " 8601 SELECT questions.*, 8602 questtypes.type_tag, 8603 origquest.obj_fi orig_obj_fi 8604 8605 FROM qpl_questions questions 8606 8607 INNER JOIN qpl_qst_type questtypes 8608 ON questtypes.question_type_id = questions.question_type_fi 8609 8610 INNER JOIN tst_rnd_cpy tstquest 8611 ON tstquest.qst_fi = questions.question_id 8612 8613 LEFT JOIN qpl_questions origquest 8614 ON origquest.question_id = questions.original_id 8615 8616 WHERE tstquest.tst_fi = %s 8617 "; 8618 8619 $query_result = $ilDB->queryF( 8620 $query, 8621 array('integer'), 8622 array($this->getTestId()) 8623 ); 8624 8625 $questions = array(); 8626 8627 while ($row = $ilDB->fetchAssoc($query_result)) { 8628 $question = $row; 8629 8630 $question['obligationPossible'] = self::isQuestionObligationPossible($row['question_id']); 8631 8632 $questions[] = $question; 8633 } 8634 8635 return $questions; 8636 } 8637 8638 /** 8639 * Returns the status of the shuffle_questions variable 8640 * 8641 * @return integer 0 if the test questions are not shuffled, 1 if the test questions are shuffled 8642 * @access public 8643 */ 8644 public function getShuffleQuestions() 8645 { 8646 return ($this->shuffle_questions) ? 1 : 0; 8647 } 8648 8649 /** 8650 * Sets the status of the shuffle_questions variable 8651 * 8652 * @param boolean $a_shuffle 0 if the test questions are not shuffled, 1 if the test questions are shuffled 8653 * @access public 8654 */ 8655 public function setShuffleQuestions($a_shuffle) 8656 { 8657 $this->shuffle_questions = ($a_shuffle) ? 1 : 0; 8658 } 8659 8660 /** 8661 * Returns the settings for the list of questions options in the test properties 8662 * This could contain one of the following values: 8663 * 0 = No list of questions offered 8664 * 1 = A list of questions is offered 8665 * 3 = A list of questions is offered and the list of questions is shown as first page of the test 8666 * 5 = A list of questions is offered and the list of questions is shown as last page of the test 8667 * 7 = A list of questions is offered and the list of questions is shown as first and last page of the test 8668 * 8669 * @return integer TRUE if the list of questions should be presented, FALSE otherwise 8670 * @access public 8671 */ 8672 public function getListOfQuestionsSettings() 8673 { 8674 return ($this->show_summary) ? $this->show_summary : 0; 8675 } 8676 8677 /** 8678 * Sets the settings for the list of questions options in the test properties 8679 * This could contain one of the following values: 8680 * 0 = No list of questions offered 8681 * 1 = A list of questions is offered 8682 * 3 = A list of questions is offered and the list of questions is shown as first page of the test 8683 * 5 = A list of questions is offered and the list of questions is shown as last page of the test 8684 * 7 = A list of questions is offered and the list of questions is shown as first and last page of the test 8685 * 8686 * @param integer $a_value 0, 1, 3, 5 or 7 8687 * @access public 8688 */ 8689 public function setListOfQuestionsSettings($a_value = 0) 8690 { 8691 $this->show_summary = $a_value; 8692 } 8693 8694 /** 8695 * Returns if the list of questions should be presented to the user or not 8696 * 8697 * @return boolean TRUE if the list of questions should be presented, FALSE otherwise 8698 * @access public 8699 */ 8700 public function getListOfQuestions() 8701 { 8702 if (($this->show_summary & 1) > 0) { 8703 return true; 8704 } else { 8705 return false; 8706 } 8707 } 8708 8709 /** 8710 * Sets if the the list of questions should be presented to the user or not 8711 * 8712 * @param boolean $a_value TRUE if the list of questions should be presented, FALSE otherwise 8713 * @access public 8714 */ 8715 public function setListOfQuestions($a_value = true) 8716 { 8717 if ($a_value) { 8718 $this->show_summary = 1; 8719 } else { 8720 $this->show_summary = 0; 8721 } 8722 } 8723 8724 /** 8725 * Returns if the list of questions should be presented as the first page of the test 8726 * 8727 * @return boolean TRUE if the list of questions is shown as first page of the test, FALSE otherwise 8728 * @access public 8729 */ 8730 public function getListOfQuestionsStart() 8731 { 8732 if (($this->show_summary & 2) > 0) { 8733 return true; 8734 } else { 8735 return false; 8736 } 8737 } 8738 8739 /** 8740 * Sets if the the list of questions as the start page of the test 8741 * 8742 * @param boolean $a_value TRUE if the list of questions should be the start page, FALSE otherwise 8743 * @access public 8744 */ 8745 public function setListOfQuestionsStart($a_value = true) 8746 { 8747 if ($a_value && $this->getListOfQuestions()) { 8748 $this->show_summary = $this->show_summary | 2; 8749 } 8750 if (!$a_value && $this->getListOfQuestions()) { 8751 if ($this->getListOfQuestionsStart()) { 8752 $this->show_summary = $this->show_summary ^ 2; 8753 } 8754 } 8755 } 8756 8757 /** 8758 * Returns if the list of questions should be presented as the last page of the test 8759 * 8760 * @return boolean TRUE if the list of questions is shown as last page of the test, FALSE otherwise 8761 * @access public 8762 */ 8763 public function getListOfQuestionsEnd() 8764 { 8765 if (($this->show_summary & 4) > 0) { 8766 return true; 8767 } else { 8768 return false; 8769 } 8770 } 8771 8772 /** 8773 * Sets if the the list of questions as the end page of the test 8774 * 8775 * @param boolean $a_value TRUE if the list of questions should be the end page, FALSE otherwise 8776 * @access public 8777 */ 8778 public function setListOfQuestionsEnd($a_value = true) 8779 { 8780 if ($a_value && $this->getListOfQuestions()) { 8781 $this->show_summary = $this->show_summary | 4; 8782 } 8783 if (!$a_value && $this->getListOfQuestions()) { 8784 if ($this->getListOfQuestionsEnd()) { 8785 $this->show_summary = $this->show_summary ^ 4; 8786 } 8787 } 8788 } 8789 8790 /** 8791 * Returns TRUE if the list of questions should be presented with the question descriptions 8792 * 8793 * @return boolean TRUE if the list of questions is shown with the question descriptions, FALSE otherwise 8794 * @access public 8795 */ 8796 public function getListOfQuestionsDescription() 8797 { 8798 if (($this->show_summary & 8) > 0) { 8799 return true; 8800 } else { 8801 return false; 8802 } 8803 } 8804 8805 /** 8806 * Sets the show_summary attribute to TRUE if the list of questions should be presented with the question descriptions 8807 * 8808 * @param boolean $a_value TRUE if the list of questions should be shown with question descriptions, FALSE otherwise 8809 * @access public 8810 */ 8811 public function setListOfQuestionsDescription($a_value = true) 8812 { 8813 if ($a_value && $this->getListOfQuestions()) { 8814 $this->show_summary = $this->show_summary | 8; 8815 } 8816 if (!$a_value && $this->getListOfQuestions()) { 8817 if ($this->getListOfQuestionsDescription()) { 8818 $this->show_summary = $this->show_summary ^ 8; 8819 } 8820 } 8821 } 8822 8823 /** 8824 * Returns the combined results presentation value 8825 * 8826 * @return integer The combined results presentation value 8827 * @access public 8828 */ 8829 public function getResultsPresentation() 8830 { 8831 return ($this->results_presentation) ? $this->results_presentation : 0; 8832 } 8833 8834 /** 8835 * Returns if the pass details should be shown when a test is not finished 8836 * 8837 * @return boolean TRUE if the pass details should be shown, FALSE otherwise 8838 * @access public 8839 */ 8840 public function getShowPassDetails() 8841 { 8842 if (($this->results_presentation & 1) > 0) { 8843 return true; 8844 } else { 8845 return false; 8846 } 8847 } 8848 8849 /** 8850 * Returns if the solution details should be presented to the user or not 8851 * 8852 * @return boolean TRUE if the solution details should be presented, FALSE otherwise 8853 * @access public 8854 */ 8855 public function getShowSolutionDetails() 8856 { 8857 if (($this->results_presentation & 2) > 0) { 8858 return true; 8859 } else { 8860 return false; 8861 } 8862 } 8863 8864 /** 8865 * Returns if the solution printview should be presented to the user or not 8866 * 8867 * @return boolean TRUE if the solution printview should be presented, FALSE otherwise 8868 * @access public 8869 */ 8870 public function getShowSolutionPrintview() 8871 { 8872 if (($this->results_presentation & 4) > 0) { 8873 return true; 8874 } else { 8875 return false; 8876 } 8877 } 8878 8879 /** 8880 * Returns if the feedback should be presented to the solution or not 8881 * 8882 * @return boolean TRUE if the feedback should be presented in the solution, FALSE otherwise 8883 * @access public 8884 */ 8885 public function getShowSolutionFeedback() 8886 { 8887 if (($this->results_presentation & 8) > 0) { 8888 return true; 8889 } else { 8890 return false; 8891 } 8892 } 8893 8894 /** 8895 * Returns if the full solution (including ILIAS content) should be presented to the solution or not 8896 * 8897 * @return boolean TRUE if the full solution should be presented in the solution output, FALSE otherwise 8898 * @access public 8899 */ 8900 public function getShowSolutionAnswersOnly() 8901 { 8902 if (($this->results_presentation & 16) > 0) { 8903 return true; 8904 } else { 8905 return false; 8906 } 8907 } 8908 8909 /** 8910 * Returns if the signature field should be shown in the test results 8911 * 8912 * @return boolean TRUE if the signature field should be shown, FALSE otherwise 8913 * @access public 8914 */ 8915 public function getShowSolutionSignature() 8916 { 8917 if (($this->results_presentation & 32) > 0) { 8918 return true; 8919 } else { 8920 return false; 8921 } 8922 } 8923 8924 /** 8925 * @return boolean TRUE if the suggested solutions should be shown, FALSE otherwise 8926 * @access public 8927 */ 8928 public function getShowSolutionSuggested() 8929 { 8930 if (($this->results_presentation & 64) > 0) { 8931 return true; 8932 } else { 8933 return false; 8934 } 8935 } 8936 8937 /** 8938 * @return boolean TRUE if the results should be compared with the correct results in the list of answers, FALSE otherwise 8939 * @access public 8940 */ 8941 public function getShowSolutionListComparison() 8942 { 8943 if (($this->results_presentation & 128) > 0) { 8944 return true; 8945 } else { 8946 return false; 8947 } 8948 } 8949 8950 /** 8951 * Sets the combined results presentation value 8952 * 8953 * @param integer $a_results_presentation The combined results presentation value 8954 * @access public 8955 */ 8956 public function setResultsPresentation($a_results_presentation = 3) 8957 { 8958 $this->results_presentation = $a_results_presentation; 8959 } 8960 8961 /** 8962 * Sets if the pass details should be shown when a test is not finished 8963 * 8964 * Sets if the pass details should be shown when a test is not finished 8965 * 8966 * @param boolean $a_details TRUE if the pass details should be shown, FALSE otherwise 8967 * @access public 8968 */ 8969 public function setShowPassDetails($a_details = 1) 8970 { 8971 if ($a_details) { 8972 $this->results_presentation = $this->results_presentation | 1; 8973 } else { 8974 if ($this->getShowPassDetails()) { 8975 $this->results_presentation = $this->results_presentation ^ 1; 8976 } 8977 } 8978 } 8979 8980 /** 8981 * Sets if the the solution details should be presented to the user or not 8982 * 8983 * @param integer $a_details 1 if the solution details should be presented, 0 otherwise 8984 * @access public 8985 */ 8986 public function setShowSolutionDetails($a_details = 1) 8987 { 8988 if ($a_details) { 8989 $this->results_presentation = $this->results_presentation | 2; 8990 } else { 8991 if ($this->getShowSolutionDetails()) { 8992 $this->results_presentation = $this->results_presentation ^ 2; 8993 } 8994 } 8995 } 8996 8997 /** 8998 * Calculates if a user may see the solution printview of his/her test results 8999 * 9000 * @return boolean TRUE if the user may see the printview, FALSE otherwise 9001 * @access public 9002 */ 9003 public function canShowSolutionPrintview($user_id = null) 9004 { 9005 return $this->getShowSolutionPrintview(); 9006 } 9007 9008 /** 9009 * Sets if the the solution printview should be presented to the user or not 9010 * 9011 * @param boolean $a_details TRUE if the solution printview should be presented, FALSE otherwise 9012 * @access public 9013 */ 9014 public function setShowSolutionPrintview($a_printview = 1) 9015 { 9016 if ($a_printview) { 9017 $this->results_presentation = $this->results_presentation | 4; 9018 } else { 9019 if ($this->getShowSolutionPrintview()) { 9020 $this->results_presentation = $this->results_presentation ^ 4; 9021 } 9022 } 9023 } 9024 9025 /** 9026 * Sets if the the feedback should be presented to the user in the solution or not 9027 * 9028 * @param boolean $a_feedback TRUE if the feedback should be presented in the solution, FALSE otherwise 9029 * @access public 9030 */ 9031 public function setShowSolutionFeedback($a_feedback = true) 9032 { 9033 if ($a_feedback) { 9034 $this->results_presentation = $this->results_presentation | 8; 9035 } else { 9036 if ($this->getShowSolutionFeedback()) { 9037 $this->results_presentation = $this->results_presentation ^ 8; 9038 } 9039 } 9040 } 9041 9042 /** 9043 * Set to true, if the full solution (including the ILIAS content pages) should be shown in the solution output 9044 * 9045 * @param boolean $a_full TRUE if the full solution should be shown in the solution output, FALSE otherwise 9046 * @access public 9047 */ 9048 public function setShowSolutionAnswersOnly($a_full = true) 9049 { 9050 if ($a_full) { 9051 $this->results_presentation = $this->results_presentation | 16; 9052 } else { 9053 if ($this->getShowSolutionAnswersOnly()) { 9054 $this->results_presentation = $this->results_presentation ^ 16; 9055 } 9056 } 9057 } 9058 9059 /** 9060 * Set to TRUE, if the signature field should be shown in the solution 9061 * 9062 * @param boolean $a_signature TRUE if the signature field should be shown, FALSE otherwise 9063 * @access public 9064 */ 9065 public function setShowSolutionSignature($a_signature = false) 9066 { 9067 if ($a_signature) { 9068 $this->results_presentation = $this->results_presentation | 32; 9069 } else { 9070 if ($this->getShowSolutionSignature()) { 9071 $this->results_presentation = $this->results_presentation ^ 32; 9072 } 9073 } 9074 } 9075 9076 /** 9077 * Set to TRUE, if the suggested solution should be shown in the solution 9078 * 9079 * @param boolean $a_solution TRUE if the suggested solution should be shown, FALSE otherwise 9080 * @access public 9081 */ 9082 public function setShowSolutionSuggested($a_solution = false) 9083 { 9084 if ($a_solution) { 9085 $this->results_presentation = $this->results_presentation | 64; 9086 } else { 9087 if ($this->getShowSolutionSuggested()) { 9088 $this->results_presentation = $this->results_presentation ^ 64; 9089 } 9090 } 9091 } 9092 9093 /** 9094 * Set to TRUE, if the list of answers should be shown prior to finish the test 9095 * 9096 * @param boolean $a_comparison TRUE if the list of answers should be shown prior to finish the test, FALSE otherwise 9097 */ 9098 public function setShowSolutionListComparison($a_comparison = false) 9099 { 9100 if ($a_comparison) { 9101 $this->results_presentation = $this->results_presentation | 128; 9102 } else { 9103 if ($this->getShowSolutionListComparison()) { 9104 $this->results_presentation = $this->results_presentation ^ 128; 9105 } 9106 } 9107 } 9108 9109 /** 9110 * @deprecated: use ilTestParticipantData instead 9111 */ 9112 public static function _getUserIdFromActiveId($active_id) 9113 { 9114 global $DIC; 9115 $ilDB = $DIC['ilDB']; 9116 $result = $ilDB->queryF( 9117 "SELECT user_fi FROM tst_active WHERE active_id = %s", 9118 array('integer'), 9119 array($active_id) 9120 ); 9121 if ($result->numRows()) { 9122 $row = $ilDB->fetchAssoc($result); 9123 return $row["user_fi"]; 9124 } else { 9125 return -1; 9126 } 9127 } 9128 9129 /** 9130 * @return boolean 9131 */ 9132 public function isLimitUsersEnabled() 9133 { 9134 return $this->limitUsersEnabled; 9135 } 9136 9137 /** 9138 * @param boolean $limitUsersEnabled 9139 */ 9140 public function setLimitUsersEnabled($limitUsersEnabled) 9141 { 9142 $this->limitUsersEnabled = $limitUsersEnabled; 9143 } 9144 9145 public function getAllowedUsers() 9146 { 9147 return ($this->allowedUsers) ? $this->allowedUsers : 0; 9148 } 9149 9150 public function setAllowedUsers($a_allowed_users) 9151 { 9152 $this->allowedUsers = $a_allowed_users; 9153 } 9154 9155 public function getAllowedUsersTimeGap() 9156 { 9157 return ($this->allowedUsersTimeGap) ? $this->allowedUsersTimeGap : 0; 9158 } 9159 9160 public function setAllowedUsersTimeGap($a_allowed_users_time_gap) 9161 { 9162 $this->allowedUsersTimeGap = $a_allowed_users_time_gap; 9163 } 9164 9165 public function checkMaximumAllowedUsers() 9166 { 9167 global $DIC; 9168 $ilDB = $DIC['ilDB']; 9169 9170 $nr_of_users = $this->getAllowedUsers(); 9171 $time_gap = ($this->getAllowedUsersTimeGap()) ? $this->getAllowedUsersTimeGap() : 60; 9172 if (($nr_of_users > 0) && ($time_gap > 0)) { 9173 $now = time(); 9174 $time_border = $now - $time_gap; 9175 $str_time_border = strftime("%Y%m%d%H%M%S", $time_border); 9176 $query = " 9177 SELECT DISTINCT tst_times.active_fi 9178 FROM tst_times 9179 INNER JOIN tst_active 9180 ON tst_times.active_fi = tst_active.active_id 9181 AND ( 9182 tst_times.pass > tst_active.last_finished_pass OR tst_active.last_finished_pass IS NULL 9183 ) 9184 WHERE tst_times.tstamp > %s 9185 AND tst_active.test_fi = %s 9186 "; 9187 $result = $ilDB->queryF($query, array('integer', 'integer'), array($time_border, $this->getTestId())); 9188 if ($result->numRows() >= $nr_of_users) { 9189 include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php"; 9190 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 9191 $this->logAction($this->lng->txtlng("assessment", "log_could_not_enter_test_due_to_simultaneous_users", ilObjAssessmentFolder::_getLogLanguage())); 9192 } 9193 return false; 9194 } else { 9195 return true; 9196 } 9197 } 9198 return true; 9199 } 9200 9201 public function _getLastAccess($active_id) 9202 { 9203 global $DIC; 9204 $ilDB = $DIC['ilDB']; 9205 9206 $result = $ilDB->queryF( 9207 "SELECT finished FROM tst_times WHERE active_fi = %s ORDER BY finished DESC", 9208 array('integer'), 9209 array($active_id) 9210 ); 9211 if ($result->numRows()) { 9212 $row = $ilDB->fetchAssoc($result); 9213 return $row["finished"]; 9214 } 9215 return ""; 9216 } 9217 9218 public static function lookupLastTestPassAccess($activeId, $passIndex) 9219 { 9220 global $DIC; /* @var \ILIAS\DI\Container $DIC */ 9221 9222 $query = " 9223 SELECT MAX(tst_times.tstamp) as last_pass_access 9224 FROM tst_times 9225 WHERE active_fi = %s 9226 AND pass = %s 9227 "; 9228 9229 $res = $DIC->database()->queryF( 9230 $query, 9231 array('integer', 'integer'), 9232 array($activeId, $passIndex) 9233 ); 9234 9235 while ($row = $DIC->database()->fetchAssoc($res)) { 9236 return $row['last_pass_access']; 9237 } 9238 9239 return null; 9240 } 9241 9242 /** 9243 * Checks if a given string contains HTML or not 9244 * 9245 * @param string $a_text Text which should be checked 9246 * @return boolean 9247 * @access public 9248 */ 9249 public function isHTML($a_text) 9250 { 9251 if (preg_match("/<[^>]*?>/", $a_text)) { 9252 return true; 9253 } else { 9254 return false; 9255 } 9256 } 9257 9258 /** 9259 * Reads an QTI material tag an creates a text string 9260 * 9261 * @param string $a_material QTI material tag 9262 * @return string text or xhtml string 9263 * @access public 9264 */ 9265 public function QTIMaterialToString($a_material) 9266 { 9267 $result = ""; 9268 for ($i = 0; $i < $a_material->getMaterialCount(); $i++) { 9269 $material = $a_material->getMaterial($i); 9270 if (strcmp($material["type"], "mattext") == 0) { 9271 $result .= $material["material"]->getContent(); 9272 } 9273 if (strcmp($material["type"], "matimage") == 0) { 9274 $matimage = $material["material"]; 9275 if (preg_match("/(il_([0-9]+)_mob_([0-9]+))/", $matimage->getLabel(), $matches)) { 9276 // import an mediaobject which was inserted using tiny mce 9277 if (!is_array($_SESSION["import_mob_xhtml"])) { 9278 $_SESSION["import_mob_xhtml"] = array(); 9279 } 9280 array_push($_SESSION["import_mob_xhtml"], array("mob" => $matimage->getLabel(), "uri" => $matimage->getUri())); 9281 } 9282 } 9283 } 9284 global $DIC; 9285 $ilLog = $DIC['ilLog']; 9286 $ilLog->write(print_r($_SESSION["import_mob_xhtml"], true)); 9287 return $result; 9288 } 9289 9290 /** 9291 * Creates a QTI material tag from a plain text or xhtml text 9292 * 9293 * @param object $a_xml_writer Reference to the ILIAS XML writer 9294 * @param string $a_material plain text or html text containing the material 9295 * @return string QTI material tag 9296 * @access public 9297 */ 9298 public function addQTIMaterial(&$a_xml_writer, $a_material) 9299 { 9300 include_once "./Services/RTE/classes/class.ilRTE.php"; 9301 include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php"); 9302 9303 $a_xml_writer->xmlStartTag("material"); 9304 $attrs = array( 9305 "texttype" => "text/plain" 9306 ); 9307 if ($this->isHTML($a_material)) { 9308 $attrs["texttype"] = "text/xhtml"; 9309 } 9310 $a_xml_writer->xmlElement("mattext", $attrs, ilRTE::_replaceMediaObjectImageSrc($a_material, 0)); 9311 9312 $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId()); 9313 foreach ($mobs as $mob) { 9314 $moblabel = "il_" . IL_INST_ID . "_mob_" . $mob; 9315 if (strpos($a_material, "mm_$mob") !== false) { 9316 if (ilObjMediaObject::_exists($mob)) { 9317 $mob_obj = new ilObjMediaObject($mob); 9318 $imgattrs = array( 9319 "label" => $moblabel, 9320 "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle() 9321 ); 9322 } 9323 $a_xml_writer->xmlElement("matimage", $imgattrs, null); 9324 } 9325 } 9326 $a_xml_writer->xmlEndTag("material"); 9327 } 9328 9329 /** 9330 * Prepares a string for a text area output in tests 9331 * 9332 * @param string $txt_output String which should be prepared for output 9333 * @access public 9334 */ 9335 public function prepareTextareaOutput($txt_output, $prepare_for_latex_output = false, $omitNl2BrWhenTextArea = false) 9336 { 9337 include_once "./Services/Utilities/classes/class.ilUtil.php"; 9338 return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output, $omitNl2BrWhenTextArea); 9339 } 9340 9341 /** 9342 * Saves the visibility settings of the certificate 9343 * 9344 * @param integer $a_value The value for the visibility settings (0 = always, 1 = only passed, 2 = never) 9345 * @access private 9346 */ 9347 public function saveCertificateVisibility($a_value) 9348 { 9349 global $DIC; 9350 $ilDB = $DIC['ilDB']; 9351 9352 $affectedRows = $ilDB->manipulateF( 9353 "UPDATE tst_tests SET certificate_visibility = %s, tstamp = %s WHERE test_id = %s", 9354 array('text', 'integer', 'integer'), 9355 array($a_value, time(), $this->getTestId()) 9356 ); 9357 } 9358 9359 /** 9360 * Returns the visibility settings of the certificate 9361 * 9362 * @return integer The value for the visibility settings (0 = always, 1 = only passed, 2 = never) 9363 * @access public 9364 */ 9365 public function getCertificateVisibility() 9366 { 9367 return (strlen($this->certificate_visibility)) ? $this->certificate_visibility : 0; 9368 } 9369 9370 /** 9371 * Sets the visibility settings of the certificate 9372 * 9373 * @param integer $a_value The value for the visibility settings (0 = always, 1 = only passed, 2 = never) 9374 * @access public 9375 */ 9376 public function setCertificateVisibility($a_value) 9377 { 9378 $this->certificate_visibility = $a_value; 9379 } 9380 9381 /** 9382 * Returns the anonymity status of the test 9383 * 9384 * @return integer The value for the anonymity status (0 = personalized, 1 = anonymized) 9385 * @access public 9386 */ 9387 public function getAnonymity() 9388 { 9389 return ($this->anonymity) ? 1 : 0; 9390 } 9391 9392 /** 9393 * Sets the anonymity status of the test 9394 * 9395 * @param integer $a_value The value for the anonymity status (0 = personalized, 1 = anonymized) 9396 * @access public 9397 */ 9398 public function setAnonymity($a_value = 0) 9399 { 9400 switch ($a_value) { 9401 case 1: 9402 $this->anonymity = 1; 9403 break; 9404 default: 9405 $this->anonymity = 0; 9406 break; 9407 } 9408 } 9409 9410 /** 9411 * Returns wheather the cancel test button is shown or not 9412 * 9413 * @return integer The value for the show cancel status (0 = don't show, 1 = show) 9414 * @access public 9415 */ 9416 public function getShowCancel() 9417 { 9418 return ($this->show_cancel) ? 1 : 0; 9419 } 9420 9421 /** 9422 * Sets the cancel test button status 9423 * 9424 * @param integer $a_value The value for the cancel test status (0 = don't show, 1 = show) 9425 * @access public 9426 */ 9427 public function setShowCancel($a_value = 1) 9428 { 9429 switch ($a_value) { 9430 case 1: 9431 $this->show_cancel = 1; 9432 break; 9433 default: 9434 $this->show_cancel = 0; 9435 break; 9436 } 9437 } 9438 9439 /** 9440 * Returns wheather the marker button is shown or not 9441 * 9442 * @return integer The value for the marker status (0 = don't show, 1 = show) 9443 * @access public 9444 */ 9445 public function getShowMarker() 9446 { 9447 return ($this->show_marker) ? 1 : 0; 9448 } 9449 9450 /** 9451 * Sets the marker button status 9452 * 9453 * @param integer $a_value The value for the marker status (0 = don't show, 1 = show) 9454 * @access public 9455 */ 9456 public function setShowMarker($a_value = 1) 9457 { 9458 switch ($a_value) { 9459 case 1: 9460 $this->show_marker = 1; 9461 break; 9462 default: 9463 $this->show_marker = 0; 9464 break; 9465 } 9466 } 9467 9468 /** 9469 * Returns the fixed participants status 9470 * 9471 * @return integer The value for the fixed participants status (0 = don't allow, 1 = allow) 9472 * @access public 9473 */ 9474 public function getFixedParticipants() 9475 { 9476 return ($this->fixed_participants) ? 1 : 0; 9477 } 9478 9479 /** 9480 * Sets the fixed participants status 9481 * 9482 * @param integer $a_value The value for the fixed participants status (0 = don't allow, 1 = allow) 9483 * @access public 9484 */ 9485 public function setFixedParticipants($a_value = 1) 9486 { 9487 switch ($a_value) { 9488 case 1: 9489 $this->fixed_participants = 1; 9490 break; 9491 default: 9492 $this->fixed_participants = 0; 9493 break; 9494 } 9495 } 9496 9497 /** 9498 * Returns the anonymity status of a test with a given object id 9499 * 9500 * @param int $a_obj_id The object id of the test object 9501 * @return integer The value for the anonymity status (0 = personalized, 1 = anonymized) 9502 * @access public 9503 */ 9504 public static function _lookupAnonymity($a_obj_id) 9505 { 9506 global $DIC; 9507 $ilDB = $DIC['ilDB']; 9508 9509 $result = $ilDB->queryF( 9510 "SELECT anonymity FROM tst_tests WHERE obj_fi = %s", 9511 array('integer'), 9512 array($a_obj_id) 9513 ); 9514 while ($row = $ilDB->fetchAssoc($result)) { 9515 return $row['anonymity']; 9516 } 9517 return 0; 9518 } 9519 9520 /** 9521 * returns the question set type of test relating to passed active id 9522 * 9523 * @param integer $activeId 9524 * @return string $questionSetType 9525 */ 9526 public static function lookupQuestionSetTypeByActiveId($active_id) 9527 { 9528 global $DIC; 9529 $ilDB = $DIC['ilDB']; 9530 9531 $query = " 9532 SELECT tst_tests.question_set_type 9533 FROM tst_active 9534 INNER JOIN tst_tests 9535 ON tst_active.test_fi = tst_tests.test_id 9536 WHERE tst_active.active_id = %s 9537 "; 9538 9539 $res = $ilDB->queryF($query, array('integer'), array($active_id)); 9540 9541 while ($row = $ilDB->fetchAssoc($res)) { 9542 return $row['question_set_type']; 9543 } 9544 9545 return null; 9546 } 9547 9548 /** 9549 * Returns the random status of a test with a given object id 9550 * 9551 * @param int $a_obj_id The object id of the test object 9552 * @return integer The value for the anonymity status (0 = no random, 1 = random) 9553 * @access public 9554 * @deprecated 9555 */ 9556 public function _lookupRandomTestFromActiveId($active_id) 9557 { 9558 throw new Exception(__METHOD__ . ' is deprecated ... use ilObjTest::lookupQuestionSetTypeByActiveId() instead!'); 9559 9560 global $DIC; 9561 $ilDB = $DIC['ilDB']; 9562 9563 $result = $ilDB->queryF( 9564 "SELECT tst_tests.random_test FROM tst_tests, tst_active WHERE tst_active.active_id = %s AND tst_active.test_fi = tst_tests.test_id", 9565 array('integer'), 9566 array($active_id) 9567 ); 9568 while ($row = $ilDB->fetchAssoc($result)) { 9569 return $row['random_test']; 9570 } 9571 return 0; 9572 } 9573 9574 /** 9575 * Returns the full name of a test user according to the anonymity status 9576 * 9577 * @param int $user_id The database ID of the user 9578 * @param boolean $overwrite_anonymity Indicates if the anonymity status should be ignored 9579 * @return string The full name of the user or UNKNOWN if the anonymity status is affected 9580 * @access public 9581 * 9582 * @deprecated: use ilTestParticipantData instead 9583 */ 9584 public function userLookupFullName($user_id, $overwrite_anonymity = false, $sorted_order = false, $suffix = "") 9585 { 9586 if ($this->getAnonymity() && !$overwrite_anonymity) { 9587 return $this->lng->txt("anonymous") . $suffix; 9588 } else { 9589 include_once './Services/User/classes/class.ilObjUser.php'; 9590 $uname = ilObjUser::_lookupName($user_id); 9591 if (strlen($uname["firstname"] . $uname["lastname"]) == 0) { 9592 $uname["firstname"] = $this->lng->txt("deleted_user"); 9593 } 9594 if ($sorted_order) { 9595 return trim($uname["lastname"] . ", " . $uname["firstname"]) . $suffix; 9596 } else { 9597 return trim($uname["firstname"] . " " . $uname["lastname"]) . $suffix; 9598 } 9599 } 9600 } 9601 9602 /** 9603 * Returns the "Start the Test" label for the Info page 9604 * 9605 * @param int $active_id The active id of the current user 9606 * @return string The "Start the Test" label 9607 * @access public 9608 */ 9609 public function getStartTestLabel($active_id) 9610 { 9611 if ($this->getNrOfTries() == 1) { 9612 return $this->lng->txt("tst_start_test"); 9613 } 9614 $active_pass = self::_getPass($active_id); 9615 $res = $this->getNrOfResultsForPass($active_id, $active_pass); 9616 if ($res == 0) { 9617 if ($active_pass == 0) { 9618 return $this->lng->txt("tst_start_test"); 9619 } else { 9620 return $this->lng->txt("tst_start_new_test_pass"); 9621 } 9622 } else { 9623 return $this->lng->txt("tst_resume_test"); 9624 } 9625 } 9626 9627 /** 9628 * Returns the available test defaults for the active user 9629 * @return array An array containing the defaults 9630 * @access public 9631 */ 9632 public function getAvailableDefaults() 9633 { 9634 /** 9635 * @var $ilDB ilDBInterface 9636 * @var $ilUser ilObjUser 9637 */ 9638 global $DIC; 9639 $ilDB = $DIC['ilDB']; 9640 $ilUser = $DIC['ilUser']; 9641 9642 $result = $ilDB->queryF( 9643 "SELECT * FROM tst_test_defaults WHERE user_fi = %s ORDER BY name ASC", 9644 array('integer'), 9645 array($ilUser->getId()) 9646 ); 9647 $defaults = array(); 9648 while ($row = $ilDB->fetchAssoc($result)) { 9649 $defaults[$row["test_defaults_id"]] = $row; 9650 } 9651 return $defaults; 9652 } 9653 9654 /** 9655 * Returns the test defaults for a given id 9656 * 9657 * @param integer $test_defaults_id The database id of a test defaults dataset 9658 * @return array An array containing the test defaults 9659 * @access public 9660 */ 9661 public function &getTestDefaults($test_defaults_id) 9662 { 9663 return self::_getTestDefaults($test_defaults_id); 9664 } 9665 9666 public static function _getTestDefaults($test_defaults_id) 9667 { 9668 global $DIC; 9669 $ilDB = $DIC['ilDB']; 9670 9671 $result = $ilDB->queryF( 9672 "SELECT * FROM tst_test_defaults WHERE test_defaults_id = %s", 9673 array('integer'), 9674 array($test_defaults_id) 9675 ); 9676 if ($result->numRows() == 1) { 9677 $row = $ilDB->fetchAssoc($result); 9678 return $row; 9679 } else { 9680 return null; 9681 } 9682 } 9683 9684 /** 9685 * Deletes the defaults for a test 9686 * 9687 * @param integer $test_default_id The database ID of the test defaults 9688 * @access public 9689 */ 9690 public function deleteDefaults($test_default_id) 9691 { 9692 global $DIC; 9693 $ilDB = $DIC['ilDB']; 9694 $affectedRows = $ilDB->manipulateF( 9695 "DELETE FROM tst_test_defaults WHERE test_defaults_id = %s", 9696 array('integer'), 9697 array($test_default_id) 9698 ); 9699 } 9700 9701 /** 9702 * Adds the defaults of this test to the test defaults 9703 * 9704 * @param string $a_name The name of the test defaults 9705 * @access public 9706 */ 9707 public function addDefaults($a_name) 9708 { 9709 global $DIC; 9710 $ilDB = $DIC['ilDB']; 9711 $ilUser = $DIC['ilUser']; 9712 $testsettings = array( 9713 "TitleOutput" => $this->getTitleOutput(), 9714 "PassScoring" => $this->getPassScoring(), 9715 "IntroEnabled" => $this->isIntroductionEnabled(), 9716 "Introduction" => $this->getIntroduction(), 9717 "FinalStatement" => $this->getFinalStatement(), 9718 "ShowInfo" => $this->getShowInfo(), 9719 "ForceJS" => $this->getForceJS(), 9720 "CustomStyle" => $this->getCustomStyle(), 9721 "ShowFinalStatement" => $this->getShowFinalStatement(), 9722 "SequenceSettings" => $this->getSequenceSettings(), 9723 "ScoreReporting" => $this->getScoreReporting(), 9724 "ScoreCutting" => $this->getScoreCutting(), 9725 'SpecificAnswerFeedback' => $this->getSpecificAnswerFeedback(), 9726 'PrintBsWithRes' => (int) $this->isBestSolutionPrintedWithResult(), 9727 "InstantFeedbackSolution" => $this->getInstantFeedbackSolution(), 9728 "AnswerFeedback" => $this->getAnswerFeedback(), 9729 "AnswerFeedbackPoints" => $this->getAnswerFeedbackPoints(), 9730 "ResultsPresentation" => $this->getResultsPresentation(), 9731 "Anonymity" => $this->getAnonymity(), 9732 "ShowCancel" => $this->getShowCancel(), 9733 "ShowMarker" => $this->getShowMarker(), 9734 "ReportingDate" => $this->getReportingDate(), 9735 "NrOfTries" => $this->getNrOfTries(), 9736 'BlockAfterPassed' => (int) $this->isBlockPassesAfterPassedEnabled(), 9737 "Shuffle" => $this->getShuffleQuestions(), 9738 "Kiosk" => $this->getKiosk(), 9739 "UsePreviousAnswers" => $this->getUsePreviousAnswers(), 9740 "ProcessingTime" => $this->getProcessingTime(), 9741 "EnableProcessingTime" => $this->getEnableProcessingTime(), 9742 "ResetProcessingTime" => $this->getResetProcessingTime(), 9743 "StartingTimeEnabled" => $this->isStartingTimeEnabled(), 9744 "StartingTime" => $this->getStartingTime(), 9745 "EndingTimeEnabled" => $this->isEndingTimeEnabled(), 9746 "EndingTime" => $this->getEndingTime(), 9747 "ECTSOutput" => $this->getECTSOutput(), 9748 "ECTSFX" => $this->getECTSFX(), 9749 "ECTSGrades" => $this->getECTSGrades(), 9750 "questionSetType" => $this->getQuestionSetType(), 9751 "CountSystem" => $this->getCountSystem(), 9752 "MCScoring" => $this->getMCScoring(), 9753 "mailnotification" => $this->getMailNotification(), 9754 "mailnottype" => $this->getMailNotificationType(), 9755 "exportsettings" => $this->getExportSettings(), 9756 "ListOfQuestionsSettings" => $this->getListOfQuestionsSettings(), 9757 'obligations_enabled' => (int) $this->areObligationsEnabled(), 9758 'offer_question_hints' => (int) $this->isOfferingQuestionHintsEnabled(), 9759 'pass_deletion_allowed' => (int) $this->isPassDeletionAllowed(), 9760 'enable_examview' => $this->getEnableExamview(), 9761 'show_examview_html' => $this->getShowExamviewHtml(), 9762 'show_examview_pdf' => $this->getShowExamviewPdf(), 9763 'char_selector_availability' => $this->getCharSelectorAvailability(), 9764 'char_selector_definition' => $this->getCharSelectorDefinition(), 9765 'skill_service' => (int) $this->isSkillServiceEnabled(), 9766 'result_tax_filters' => (array) $this->getResultFilterTaxIds(), 9767 'show_grading_status' => (int) $this->isShowGradingStatusEnabled(), 9768 'show_grading_mark' => (int) $this->isShowGradingMarkEnabled(), 9769 9770 'follow_qst_answer_fixation' => $this->isFollowupQuestionAnswerFixationEnabled(), 9771 'inst_fb_answer_fixation' => $this->isInstantFeedbackAnswerFixationEnabled(), 9772 'force_inst_fb' => $this->isForceInstantFeedbackEnabled(), 9773 'redirection_mode' => $this->getRedirectionMode(), 9774 'redirection_url' => $this->getRedirectionUrl(), 9775 'sign_submission' => $this->getSignSubmission(), 9776 'autosave' => (int) $this->getAutosave(), 9777 'autosave_ival' => (int) $this->getAutosaveIval(), 9778 'examid_in_test_pass' => (int) $this->isShowExamIdInTestPassEnabled(), 9779 'examid_in_test_res' => (int) $this->isShowExamIdInTestResultsEnabled(), 9780 9781 'enable_archiving' => (int) $this->getEnableArchiving(), 9782 'password_enabled' => (int) $this->isPasswordEnabled(), 9783 'password' => (string) $this->getPassword(), 9784 'fixed_participants' => $this->getFixedParticipants(), 9785 'limit_users_enabled' => $this->isLimitUsersEnabled(), 9786 'allowedusers' => $this->getAllowedUsers(), 9787 'alloweduserstimegap' => $this->getAllowedUsersTimeGap(), 9788 'pool_usage' => $this->getPoolUsage(), 9789 'activation_limited' => $this->isActivationLimited(), 9790 'activation_start_time' => $this->getActivationStartingTime(), 9791 'activation_end_time' => $this->getActivationEndingTime(), 9792 'activation_visibility' => $this->getActivationVisibility(), 9793 'highscore_enabled' => $this->getHighscoreEnabled(), 9794 'highscore_anon' => $this->getHighscoreAnon(), 9795 'highscore_achieved_ts' => $this->getHighscoreAchievedTS(), 9796 'highscore_score' => $this->getHighscoreScore(), 9797 'highscore_percentage' => $this->getHighscorePercentage(), 9798 'highscore_hints' => $this->getHighscoreHints(), 9799 'highscore_wtime' => $this->getHighscoreWTime(), 9800 'highscore_own_table' => $this->getHighscoreOwnTable(), 9801 'highscore_top_table' => $this->getHighscoreTopTable(), 9802 'highscore_top_num' => $this->getHighscoreTopNum(), 9803 'use_previous_answers' => (string) $this->getUsePreviousAnswers(), 9804 'pass_waiting' => $this->getPassWaiting() 9805 ); 9806 9807 $next_id = $ilDB->nextId('tst_test_defaults'); 9808 $ilDB->insert( 9809 'tst_test_defaults', 9810 array( 9811 'test_defaults_id' => array('integer', $next_id), 9812 'name' => array('text', $a_name), 9813 'user_fi' => array('integer', $ilUser->getId()), 9814 'defaults' => array('clob', serialize($testsettings)), 9815 'marks' => array('clob', serialize($this->mark_schema)), 9816 'tstamp' => array('integer', time()) 9817 ) 9818 ); 9819 } 9820 9821 /** 9822 * Applies given test defaults to this test 9823 * 9824 * @param array $test_default The test defaults database id. 9825 * 9826 * @return boolean TRUE if the application succeeds, FALSE otherwise 9827 */ 9828 public function applyDefaults($test_defaults) 9829 { 9830 $testsettings = unserialize($test_defaults["defaults"]); 9831 include_once "./Modules/Test/classes/class.assMarkSchema.php"; 9832 $this->mark_schema = unserialize($test_defaults["marks"]); 9833 9834 $this->setTitleOutput($testsettings["TitleOutput"]); 9835 $this->setPassScoring($testsettings["PassScoring"]); 9836 $this->setIntroductionEnabled($testsettings["IntroEnabled"]); 9837 $this->setIntroduction($testsettings["Introduction"]); 9838 $this->setFinalStatement($testsettings["FinalStatement"]); 9839 $this->setShowInfo($testsettings["ShowInfo"]); 9840 $this->setForceJS($testsettings["ForceJS"]); 9841 $this->setCustomStyle($testsettings["CustomStyle"]); 9842 $this->setShowFinalStatement($testsettings["ShowFinalStatement"]); 9843 $this->setSequenceSettings($testsettings["SequenceSettings"]); 9844 $this->setScoreReporting($testsettings["ScoreReporting"]); 9845 $this->setScoreCutting($testsettings['ScoreCutting']); 9846 $this->setSpecificAnswerFeedback($testsettings['SpecificAnswerFeedback']); 9847 $this->setPrintBestSolutionWithResult((bool) $testsettings['PrintBsWithRes']); 9848 $this->setInstantFeedbackSolution($testsettings["InstantFeedbackSolution"]); 9849 $this->setAnswerFeedback($testsettings["AnswerFeedback"]); 9850 $this->setAnswerFeedbackPoints($testsettings["AnswerFeedbackPoints"]); 9851 $this->setResultsPresentation($testsettings["ResultsPresentation"]); 9852 $this->setAnonymity($testsettings["Anonymity"]); 9853 $this->setShowCancel($testsettings["ShowCancel"]); 9854 $this->setShuffleQuestions($testsettings["Shuffle"]); 9855 $this->setShowMarker($testsettings["ShowMarker"]); 9856 $this->setReportingDate($testsettings["ReportingDate"]); 9857 $this->setNrOfTries($testsettings["NrOfTries"]); 9858 $this->setBlockPassesAfterPassedEnabled((bool) $testsettings['BlockAfterPassed']); 9859 $this->setUsePreviousAnswers($testsettings["UsePreviousAnswers"]); 9860 $this->setRedirectionMode($testsettings['redirection_mode']); 9861 $this->setRedirectionUrl($testsettings['redirection_url']); 9862 $this->setProcessingTime($testsettings["ProcessingTime"]); 9863 $this->setResetProcessingTime($testsettings["ResetProcessingTime"]); 9864 $this->setEnableProcessingTime($testsettings["EnableProcessingTime"]); 9865 $this->setStartingTimeEnabled($testsettings["StartingTimeEnabled"]); 9866 $this->setStartingTime($testsettings["StartingTime"]); 9867 $this->setKiosk($testsettings["Kiosk"]); 9868 $this->setEndingTimeEnabled($testsettings["EndingTimeEnabled"]); 9869 $this->setEndingTime($testsettings["EndingTime"]); 9870 $this->setECTSOutput($testsettings["ECTSOutput"]); 9871 $this->setECTSFX($testsettings["ECTSFX"]); 9872 $this->setECTSGrades($testsettings["ECTSGrades"]); 9873 if (isset($testsettings["isRandomTest"])) { 9874 if ($testsettings["isRandomTest"]) { 9875 $this->setQuestionSetType(self::QUESTION_SET_TYPE_RANDOM); 9876 } else { 9877 $this->setQuestionSetType(self::QUESTION_SET_TYPE_FIXED); 9878 } 9879 } elseif (isset($testsettings["questionSetType"])) { 9880 $this->setQuestionSetType($testsettings["questionSetType"]); 9881 } 9882 $this->setCountSystem($testsettings["CountSystem"]); 9883 $this->setMCScoring($testsettings["MCScoring"]); 9884 $this->setMailNotification($testsettings["mailnotification"]); 9885 $this->setMailNotificationType($testsettings["mailnottype"]); 9886 $this->setExportSettings($testsettings['exportsettings']); 9887 $this->setListOfQuestionsSettings($testsettings["ListOfQuestionsSettings"]); 9888 $this->setObligationsEnabled($testsettings["obligations_enabled"]); 9889 $this->setOfferingQuestionHintsEnabled($testsettings["offer_question_hints"]); 9890 $this->setHighscoreEnabled($testsettings['highscore_enabled']); 9891 $this->setHighscoreAnon($testsettings['highscore_anon']); 9892 $this->setHighscoreAchievedTS($testsettings['highscore_achieved_ts']); 9893 $this->setHighscoreScore($testsettings['highscore_score']); 9894 $this->setHighscorePercentage($testsettings['highscore_percentage']); 9895 $this->setHighscoreHints($testsettings['highscore_hints']); 9896 $this->setHighscoreWTime($testsettings['highscore_wtime']); 9897 $this->setHighscoreOwnTable($testsettings['highscore_own_table']); 9898 $this->setHighscoreTopTable($testsettings['highscore_top_table']); 9899 $this->setHighscoreTopNum($testsettings['highscore_top_num']); 9900 $this->setPassDeletionAllowed($testsettings['pass_deletion_allowed']); 9901 if (isset($testsettings['examid_in_kiosk'])) { 9902 $this->setShowExamIdInTestPassEnabled($testsettings['examid_in_kiosk']); 9903 } else { 9904 $this->setShowExamIdInTestPassEnabled($testsettings['examid_in_test_pass']); 9905 } 9906 if (isset($testsettings['show_exam_id'])) { 9907 $this->setShowExamIdInTestResultsEnabled($testsettings['show_exam_id']); 9908 } else { 9909 $this->setShowExamIdInTestResultsEnabled($testsettings['examid_in_test_res']); 9910 } 9911 $this->setEnableExamview($testsettings['enable_examview']); 9912 $this->setShowExamviewHtml($testsettings['show_examview_html']); 9913 $this->setShowExamviewPdf($testsettings['show_examview_pdf']); 9914 $this->setEnableArchiving($testsettings['enable_archiving']); 9915 $this->setSignSubmission($testsettings['sign_submission']); 9916 $this->setCharSelectorAvailability($testsettings['char_selector_availability']); 9917 $this->setCharSelectorDefinition($testsettings['char_selector_definition']); 9918 $this->setSkillServiceEnabled((bool) $testsettings['skill_service']); 9919 $this->setResultFilterTaxIds((array) $testsettings['result_tax_filters']); 9920 $this->setShowGradingStatusEnabled((bool) $testsettings['show_grading_status']); 9921 $this->setShowGradingMarkEnabled((bool) $testsettings['show_grading_mark']); 9922 9923 $this->setFollowupQuestionAnswerFixationEnabled($testsettings['follow_qst_answer_fixation']); 9924 $this->setInstantFeedbackAnswerFixationEnabled($testsettings['inst_fb_answer_fixation']); 9925 $this->setForceInstantFeedbackEnabled($testsettings['force_inst_fb']); 9926 $this->setRedirectionMode($testsettings['redirection_mode']); 9927 $this->setRedirectionUrl($testsettings['redirection_url']); 9928 9929 $this->setAutosave($testsettings['autosave']); 9930 $this->setAutosaveIval($testsettings['autosave_ival']); 9931 $this->setShowExamIdInTestResultsEnabled((int) $testsettings['examid_in_test_res']); 9932 $this->setPasswordEnabled($testsettings['password_enabled']); 9933 $this->setPassword($testsettings['password']); 9934 $this->setFixedParticipants($testsettings['fixed_participants']); 9935 $this->setLimitUsersEnabled($testsettings['limit_users_enabled']); 9936 $this->setAllowedUsers($testsettings['allowedusers']); 9937 $this->setAllowedUsersTimeGap($testsettings['alloweduserstimegap']); 9938 $this->setUsePreviousAnswers($testsettings['use_previous_answers']); 9939 $this->setPoolUsage($testsettings['pool_usage']); 9940 $this->setActivationLimited($testsettings['activation_limited']); 9941 $this->setActivationStartingTime($testsettings['activation_start_time']); 9942 $this->setActivationEndingTime($testsettings['activation_end_time']); 9943 $this->setActivationVisibility($testsettings['activation_visibility']); 9944 $this->setPassWaiting($testsettings['pass_waiting']); 9945 9946 $this->saveToDb(); 9947 9948 return true; 9949 } 9950 9951 /** 9952 * Convert a print output to XSL-FO 9953 * 9954 * @param string $print_output The print output 9955 * @return string XSL-FO code 9956 * @access public 9957 */ 9958 public function processPrintoutput2FO($print_output) 9959 { 9960 if (extension_loaded("tidy")) { 9961 $config = array( 9962 "indent" => false, 9963 "output-xml" => true, 9964 "numeric-entities" => true 9965 ); 9966 $tidy = new tidy(); 9967 $tidy->parseString($print_output, $config, 'utf8'); 9968 $tidy->cleanRepair(); 9969 $print_output = tidy_get_output($tidy); 9970 $print_output = preg_replace("/^.*?(<html)/", "\\1", $print_output); 9971 } else { 9972 $print_output = str_replace(" ", " ", $print_output); 9973 $print_output = str_replace("⊗", "X", $print_output); 9974 } 9975 $xsl = file_get_contents("./Modules/Test/xml/question2fo.xsl"); 9976 9977 // additional font support 9978 global $DIC; 9979 $xsl = str_replace( 9980 'font-family="Helvetica, unifont"', 9981 'font-family="' . $DIC['ilSetting']->get('rpc_pdf_font', 'Helvetica, unifont') . '"', 9982 $xsl 9983 ); 9984 9985 $args = array( '/_xml' => $print_output, '/_xsl' => $xsl ); 9986 $xh = xslt_create(); 9987 $params = array(); 9988 $output = xslt_process($xh, "arg:/_xml", "arg:/_xsl", null, $args, $params); 9989 xslt_error($xh); 9990 xslt_free($xh); 9991 return $output; 9992 } 9993 9994 /** 9995 * Delivers a PDF file from XHTML 9996 * 9997 * @param string $html The XHTML string 9998 * @access public 9999 */ 10000 public function deliverPDFfromHTML($content, $title = null) 10001 { 10002 $content = preg_replace("/href=\".*?\"/", "", $content); 10003 $printbody = new ilTemplate("tpl.il_as_tst_print_body.html", true, true, "Modules/Test"); 10004 $printbody->setVariable("TITLE", ilUtil::prepareFormOutput($this->getTitle())); 10005 $printbody->setVariable("ADM_CONTENT", $content); 10006 $printbody->setCurrentBlock("css_file"); 10007 $printbody->setVariable("CSS_FILE", $this->getTestStyleLocation("filesystem")); 10008 $printbody->parseCurrentBlock(); 10009 $printbody->setCurrentBlock("css_file"); 10010 $printbody->setVariable("CSS_FILE", ilUtil::getStyleSheetLocation("filesystem", "delos.css")); 10011 $printbody->parseCurrentBlock(); 10012 $printoutput = $printbody->get(); 10013 $html = str_replace("href=\"./", "href=\"" . ILIAS_HTTP_PATH . "/", $printoutput); 10014 $html = preg_replace("/<div id=\"dontprint\">.*?<\\/div>/ims", "", $html); 10015 if (extension_loaded("tidy")) { 10016 $config = array( 10017 "indent" => false, 10018 "output-xml" => true, 10019 "numeric-entities" => true 10020 ); 10021 $tidy = new tidy(); 10022 $tidy->parseString($html, $config, 'utf8'); 10023 $tidy->cleanRepair(); 10024 $html = tidy_get_output($tidy); 10025 $html = preg_replace("/^.*?(<html)/", "\\1", $html); 10026 } else { 10027 $html = str_replace(" ", " ", $html); 10028 $html = str_replace("⊗", "X", $html); 10029 } 10030 $html = preg_replace("/src=\".\\//ims", "src=\"" . ILIAS_HTTP_PATH . "/", $html); 10031 $this->deliverPDFfromFO($this->processPrintoutput2FO($html), $title); 10032 } 10033 10034 /** 10035 * Delivers a PDF file from a XSL-FO string 10036 * 10037 * @param string $fo The XSL-FO string 10038 * @access public 10039 */ 10040 public function deliverPDFfromFO($fo, $title = null) 10041 { 10042 global $DIC; 10043 $ilLog = $DIC['ilLog']; 10044 10045 include_once "./Services/Utilities/classes/class.ilUtil.php"; 10046 $fo_file = ilUtil::ilTempnam() . ".fo"; 10047 $fp = fopen($fo_file, "w"); 10048 fwrite($fp, $fo); 10049 fclose($fp); 10050 10051 include_once './Services/WebServices/RPC/classes/class.ilRpcClientFactory.php'; 10052 try { 10053 $pdf_base64 = ilRpcClientFactory::factory('RPCTransformationHandler')->ilFO2PDF($fo); 10054 $filename = (strlen($title)) ? $title : $this->getTitle(); 10055 ilUtil::deliverData($pdf_base64->scalar, ilUtil::getASCIIFilename($filename) . ".pdf", "application/pdf", false, true); 10056 return true; 10057 } catch (Exception $e) { 10058 $ilLog->write(__METHOD__ . ': ' . $e->getMessage()); 10059 return false; 10060 } 10061 } 10062 10063 /** 10064 * Retrieves the feedback comment for a question in a test if it is finalized 10065 * 10066 * @param integer $active_id Active ID of the user 10067 * @param integer $question_id Question ID 10068 * @param integer $pass Pass number 10069 * @return string The feedback text 10070 * @access public 10071 */ 10072 public static function getManualFeedback($active_id, $question_id, $pass) 10073 { 10074 $feedback = ""; 10075 $row = self::getSingleManualFeedback($active_id, $question_id, $pass); 10076 10077 if (count($row) > 0 && ($row['finalized_evaluation'] || \ilTestService::isManScoringDone($active_id))) { 10078 $feedback = $row['feedback']; 10079 } 10080 10081 return $feedback; 10082 } 10083 10084 /** 10085 * Retrieves the manual feedback for a question in a test 10086 * 10087 * @param integer $active_id Active ID of the user 10088 * @param integer $question_id Question ID 10089 * @param integer $pass Pass number 10090 * @return array The feedback text 10091 * @access public 10092 */ 10093 public static function getSingleManualFeedback($active_id, $question_id, $pass) 10094 { 10095 global $DIC; 10096 10097 $ilDB = $DIC->database(); 10098 $row = array(); 10099 $result = $ilDB->queryF( 10100 "SELECT * FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s", 10101 array('integer', 'integer', 'integer'), 10102 array($active_id, $question_id, $pass) 10103 ); 10104 10105 if ($result->numRows() === 1) { 10106 $row = $ilDB->fetchAssoc($result); 10107 $row['feedback'] = ilRTE::_replaceMediaObjectImageSrc($row['feedback'], 1); 10108 } else { 10109 $DIC->logger()->root()->warning("WARNING: Multiple feedback entries on tst_manual_fb for " . 10110 "active_fi = $active_id , question_fi = $question_id and pass = $pass"); 10111 } 10112 10113 return $row; 10114 } 10115 10116 /** 10117 * Retrieves the manual feedback for a question in a test 10118 * 10119 * @param integer $question_id Question ID 10120 * @return array The feedback text 10121 * @access public 10122 */ 10123 public static function getCompleteManualFeedback(int $question_id) 10124 { 10125 global $DIC; 10126 10127 $ilDB = $DIC->database(); 10128 $feedback = array(); 10129 $result = $ilDB->queryF( 10130 "SELECT * FROM tst_manual_fb WHERE question_fi = %s", 10131 array('integer'), 10132 array($question_id) 10133 ); 10134 10135 while ($row = $ilDB->fetchAssoc($result)) { 10136 $active = $row['active_fi']; 10137 $pass = $row['pass']; 10138 $question = $row['question_fi']; 10139 10140 $row['feedback'] = ilRTE::_replaceMediaObjectImageSrc($row['feedback'], 1); 10141 10142 $feedback[$active][$pass][$question] = $row; 10143 } 10144 10145 return $feedback; 10146 } 10147 10148 /** 10149 * Saves the manual feedback for a question in a test 10150 * @param integer $active_id Active ID of the user 10151 * @param integer $question_id Question ID 10152 * @param integer $pass Pass number 10153 * @param string $feedback The feedback text 10154 * @param boolean $finalized In Feedback is final 10155 * @param boolean $is_single_feedback 10156 * @return boolean TRUE if the operation succeeds, FALSE otherwise 10157 * @access public 10158 */ 10159 public function saveManualFeedback($active_id, $question_id, $pass, $feedback, $finalized = false, $is_single_feedback = false) 10160 { 10161 global $DIC; 10162 10163 $feedback_old = $this->getSingleManualFeedback($active_id, $question_id, $pass); 10164 10165 $finalized_record = (int) $feedback_old['finalized_evaluation']; 10166 if ($finalized_record === 0 || ($is_single_feedback && $finalized_record === 1)) { 10167 $DIC->database()->manipulateF( 10168 "DELETE FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s", 10169 array('integer', 'integer', 'integer'), 10170 array($active_id, $question_id, $pass) 10171 ); 10172 10173 $this->insertManualFeedback($active_id, $question_id, $pass, $feedback, $finalized, $feedback_old); 10174 10175 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 10176 $this->logManualFeedback($active_id, $question_id, $feedback); 10177 } 10178 } 10179 10180 return true; 10181 } 10182 10183 /** 10184 * Inserts a manual feedback into the DB 10185 * 10186 * @param integer $active_id Active ID of the user 10187 * @param integer $question_id Question ID 10188 * @param integer $pass Pass number 10189 * @param string $feedback The feedback text 10190 * @param array $feedback_old The feedback before update 10191 * @param boolean $finalized In Feedback is final 10192 */ 10193 private function insertManualFeedback($active_id, $question_id, $pass, $feedback, $finalized, $feedback_old) 10194 { 10195 global $DIC; 10196 10197 $ilDB = $DIC->database(); 10198 $ilUser = $DIC->user(); 10199 $next_id = $ilDB->nextId('tst_manual_fb'); 10200 $user = $ilUser->getId(); 10201 $finalized_time = time(); 10202 10203 $update_default = [ 10204 'manual_feedback_id' => [ 'integer', $next_id], 10205 'active_fi' => [ 'integer', $active_id], 10206 'question_fi' => [ 'integer', $question_id], 10207 'pass' => [ 'integer', $pass], 10208 'feedback' => [ 'clob', ilRTE::_replaceMediaObjectImageSrc($feedback, 0)], 10209 'tstamp' => [ 'integer', time()] 10210 ]; 10211 10212 if ($feedback_old['finalized_evaluation'] == 1) { 10213 $user = $feedback_old['finalized_by_usr_id']; 10214 $finalized_time = $feedback_old['finalized_tstamp']; 10215 } 10216 10217 if ($finalized === true || $feedback_old['finalized_evaluation'] == 1) { 10218 if (!array_key_exists('evaluated', $_POST)) { 10219 $update_default['finalized_evaluation'] = ['integer', 0]; 10220 $update_default['finalized_by_usr_id'] = ['integer', 0]; 10221 $update_default['finalized_tstamp'] = ['integer', 0]; 10222 } else { 10223 $update_default['finalized_evaluation'] = ['integer', 1]; 10224 $update_default['finalized_by_usr_id'] = ['integer', $user]; 10225 $update_default['finalized_tstamp'] = ['integer', $finalized_time]; 10226 } 10227 } 10228 10229 $ilDB->insert('tst_manual_fb', $update_default); 10230 } 10231 10232 /** 10233 * Creates a log for the manual feedback 10234 * 10235 * @param integer $active_id Active ID of the user 10236 * @param integer $question_id Question ID 10237 * @param string $feedback The feedback text 10238 */ 10239 private function logManualFeedback($active_id, $question_id, $feedback) 10240 { 10241 global $DIC; 10242 10243 $ilUser = $DIC->user(); 10244 $lng = $DIC->language(); 10245 $username = ilObjTestAccess::_getParticipantData($active_id); 10246 10247 $this->logAction( 10248 sprintf( 10249 $lng->txtlng('assessment', 'log_manual_feedback', ilObjAssessmentFolder::_getLogLanguage()), 10250 $ilUser->getFullname() . ' (' . $ilUser->getLogin() . ')', 10251 $username, 10252 assQuestion::_getQuestionTitle($question_id), 10253 $feedback 10254 ) 10255 ); 10256 } 10257 10258 /** 10259 * Returns if Javascript should be chosen for drag & drop actions 10260 * for the active user 10261 * 10262 * @return boolean TRUE if Javascript should be chosen, FALSE otherwise 10263 * @access public 10264 */ 10265 public function getJavaScriptOutput() 10266 { 10267 return true; 10268 10269 // global $DIC; 10270// $ilUser = $DIC['ilUser']; 10271// if (strcmp($_GET["tst_javascript"], "0") == 0) return FALSE; 10272// if ($this->getForceJS()) return TRUE; 10273// $assessmentSetting = new ilSetting("assessment"); 10274// return ($ilUser->getPref("tst_javascript") === FALSE) ? $assessmentSetting->get("use_javascript") : $ilUser->getPref("tst_javascript"); 10275 } 10276 10277 public function &createTestSequence($active_id, $pass, $shuffle) 10278 { 10279 include_once "./Modules/Test/classes/class.ilTestSequence.php"; 10280 $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest()); 10281 } 10282 10283 /** 10284 * Sets the test ID 10285 * 10286 * @param integer $a_id Test ID 10287 */ 10288 public function setTestId($a_id) 10289 { 10290 $this->test_id = $a_id; 10291 } 10292 10293 /** 10294 * returns all test results for all participants 10295 * 10296 * @param array $partipants array of user ids 10297 * @param boolean if true, the result will be prepared for csv output (see processCSVRow) 10298 * 10299 * @return array of fields, see code for column titles 10300 */ 10301 public function getDetailedTestResults($participants) 10302 { 10303 $results = array(); 10304 if (count($participants)) { 10305 foreach ($participants as $active_id => $user_rec) { 10306 $row = array(); 10307 $reached_points = 0; 10308 $max_points = 0; 10309 foreach ($this->questions as $value) { 10310 $question = &ilObjTest::_instanciateQuestion($value); 10311 if (is_object($question)) { 10312 $max_points += $question->getMaximumPoints(); 10313 $reached_points += $question->getReachedPoints($active_id); 10314 if ($max_points > 0) { 10315 $percentvalue = $reached_points / $max_points; 10316 if ($percentvalue < 0) { 10317 $percentvalue = 0.0; 10318 } 10319 } else { 10320 $percentvalue = 0; 10321 } 10322 if ($this->getAnonymity()) { 10323 $user_rec['firstname'] = ""; 10324 $user_rec['lastname'] = $this->lng->txt("anonymous"); 10325 } 10326 $row = array( 10327 "user_id" => $user_rec['usr_id'], 10328 "matriculation" => $user_rec['matriculation'], 10329 "lastname" => $user_rec['lastname'], 10330 "firstname" => $user_rec['firstname'], 10331 "login" => $user_rec['login'], 10332 "question_id" => $question->getId(), 10333 "question_title" => $question->getTitle(), 10334 "reached_points" => $reached_points, 10335 "max_points" => $max_points 10336 ); 10337 $results[] = $row; 10338 } 10339 } 10340 } 10341 } 10342 return $results; 10343 } 10344 10345 /** 10346 * Get test Object ID for question ID 10347 */ 10348 public static function _lookupTestObjIdForQuestionId($a_q_id) 10349 { 10350 global $DIC; 10351 $ilDB = $DIC['ilDB']; 10352 10353 $result = $ilDB->queryF( 10354 "SELECT t.obj_fi obj_id FROM tst_test_question q, tst_tests t WHERE q.test_fi = t.test_id AND q.question_fi = %s", 10355 array('integer'), 10356 array($a_q_id) 10357 ); 10358 $rec = $ilDB->fetchAssoc($result); 10359 return $rec["obj_id"]; 10360 } 10361 10362 /** 10363 * Checks wheather or not a question plugin with a given name is active 10364 * 10365 * @param string $a_pname The plugin name 10366 * @access public 10367 */ 10368 public function isPluginActive($a_pname) 10369 { 10370 global $DIC; 10371 $ilPluginAdmin = $DIC['ilPluginAdmin']; 10372 if ($ilPluginAdmin->isActive(IL_COMP_MODULE, "TestQuestionPool", "qst", $a_pname)) { 10373 return true; 10374 } else { 10375 return false; 10376 } 10377 } 10378 10379 public function getPassed($active_id) 10380 { 10381 global $DIC; 10382 $ilDB = $DIC['ilDB']; 10383 10384 $result = $ilDB->queryF( 10385 "SELECT passed FROM tst_result_cache WHERE active_fi = %s", 10386 array('integer'), 10387 array($active_id) 10388 ); 10389 if ($result->numRows()) { 10390 $row = $ilDB->fetchAssoc($result); 10391 return $row['passed']; 10392 } else { 10393 $counted_pass = ilObjTest::_getResultPass($active_id); 10394 $result_array = &$this->getTestResult($active_id, $counted_pass); 10395 return $result_array["test"]["passed"]; 10396 } 10397 } 10398 10399 /** 10400 * Checks whether the certificate button could be shown on the info page or not 10401 * 10402 * @access public 10403 */ 10404 public function canShowCertificate($testSession, $user_id, $active_id) 10405 { 10406 if ($this->canShowTestResults($testSession)) { 10407 $isComplete = false; 10408 $userCertificateRepository = new ilUserCertificateRepository($this->db, $this->log); 10409 try { 10410 $userCertificateRepository->fetchActiveCertificate($user_id, $this->getId()); 10411 $isComplete = true; 10412 } catch (ilException $e) { 10413 } 10414 10415 if ($isComplete) { 10416 $vis = $this->getCertificateVisibility(); 10417 $showcert = false; 10418 switch ($vis) { 10419 case 0: 10420 $showcert = true; 10421 break; 10422 case 1: 10423 if ($this->getPassed($active_id)) { 10424 $showcert = true; 10425 } 10426 break; 10427 case 2: 10428 $showcert = false; 10429 break; 10430 } 10431 if ($showcert) { 10432 return true; 10433 } else { 10434 return false; 10435 } 10436 } else { 10437 return false; 10438 } 10439 } else { 10440 return false; 10441 } 10442 } 10443 10444 /** 10445 * Creates an associated array with all active id's for a given test and original question id 10446 */ 10447 public function getParticipantsForTestAndQuestion($test_id, $question_id) 10448 { 10449 /** @var ilDBInterface $ilDB */ 10450 global $DIC; 10451 $ilDB = $DIC['ilDB']; 10452 10453 $query = " 10454 SELECT tst_test_result.active_fi, tst_test_result.question_fi, tst_test_result.pass 10455 FROM tst_test_result 10456 INNER JOIN tst_active ON tst_active.active_id = tst_test_result.active_fi AND tst_active.test_fi = %s 10457 INNER JOIN qpl_questions ON qpl_questions.question_id = tst_test_result.question_fi 10458 LEFT JOIN usr_data ON usr_data.usr_id = tst_active.user_fi 10459 WHERE tst_test_result.question_fi = %s 10460 ORDER BY usr_data.lastname ASC, usr_data.firstname ASC 10461 "; 10462 10463 $result = $ilDB->queryF( 10464 $query, 10465 array('integer', 'integer'), 10466 array($test_id, $question_id) 10467 ); 10468 $foundusers = array(); 10469 /** @noinspection PhpAssignmentInConditionInspection */ 10470 while ($row = $ilDB->fetchAssoc($result)) { 10471 if ($this->getAccessFilteredParticipantList() && !$this->getAccessFilteredParticipantList()->isActiveIdInList($row["active_fi"])) { 10472 continue; 10473 } 10474 10475 if (!array_key_exists($row["active_fi"], $foundusers)) { 10476 $foundusers[$row["active_fi"]] = array(); 10477 } 10478 array_push($foundusers[$row["active_fi"]], array("pass" => $row["pass"], "qid" => $row["question_fi"])); 10479 } 10480 return $foundusers; 10481 } 10482 10483 /** 10484 * Returns the aggregated test results 10485 * 10486 * @access public 10487 */ 10488 public function getAggregatedResultsData() 10489 { 10490 $data = &$this->getCompleteEvaluationData(); 10491 $foundParticipants = &$data->getParticipants(); 10492 $results = array("overview" => array(), "questions" => array()); 10493 if (count($foundParticipants)) { 10494 $results["overview"][$this->lng->txt("tst_eval_total_persons")] = count($foundParticipants); 10495 $total_finished = $data->getTotalFinishedParticipants(); 10496 $results["overview"][$this->lng->txt("tst_eval_total_finished")] = $total_finished; 10497 $average_time = $this->evalTotalStartedAverageTime($data->getParticipantIds()); 10498 $diff_seconds = $average_time; 10499 $diff_hours = floor($diff_seconds / 3600); 10500 $diff_seconds -= $diff_hours * 3600; 10501 $diff_minutes = floor($diff_seconds / 60); 10502 $diff_seconds -= $diff_minutes * 60; 10503 $results["overview"][$this->lng->txt("tst_eval_total_finished_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds); 10504 $total_passed = 0; 10505 $total_passed_reached = 0; 10506 $total_passed_max = 0; 10507 $total_passed_time = 0; 10508 foreach ($foundParticipants as $userdata) { 10509 if ($userdata->getPassed()) { 10510 $total_passed++; 10511 $total_passed_reached += $userdata->getReached(); 10512 $total_passed_max += $userdata->getMaxpoints(); 10513 $total_passed_time += $userdata->getTimeOfWork(); 10514 } 10515 } 10516 $average_passed_reached = $total_passed ? $total_passed_reached / $total_passed : 0; 10517 $average_passed_max = $total_passed ? $total_passed_max / $total_passed : 0; 10518 $average_passed_time = $total_passed ? $total_passed_time / $total_passed : 0; 10519 $results["overview"][$this->lng->txt("tst_eval_total_passed")] = $total_passed; 10520 $results["overview"][$this->lng->txt("tst_eval_total_passed_average_points")] = sprintf("%2.2f", $average_passed_reached) . " " . strtolower($this->lng->txt("of")) . " " . sprintf("%2.2f", $average_passed_max); 10521 $average_time = $average_passed_time; 10522 $diff_seconds = $average_time; 10523 $diff_hours = floor($diff_seconds / 3600); 10524 $diff_seconds -= $diff_hours * 3600; 10525 $diff_minutes = floor($diff_seconds / 60); 10526 $diff_seconds -= $diff_minutes * 60; 10527 $results["overview"][$this->lng->txt("tst_eval_total_passed_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds); 10528 } 10529 10530 foreach ($data->getQuestionTitles() as $question_id => $question_title) { 10531 $answered = 0; 10532 $reached = 0; 10533 $max = 0; 10534 foreach ($foundParticipants as $userdata) { 10535 for ($i = 0; $i <= $userdata->getLastPass(); $i++) { 10536 if (is_object($userdata->getPass($i))) { 10537 $question = &$userdata->getPass($i)->getAnsweredQuestionByQuestionId($question_id); 10538 if (is_array($question)) { 10539 $answered++; 10540 $reached += $question["reached"]; 10541 $max += $question["points"]; 10542 } 10543 } 10544 } 10545 } 10546 $percent = $max ? $reached / $max * 100.0 : 0; 10547 $results["questions"][$question_id] = array( 10548 $question_title, 10549 sprintf("%.2f", $answered ? $reached / $answered : 0) . " " . strtolower($this->lng->txt("of")) . " " . sprintf("%.2f", $answered ? $max / $answered : 0), 10550 sprintf("%.2f", $percent) . "%", 10551 $answered, 10552 sprintf("%.2f", $answered ? $reached / $answered : 0), 10553 sprintf("%.2f", $answered ? $max / $answered : 0), 10554 $percent / 100.0 10555 ); 10556 } 10557 return $results; 10558 } 10559 10560 /** 10561 * Get zipped xml file for test 10562 */ 10563 public function getXMLZip() 10564 { 10565 require_once 'Modules/Test/classes/class.ilTestExportFactory.php'; 10566 $expFactory = new ilTestExportFactory($this); 10567 $test_exp = $expFactory->getExporter('xml'); 10568 return $test_exp->buildExportFile(); 10569 } 10570 10571 /** 10572 * Get mail notification settings 10573 */ 10574 public function getMailNotification() 10575 { 10576 return $this->mailnotification; 10577 } 10578 10579 /** 10580 * Set mail notification settings 10581 * 10582 * @param $a_notification Mail notification setting 10583 */ 10584 public function setMailNotification($a_notification) 10585 { 10586 $this->mailnotification = $a_notification; 10587 } 10588 10589 public function sendSimpleNotification($active_id) 10590 { 10591 include_once "./Modules/Test/classes/class.ilTestMailNotification.php"; 10592 10593 $mail = new ilTestMailNotification(); 10594 $owner_id = $this->getOwner(); 10595 $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id)); 10596 $mail->sendSimpleNotification($owner_id, $this->getTitle(), $usr_data); 10597 } 10598 10599 /** 10600 * Gets additional user fields that should be shown in the user evaluation 10601 * 10602 * @return array An array containing the database fields that should be shown in the evaluation 10603 */ 10604 public function getEvaluationAdditionalFields() 10605 { 10606 include_once "./Modules/Test/classes/class.ilObjTestGUI.php"; 10607 include_once "./Modules/Test/classes/tables/class.ilEvaluationAllTableGUI.php"; 10608 $table_gui = new ilEvaluationAllTableGUI(new ilObjTestGUI($this->getRefId()), 'outEvaluation', $this->getAnonymity()); 10609 return $table_gui->getSelectedColumns(); 10610 } 10611 10612 public function sendAdvancedNotification($active_id) 10613 { 10614 include_once "./Modules/Test/classes/class.ilTestMailNotification.php"; 10615 10616 $mail = new ilTestMailNotification(); 10617 $owner_id = $this->getOwner(); 10618 $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id)); 10619 10620 $participantList = new ilTestParticipantList($this); 10621 $participantList->initializeFromDbRows($this->getTestParticipants()); 10622 10623 require_once 'Modules/Test/classes/class.ilTestExportFactory.php'; 10624 $expFactory = new ilTestExportFactory($this); 10625 $exportObj = $expFactory->getExporter('results'); 10626 $exportObj->setForcedAccessFilteredParticipantList($participantList); 10627 $file = $exportObj->exportToExcel($deliver = false, 'active_id', $active_id, $passedonly = false); 10628 include_once "./Services/Mail/classes/class.ilFileDataMail.php"; 10629 $fd = new ilFileDataMail(ANONYMOUS_USER_ID); 10630 $fd->copyAttachmentFile($file, "result_" . $active_id . ".xls"); 10631 $file_names[] = "result_" . $active_id . ".xls"; 10632 10633 $mail->sendAdvancedNotification($owner_id, $this->getTitle(), $usr_data, $file_names); 10634 10635 if (count($file_names)) { 10636 $fd->unlinkFiles($file_names); 10637 unset($fd); 10638 @unlink($file); 10639 } 10640 } 10641 10642 public function createRandomSolutions($number) 10643 { 10644 global $DIC; 10645 $ilDB = $DIC['ilDB']; 10646 10647 // 1. get a user 10648 $query = "SELECT usr_id FROM usr_data"; 10649 $result = $ilDB->query($query); 10650 while ($data = $ilDB->fetchAssoc($result)) { 10651 $activequery = sprintf( 10652 "SELECT user_fi FROM tst_active WHERE test_fi = %s AND user_fi = %s", 10653 $ilDB->quote($this->getTestId()), 10654 $ilDB->quote($data['usr_id']) 10655 ); 10656 $activeresult = $ilDB->query($activequery); 10657 if ($activeresult->numRows() == 0) { 10658 $user_id = $data['usr_id']; 10659 if ($user_id != 13) { 10660 include_once "./Modules/Test/classes/class.ilTestSession.php"; 10661 $testSession = new ilTestSession(); 10662 $testSession->setRefId($this->getRefId()); 10663 $testSession->setTestId($this->getTestId()); 10664 $testSession->setUserId($user_id); 10665 $testSession->saveToDb(); 10666 $passes = ($this->getNrOfTries()) ? $this->getNrOfTries() : 10; 10667 $random = new \ilRandom(); 10668 $nr_of_passes = $random->int(1, $passes); 10669 $active_id = $testSession->getActiveId(); 10670 for ($pass = 0; $pass < $nr_of_passes; $pass++) { 10671 include_once "./Modules/Test/classes/class.ilTestSequence.php"; 10672 $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest()); 10673 $testSequence->loadFromDb(); 10674 $testSequence->loadQuestions(); 10675 if (!$testSequence->hasSequence()) { 10676 $testSequence->createNewSequence($this->getQuestionCount(), $shuffle); 10677 $testSequence->saveToDb(); 10678 } 10679 for ($seq = 1; $seq <= count($this->questions); $seq++) { 10680 $question_id = $testSequence->getQuestionForSequence($seq); 10681 $objQuestion = ilObjTest::_instanciateQuestion($question_id); 10682 $assSettings = new ilSetting('assessment'); 10683 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionProcessLockerFactory.php'; 10684 $processLockerFactory = new ilAssQuestionProcessLockerFactory($assSettings, $ilDB); 10685 $processLockerFactory->setQuestionId($objQuestion->getId()); 10686 $processLockerFactory->setUserId($testSession->getUserId()); 10687 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php"); 10688 $processLockerFactory->setAssessmentLogEnabled(ilObjAssessmentFolder::_enabledAssessmentLogging()); 10689 $objQuestion->setProcessLocker($processLockerFactory->getLocker()); 10690 $objQuestion->createRandomSolution($testSession->getActiveId(), $pass); 10691 } 10692 $testSession->increasePass(); 10693 $testSession->setLastSequence(0); 10694 $testSession->setLastFinishedPass($pass); 10695 $testSession->setSubmitted(1); 10696 $testSession->setSubmittedTimestamp(date('Y-m-d H:i:s')); 10697 $testSession->saveToDb(); 10698 } 10699 $number--; 10700 if ($number == 0) { 10701 return; 10702 } 10703 } 10704 } 10705 } 10706 } 10707 10708 public function getResultsForActiveId($active_id) 10709 { 10710 global $DIC; 10711 $ilDB = $DIC['ilDB']; 10712 10713 $query = " 10714 SELECT * 10715 FROM tst_result_cache 10716 WHERE active_fi = %s 10717 "; 10718 10719 $result = $ilDB->queryF( 10720 $query, 10721 array('integer'), 10722 array($active_id) 10723 ); 10724 10725 if (!$result->numRows()) { 10726 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php"; 10727 10728 assQuestion::_updateTestResultCache($active_id); 10729 10730 $query = " 10731 SELECT * 10732 FROM tst_result_cache 10733 WHERE active_fi = %s 10734 "; 10735 10736 $result = $ilDB->queryF( 10737 $query, 10738 array('integer'), 10739 array($active_id) 10740 ); 10741 } 10742 10743 $row = $ilDB->fetchAssoc($result); 10744 10745 return $row; 10746 } 10747 10748 public function getMailNotificationType() 10749 { 10750 if ($this->mailnottype == 1) { 10751 return $this->mailnottype; 10752 } else { 10753 return 0; 10754 } 10755 } 10756 10757 public function setMailNotificationType($a_type) 10758 { 10759 if ($a_type == 1) { 10760 $this->mailnottype = 1; 10761 } else { 10762 $this->mailnottype = 0; 10763 } 10764 } 10765 10766 public function getExportSettings() 10767 { 10768 if ($this->exportsettings) { 10769 return $this->exportsettings; 10770 } else { 10771 return 0; 10772 } 10773 } 10774 10775 public function setExportSettings($a_settings) 10776 { 10777 if ($a_settings) { 10778 $this->exportsettings = $a_settings; 10779 } else { 10780 $this->exportsettings = 0; 10781 } 10782 } 10783 10784 public function getExportSettingsSingleChoiceShort() 10785 { 10786 if (($this->exportsettings & 1) > 0) { 10787 return true; 10788 } else { 10789 return false; 10790 } 10791 } 10792 10793 public function setExportSettingsSingleChoiceShort($a_settings) 10794 { 10795 if ($a_settings) { 10796 $this->exportsettings = $this->exportsettings | 1; 10797 } else { 10798 if ($this->getExportSettingsSingleChoiceShort()) { 10799 $this->exportsettings = $this->exportsettings ^ 1; 10800 } 10801 } 10802 } 10803 10804 public function getEnabledViewMode() 10805 { 10806 return $this->enabled_view_mode; 10807 } 10808 10809 public function setEnabledViewMode($mode) 10810 { 10811 $this->enabled_view_mode = $mode; 10812 } 10813 10814 public function setTemplate($template_id) 10815 { 10816 $this->template_id = (int) $template_id; 10817 } 10818 10819 public function getTemplate() 10820 { 10821 return $this->template_id; 10822 } 10823 10824 public function moveQuestionAfterOLD($previous_question_id, $new_question_id) 10825 { 10826 $new_array = array(); 10827 $position = 1; 10828 10829 $query = 'SELECT question_fi FROM tst_test_question WHERE test_fi = %s'; 10830 $types = array('integer'); 10831 $values = array($this->getTestId()); 10832 10833 $new_question_id += 1; 10834 10835 global $DIC; 10836 $ilDB = $DIC['ilDB']; 10837 $inserted = false; 10838 $res = $ilDB->queryF($query, $types, $values); 10839 while ($row = $ilDB->fetchAssoc($res)) { 10840 $qid = $row['question_fi']; 10841 10842 if ($qid == $new_question_id) { 10843 continue; 10844 } elseif ($qid == $previous_question_id) { 10845 $new_array[$position++] = $qid; 10846 $new_array[$position++] = $new_question_id; 10847 $inserted = true; 10848 } else { 10849 $new_array[$position++] = $qid; 10850 } 10851 } 10852 10853 $update_query = 'UPDATE tst_test_question SET sequence = %s WHERE test_fi = %s AND question_fi = %s'; 10854 $update_types = array('integer', 'integer', 'integer'); 10855 10856 foreach ($new_array as $position => $qid) { 10857 $ilDB->manipulateF( 10858 $update_query, 10859 $update_types, 10860 $vals = array( 10861 $position, 10862 $this->getTestId(), 10863 $qid 10864 ) 10865 ); 10866 } 10867 } 10868 10869 public function isAnyInstantFeedbackOptionEnabled() 10870 { 10871 return ( 10872 $this->getSpecificAnswerFeedback() || $this->getGenericAnswerFeedback() || 10873 $this->getAnswerFeedbackPoints() || $this->getInstantFeedbackSolution() 10874 ); 10875 } 10876 10877 public function getInstantFeedbackOptionsAsArray() 10878 { 10879 $values = array(); 10880 10881 if ($this->getSpecificAnswerFeedback()) { 10882 $values[] = 'instant_feedback_specific'; 10883 } 10884 if ($this->getGenericAnswerFeedback()) { 10885 $values[] = 'instant_feedback_generic'; 10886 } 10887 if ($this->getAnswerFeedbackPoints()) { 10888 $values[] = 'instant_feedback_points'; 10889 } 10890 if ($this->getInstantFeedbackSolution()) { 10891 $values[] = 'instant_feedback_solution'; 10892 } 10893 10894 return $values; 10895 } 10896 10897 public function setInstantFeedbackOptionsByArray($options) 10898 { 10899 if (is_array($options)) { 10900 $this->setGenericAnswerFeedback(in_array('instant_feedback_generic', $options) ? 1 : 0); 10901 $this->setSpecificAnswerFeedback(in_array('instant_feedback_specific', $options) ? 1 : 0); 10902 $this->setAnswerFeedbackPoints(in_array('instant_feedback_points', $options) ? 1 : 0); 10903 $this->setInstantFeedbackSolution(in_array('instant_feedback_solution', $options) ? 1 : 0); 10904 } else { 10905 $this->setGenericAnswerFeedback(0); 10906 $this->setSpecificAnswerFeedback(0); 10907 $this->setAnswerFeedbackPoints(0); 10908 $this->setInstantFeedbackSolution(0); 10909 } 10910 } 10911 10912 public function setResultsPresentationOptionsByArray($options) 10913 { 10914 $setter = array( 10915 'pass_details' => 'setShowPassDetails', 10916 'solution_details' => 'setShowSolutionDetails', 10917 'solution_printview' => 'setShowSolutionPrintview', 10918 'solution_feedback' => 'setShowSolutionFeedback', 10919 'solution_answers_only' => 'setShowSolutionAnswersOnly', 10920 'solution_signature' => 'setShowSolutionSignature', 10921 'solution_suggested' => 'setShowSolutionSuggested', 10922 ); 10923 foreach ($setter as $key => $setter) { 10924 if (in_array($key, $options)) { 10925 $this->$setter(1); 10926 } else { 10927 $this->$setter(0); 10928 } 10929 } 10930 } 10931 10932 public function getPoolUsage() 10933 { 10934 return (boolean) $this->poolUsage; 10935 } 10936 10937 public function setPoolUsage($usage) 10938 { 10939 $this->poolUsage = (boolean) $usage; 10940 } 10941 10942 /** 10943 * @return ilTestReindexedSequencePositionMap 10944 */ 10945 public function reindexFixedQuestionOrdering() 10946 { 10947 global $DIC; 10948 $tree = $DIC['tree']; 10949 $db = $DIC['ilDB']; 10950 $pluginAdmin = $DIC['ilPluginAdmin']; 10951 10952 require_once 'Modules/Test/classes/class.ilTestQuestionSetConfigFactory.php'; 10953 $qscFactory = new ilTestQuestionSetConfigFactory($tree, $db, $pluginAdmin, $this); 10954 $questionSetConfig = $qscFactory->getQuestionSetConfig(); 10955 10956 /* @var ilTestFixedQuestionSetConfig $questionSetConfig */ 10957 $reindexedSequencePositionMap = $questionSetConfig->reindexQuestionOrdering(); 10958 10959 $this->loadQuestions(); 10960 10961 return $reindexedSequencePositionMap; 10962 } 10963 10964 public function setQuestionOrderAndObligations($orders, $obligations) 10965 { 10966 global $DIC; 10967 $ilDB = $DIC['ilDB']; 10968 10969 asort($orders); 10970 10971 $i = 0; 10972 10973 foreach ($orders as $id => $position) { 10974 $i++; 10975 10976 $obligatory = ( 10977 isset($obligations[$id]) && $obligations[$id] ? 1 : 0 10978 ); 10979 10980 $query = " 10981 UPDATE tst_test_question 10982 SET sequence = %s, 10983 obligatory = %s 10984 WHERE question_fi = %s 10985 "; 10986 10987 $ilDB->manipulateF( 10988 $query, 10989 array('integer', 'integer', 'integer'), 10990 array($i, $obligatory, $id) 10991 ); 10992 } 10993 10994 $this->loadQuestions(); 10995 } 10996 10997 public function moveQuestionAfter($question_to_move, $question_before) 10998 { 10999 global $DIC; 11000 $ilDB = $DIC['ilDB']; 11001 //var_dump(func_get_args()); 11002 if ($question_before) { 11003 $query = 'SELECT sequence, test_fi FROM tst_test_question WHERE question_fi = %s'; 11004 $types = array('integer'); 11005 $values = array($question_before); 11006 $rset = $ilDB->queryF($query, $types, $values); 11007 } 11008 11009 if (!$question_before || ($rset && !($row = $ilDB->fetchAssoc($rset)))) { 11010 $row = array( 11011 'sequence' => 0, 11012 'test_fi' => $this->getTestId(), 11013 ); 11014 } 11015 11016 $update = 'UPDATE tst_test_question SET sequence = sequence + 1 WHERE sequence > %s AND test_fi = %s'; 11017 $types = array('integer', 'integer'); 11018 $values = array($row['sequence'], $row['test_fi']); 11019 $ilDB->manipulateF($update, $types, $values); 11020 11021 $update = 'UPDATE tst_test_question SET sequence = %s WHERE question_fi = %s'; 11022 $types = array('integer', 'integer'); 11023 $values = array($row['sequence'] + 1, $question_to_move); 11024 $ilDB->manipulateF($update, $types, $values); 11025 11026 $this->reindexFixedQuestionOrdering(); 11027 } 11028 11029 public function hasQuestionsWithoutQuestionpool() 11030 { 11031 global $DIC; 11032 $ilDB = $DIC['ilDB']; 11033 11034 $questions = $this->getQuestionTitlesAndIndexes(); 11035 11036 $IN_questions = $ilDB->in('q1.question_id', array_keys($questions), false, 'integer'); 11037 11038 $query = " 11039 SELECT count(q1.question_id) cnt 11040 11041 FROM qpl_questions q1 11042 11043 INNER JOIN qpl_questions q2 11044 ON q2.question_id = q1.original_id 11045 11046 WHERE $IN_questions 11047 AND q1.obj_fi = q2.obj_fi 11048 "; 11049 11050 $rset = $ilDB->query($query); 11051 11052 $row = $ilDB->fetchAssoc($rset); 11053 11054 return $row['cnt'] > 0; 11055 } 11056 11057 /** 11058 * Gather all finished tests for user 11059 * 11060 * @param int $a_user_id 11061 * @return array(test id => passed) 11062 */ 11063 public static function _lookupFinishedUserTests($a_user_id) 11064 { 11065 global $DIC; 11066 $ilDB = $DIC['ilDB']; 11067 11068 $result = $ilDB->queryF( 11069 "SELECT test_fi,MAX(pass) AS pass FROM tst_active" . 11070 " JOIN tst_pass_result ON (tst_pass_result.active_fi = tst_active.active_id)" . 11071 " WHERE user_fi=%s" . 11072 " GROUP BY test_fi", 11073 array('integer', 'integer'), 11074 array($a_user_id, 1) 11075 ); 11076 $all = array(); 11077 while ($row = $ilDB->fetchAssoc($result)) { 11078 $obj_id = self::_getObjectIDFromTestID($row["test_fi"]); 11079 $all[$obj_id] = (bool) $row["pass"]; 11080 } 11081 return $all; 11082 } 11083 public function getQuestions() 11084 { 11085 return $this->questions; 11086 } 11087 11088 public function isOnline() 11089 { 11090 return $this->online; 11091 } 11092 11093 public function setOnline($a_online = true) 11094 { 11095 $this->online = (bool) $a_online; 11096 } 11097 11098 /** 11099 * @return null 11100 */ 11101 public function getOldOnlineStatus() 11102 { 11103 return $this->oldOnlineStatus; 11104 } 11105 11106 /** 11107 * @param null $oldOnlineStatus 11108 */ 11109 public function setOldOnlineStatus($oldOnlineStatus) 11110 { 11111 $this->oldOnlineStatus = $oldOnlineStatus; 11112 } 11113 11114 public function setPrintBestSolutionWithResult($status) 11115 { 11116 $this->print_best_solution_with_result = (bool) $status; 11117 } 11118 11119 public function isBestSolutionPrintedWithResult() 11120 { 11121 return (bool) $this->print_best_solution_with_result; 11122 } 11123 11124 /** 11125 * returns the fact wether offering hints is enabled or not 11126 * 11127 * @return boolean 11128 */ 11129 public function isOfferingQuestionHintsEnabled() 11130 { 11131 return $this->offeringQuestionHintsEnabled; 11132 } 11133 11134 /** 11135 * sets offering question hints enabled/disabled 11136 * 11137 * @param boolean $offeringQuestionHintsEnabled 11138 */ 11139 public function setOfferingQuestionHintsEnabled($offeringQuestionHintsEnabled) 11140 { 11141 $this->offeringQuestionHintsEnabled = (bool) $offeringQuestionHintsEnabled; 11142 } 11143 11144 public function setActivationVisibility($a_value) 11145 { 11146 $this->activation_visibility = (bool) $a_value; 11147 } 11148 11149 public function getActivationVisibility() 11150 { 11151 return $this->activation_visibility; 11152 } 11153 11154 public function isActivationLimited() 11155 { 11156 return (bool) $this->activation_limited; 11157 } 11158 11159 public function setActivationLimited($a_value) 11160 { 11161 $this->activation_limited = (bool) $a_value; 11162 } 11163 11164 /* GET/SET for highscore feature */ 11165 11166 /** 11167 * Sets if the highscore feature should be enabled. 11168 * 11169 * @param bool $a_enabled 11170 */ 11171 public function setHighscoreEnabled($a_enabled) 11172 { 11173 $this->_highscore_enabled = (bool) $a_enabled; 11174 } 11175 11176 /** 11177 * Gets the setting which determines if the highscore feature is enabled. 11178 * 11179 * @return bool True, if highscore is enabled. 11180 */ 11181 public function getHighscoreEnabled() 11182 { 11183 return (bool) $this->_highscore_enabled; 11184 } 11185 11186 /** 11187 * Sets if the highscores should be anonymized. 11188 * 11189 * Note: This setting will be overriden, if the test is globally anonymized. 11190 * 11191 * @param bool $a_anon 11192 */ 11193 public function setHighscoreAnon($a_anon) 11194 { 11195 $this->_highscore_anon = (bool) $a_anon; 11196 } 11197 11198 /** 11199 * Gets if the highscores should be anonymized per setting. 11200 * 11201 * Note: This method will retrieve the setting as set by the user. If you want 11202 * to figure out, if the highscore is to be shown anonymized or not, with 11203 * consideration of the global anon switch you should @see isHighscoreAnon(). 11204 * 11205 * @return bool True, if setting is to anonymize highscores. 11206 */ 11207 public function getHighscoreAnon() 11208 { 11209 return (bool) $this->_highscore_anon; 11210 } 11211 11212 /** 11213 * Gets if the highscores should be displayed anonymized. 11214 * 11215 * Note: This method considers the global anonymity switch. If you need 11216 * access to the users setting, @see getHighscoreAnon() 11217 * 11218 * @return boolean True, if output is anonymized. 11219 */ 11220 public function isHighscoreAnon() 11221 { 11222 if ($this->getAnonymity() == 1) { 11223 return true; 11224 } else { 11225 return (bool) $this->getHighscoreAnon(); 11226 } 11227 } 11228 11229 /** 11230 * Sets if the date and time of the scores achievement should be displayed. 11231 * 11232 * @param bool $a_achieved_ts 11233 */ 11234 public function setHighscoreAchievedTS($a_achieved_ts) 11235 { 11236 $this->_highscore_achieved_ts = (bool) $a_achieved_ts; 11237 } 11238 11239 /** 11240 * Returns if date and time of the scores achievement should be displayed. 11241 * 11242 * @return bool True, if column should be shown. 11243 */ 11244 public function getHighscoreAchievedTS() 11245 { 11246 return (bool) $this->_highscore_achieved_ts; 11247 } 11248 11249 /** 11250 * Sets if the actual score should be displayed. 11251 * 11252 * @param bool $a_score 11253 */ 11254 public function setHighscoreScore($a_score) 11255 { 11256 $this->_highscore_score = (bool) $a_score; 11257 } 11258 11259 /** 11260 * Gets if the score column should be shown. 11261 * 11262 * @return bool True, if score column should be shown. 11263 */ 11264 public function getHighscoreScore() 11265 { 11266 return (bool) $this->_highscore_score; 11267 } 11268 11269 /** 11270 * Sets if the percentages of the scores pass should be shown. 11271 * 11272 * @param bool $a_percentage 11273 */ 11274 public function setHighscorePercentage($a_percentage) 11275 { 11276 $this->_highscore_percentage = (bool) $a_percentage; 11277 } 11278 11279 /** 11280 * Gets if the percentage column should be shown. 11281 * 11282 * @return bool True, if percentage column should be shown. 11283 */ 11284 public function getHighscorePercentage() 11285 { 11286 return (bool) $this->_highscore_percentage; 11287 } 11288 11289 /** 11290 * Sets if the number of requested hints should be shown. 11291 * 11292 * @param bool $a_hints 11293 */ 11294 public function setHighscoreHints($a_hints) 11295 { 11296 $this->_highscore_hints = (bool) $a_hints; 11297 } 11298 11299 /** 11300 * Gets, if the column with the number of requested hints should be shown. 11301 * 11302 * @return bool True, if the hints-column should be shown. 11303 */ 11304 public function getHighscoreHints() 11305 { 11306 return (bool) $this->_highscore_hints; 11307 } 11308 11309 /** 11310 * Sets if the workingtime of the scores should be shown. 11311 * 11312 * @param bool $a_wtime 11313 */ 11314 public function setHighscoreWTime($a_wtime) 11315 { 11316 $this->_highscore_wtime = (bool) $a_wtime; 11317 } 11318 11319 /** 11320 * Gets if the column with the workingtime should be shown. 11321 * 11322 * @return bool True, if the workingtime column should be shown. 11323 */ 11324 public function getHighscoreWTime() 11325 { 11326 return (bool) $this->_highscore_wtime; 11327 } 11328 11329 /** 11330 * Sets if the table with the own ranking should be shown. 11331 * 11332 * @param bool $a_own_table True, if table with own ranking should be shown. 11333 */ 11334 public function setHighscoreOwnTable($a_own_table) 11335 { 11336 $this->_highscore_own_table = (bool) $a_own_table; 11337 } 11338 11339 /** 11340 * Gets if the own rankings table should be shown. 11341 * 11342 * @return bool True, if the own rankings table should be shown. 11343 */ 11344 public function getHighscoreOwnTable() 11345 { 11346 return (bool) $this->_highscore_own_table; 11347 } 11348 11349 /** 11350 * Sets if the top-rankings table should be shown. 11351 * 11352 * @param bool $a_top_table 11353 */ 11354 public function setHighscoreTopTable($a_top_table) 11355 { 11356 $this->_highscore_top_table = (bool) $a_top_table; 11357 } 11358 11359 /** 11360 * Gets, if the top-rankings table should be shown. 11361 * 11362 * @return bool True, if top-rankings table should be shown. 11363 */ 11364 public function getHighscoreTopTable() 11365 { 11366 return (bool) $this->_highscore_top_table; 11367 } 11368 11369 /** 11370 * Sets the number of entries which are to be shown in the top-rankings 11371 * table. 11372 * 11373 * @param integer $a_top_num Number of entries in the top-rankings table. 11374 */ 11375 public function setHighscoreTopNum($a_top_num) 11376 { 11377 $this->_highscore_top_num = (int) $a_top_num; 11378 } 11379 11380 /** 11381 * Gets the number of entries which are to be shown in the top-rankings table. 11382 * Default: 10 entries 11383 * 11384 * @param integer $a_retval Optional return value if nothing is set, defaults to 10. 11385 * 11386 * @return integer Number of entries to be shown in the top-rankings table. 11387 */ 11388 public function getHighscoreTopNum($a_retval = 10) 11389 { 11390 $retval = $a_retval; 11391 if ((int) $this->_highscore_top_num != 0) { 11392 $retval = $this->_highscore_top_num; 11393 } 11394 11395 return $retval; 11396 } 11397 11398 /** 11399 * @return int 11400 */ 11401 public function getHighscoreMode() 11402 { 11403 switch (true) { 11404 case $this->getHighscoreOwnTable() && $this->getHighscoreTopTable(): 11405 return self::HIGHSCORE_SHOW_ALL_TABLES; 11406 break; 11407 11408 case $this->getHighscoreTopTable(): 11409 return self::HIGHSCORE_SHOW_TOP_TABLE; 11410 break; 11411 11412 case $this->getHighscoreOwnTable(): 11413 default: 11414 return self::HIGHSCORE_SHOW_OWN_TABLE; 11415 break; 11416 } 11417 } 11418 11419 /** 11420 * @param $mode int 11421 */ 11422 public function setHighscoreMode($mode) 11423 { 11424 switch ($mode) { 11425 case self::HIGHSCORE_SHOW_ALL_TABLES: 11426 $this->setHighscoreTopTable(1); 11427 $this->setHighscoreOwnTable(1); 11428 break; 11429 11430 case self::HIGHSCORE_SHOW_TOP_TABLE: 11431 $this->setHighscoreTopTable(1); 11432 $this->setHighscoreOwnTable(0); 11433 break; 11434 11435 case self::HIGHSCORE_SHOW_OWN_TABLE: 11436 default: 11437 $this->setHighscoreTopTable(0); 11438 $this->setHighscoreOwnTable(1); 11439 break; 11440 } 11441 } 11442 /* End GET/SET for highscore feature*/ 11443 11444 public function setSpecificAnswerFeedback($specific_answer_feedback) 11445 { 11446 switch ($specific_answer_feedback) { 11447 case 1: 11448 $this->specific_answer_feedback = 1; 11449 break; 11450 default: 11451 $this->specific_answer_feedback = 0; 11452 break; 11453 } 11454 } 11455 11456 public function getSpecificAnswerFeedback() 11457 { 11458 switch ($this->specific_answer_feedback) { 11459 case 1: 11460 return 1; 11461 default: 11462 return 0; 11463 } 11464 } 11465 11466 /** 11467 * sets obligations enabled/disabled 11468 * 11469 * @param boolean $obligationsEnabled 11470 */ 11471 public function setObligationsEnabled($obligationsEnabled = true) 11472 { 11473 $this->obligationsEnabled = (bool) $obligationsEnabled; 11474 } 11475 11476 /** 11477 * returns the fact wether obligations are enabled or not 11478 * 11479 * @return boolean 11480 */ 11481 public function areObligationsEnabled() 11482 { 11483 return (bool) $this->obligationsEnabled; 11484 } 11485 11486 /** 11487 * checks wether the obligation for question with given id is possible or not 11488 * 11489 * @param integer $questionId 11490 * @return boolean $obligationPossible 11491 */ 11492 public static function isQuestionObligationPossible($questionId) 11493 { 11494 require_once('Modules/TestQuestionPool/classes/class.assQuestion.php'); 11495 11496 $classConcreteQuestion = assQuestion::_getQuestionType($questionId); 11497 11498 assQuestion::_includeClass($classConcreteQuestion, 0); 11499 11500 // static binder is not at work yet (in PHP < 5.3) 11501 //$obligationPossible = $classConcreteQuestion::isObligationPossible(); 11502 $obligationPossible = call_user_func(array($classConcreteQuestion, 'isObligationPossible'), $questionId); 11503 11504 return $obligationPossible; 11505 } 11506 11507 /** 11508 * checks wether the question with given id is marked as obligatory or not 11509 * 11510 * @param integer $questionId 11511 * @return boolean $obligatory 11512 */ 11513 public static function isQuestionObligatory($question_id) 11514 { 11515 global $DIC; 11516 $ilDB = $DIC['ilDB']; 11517 11518 $rset = $ilDB->queryF('SELECT obligatory FROM tst_test_question WHERE question_fi = %s', array('integer'), array($question_id)); 11519 11520 if ($row = $ilDB->fetchAssoc($rset)) { 11521 return (bool) $row['obligatory']; 11522 } 11523 11524 return false; 11525 } 11526 11527 /** 11528 * checks wether all questions marked as obligatory were answered 11529 * within the test pass with given testId, activeId and pass index 11530 * 11531 * @static 11532 * @access public 11533 * @global ilDBInterface $ilDB 11534 * @param integer $test_id 11535 * @param integer $active_id 11536 * @param integer $pass 11537 * @return boolean $allObligationsAnswered 11538 */ 11539 public static function allObligationsAnswered($test_id, $active_id, $pass) 11540 { 11541 global $DIC; 11542 $ilDB = $DIC['ilDB']; 11543 11544 $rset = $ilDB->queryF( 11545 'SELECT obligations_answered FROM tst_pass_result WHERE active_fi = %s AND pass = %s', 11546 array('integer', 'integer'), 11547 array($active_id, $pass) 11548 ); 11549 11550 if ($row = $ilDB->fetchAssoc($rset)) { 11551 return (bool) $row['obligations_answered']; 11552 } 11553 11554 return !self::hasObligations($test_id); 11555 } 11556 11557 /** 11558 * returns the fact wether the test with given test id 11559 * contains questions markes as obligatory or not 11560 * 11561 * @global ilDBInterface $ilDB 11562 * @param integer $test_id 11563 * @return boolean $hasObligations 11564 */ 11565 public static function hasObligations($test_id) 11566 { 11567 global $DIC; 11568 $ilDB = $DIC['ilDB']; 11569 11570 $rset = $ilDB->queryF( 11571 'SELECT count(*) cnt FROM tst_test_question WHERE test_fi = %s AND obligatory = 1', 11572 array('integer'), 11573 array($test_id) 11574 ); 11575 11576 $row = $ilDB->fetchAssoc($rset); 11577 11578 return (bool) $row['cnt'] > 0; 11579 } 11580 11581 public function setAutosave($autosave) 11582 { 11583 $this->autosave = $autosave; 11584 } 11585 11586 public function getAutosave() 11587 { 11588 return $this->autosave; 11589 } 11590 11591 public function setAutosaveIval($autosave_ival) 11592 { 11593 $this->autosave_ival = $autosave_ival; 11594 } 11595 11596 public function getAutosaveIval() 11597 { 11598 return $this->autosave_ival; 11599 } 11600 11601 /** 11602 * getter for the test setting passDeletionAllowed 11603 * 11604 * @return integer 11605 */ 11606 public function isPassDeletionAllowed() 11607 { 11608 return $this->passDeletionAllowed; 11609 } 11610 11611 /** 11612 * setter for the test setting passDeletionAllowed 11613 * 11614 * @return integer 11615 */ 11616 public function setPassDeletionAllowed($passDeletionAllowed) 11617 { 11618 $this->passDeletionAllowed = (bool) $passDeletionAllowed; 11619 } 11620 11621 #region Examview / PDF Examview 11622 /** 11623 * @param boolean $show_examview_html 11624 */ 11625 public function setShowExamviewHtml($show_examview_html) 11626 { 11627 $this->show_examview_html = $show_examview_html; 11628 } 11629 11630 /** 11631 * @return boolean 11632 */ 11633 public function getShowExamviewHtml() 11634 { 11635 return $this->show_examview_html; 11636 } 11637 11638 /** 11639 * @param boolean $show_examview_pdf 11640 */ 11641 public function setShowExamviewPdf($show_examview_pdf) 11642 { 11643 $this->show_examview_pdf = $show_examview_pdf; 11644 } 11645 11646 /** 11647 * @return boolean 11648 */ 11649 public function getShowExamviewPdf() 11650 { 11651 return $this->show_examview_pdf; 11652 } 11653 11654 /** 11655 * @param boolean $enable_examview 11656 */ 11657 public function setEnableExamview($enable_examview) 11658 { 11659 $this->enable_examview = $enable_examview; 11660 } 11661 11662 /** 11663 * @return boolean 11664 */ 11665 public function getEnableExamview() 11666 { 11667 return $this->enable_examview; 11668 } 11669 11670 #endregion 11671 11672 public function setActivationStartingTime($starting_time = null) 11673 { 11674 $this->activation_starting_time = $starting_time; 11675 } 11676 11677 public function setActivationEndingTime($ending_time = null) 11678 { 11679 $this->activation_ending_time = $ending_time; 11680 } 11681 11682 public function getActivationStartingTime() 11683 { 11684 return (strlen($this->activation_starting_time)) ? $this->activation_starting_time : null; 11685 } 11686 11687 public function getActivationEndingTime() 11688 { 11689 return (strlen($this->activation_ending_time)) ? $this->activation_ending_time : null; 11690 } 11691 11692 /** 11693 * Note, this function should only be used if absolutely necessary, since it perform joins on tables that 11694 * tend to grow huge and returns vast amount of data. If possible, use getStartingTimeOfUser($active_id) instead 11695 * 11696 * @return array 11697 */ 11698 public function getStartingTimeOfParticipants() 11699 { 11700 global $DIC; 11701 $ilDB = $DIC['ilDB']; 11702 11703 $times = array(); 11704 $result = $ilDB->queryF("SELECT tst_times.active_fi, tst_times.started FROM tst_times, tst_active WHERE tst_times.active_fi = tst_active.active_id AND tst_active.test_fi = %s ORDER BY tst_times.tstamp DESC", 11705 array('integer'), 11706 array($this->getTestId()) 11707 ); 11708 while ($row = $ilDB->fetchAssoc($result)) { 11709 $times[$row['active_fi']] = $row['started']; 11710 } 11711 return $times; 11712 } 11713 11714 public function getTimeExtensionsOfParticipants() 11715 { 11716 global $DIC; 11717 $ilDB = $DIC['ilDB']; 11718 11719 $times = array(); 11720 $result = $ilDB->queryF( 11721 "SELECT tst_addtime.active_fi, tst_addtime.additionaltime FROM tst_addtime, tst_active WHERE tst_addtime.active_fi = tst_active.active_id AND tst_active.test_fi = %s", 11722 array('integer'), 11723 array($this->getTestId()) 11724 ); 11725 while ($row = $ilDB->fetchAssoc($result)) { 11726 $times[$row['active_fi']] = $row['additionaltime']; 11727 } 11728 return $times; 11729 } 11730 11731 public function getExtraTime($active_id) 11732 { 11733 global $DIC; 11734 $ilDB = $DIC['ilDB']; 11735 11736 $result = $ilDB->queryF( 11737 "SELECT additionaltime FROM tst_addtime WHERE active_fi = %s", 11738 array('integer'), 11739 array($active_id) 11740 ); 11741 if ($result->numRows() > 0) { 11742 $row = $ilDB->fetchAssoc($result); 11743 return $row['additionaltime']; 11744 } 11745 return 0; 11746 } 11747 11748 public function addExtraTime($active_id, $minutes) 11749 { 11750 global $DIC; /* @var ILIAS\DI\Container $DIC */ 11751 11752 require_once 'Modules/Test/classes/class.ilTestParticipantData.php'; 11753 $participantData = new ilTestParticipantData($DIC->database(), $DIC->language()); 11754 11755 $participantData->setParticipantAccessFilter( 11756 ilTestParticipantAccessFilter::getManageParticipantsUserFilter($this->getRefId()) 11757 ); 11758 11759 if ($active_id) { 11760 $participantData->setActiveIdsFilter(array($active_id)); 11761 } 11762 11763 $participantData->load($this->getTestId()); 11764 11765 foreach ($participantData->getActiveIds() as $active_id) { 11766 $result = $DIC->database()->queryF( 11767 "SELECT active_fi FROM tst_addtime WHERE active_fi = %s", 11768 array('integer'), 11769 array($active_id) 11770 ); 11771 11772 if ($result->numRows() > 0) { 11773 $DIC->database()->manipulateF( 11774 "DELETE FROM tst_addtime WHERE active_fi = %s", 11775 array('integer'), 11776 array($active_id) 11777 ); 11778 } 11779 11780 $DIC->database()->manipulateF( 11781 "UPDATE tst_active SET tries = %s, submitted = %s, submittimestamp = %s WHERE active_id = %s", 11782 array('integer','integer','timestamp','integer'), 11783 array(0, 0, null, $active_id) 11784 ); 11785 11786 $DIC->database()->manipulateF( 11787 "INSERT INTO tst_addtime (active_fi, additionaltime, tstamp) VALUES (%s, %s, %s)", 11788 array('integer','integer','integer'), 11789 array($active_id, $minutes, time()) 11790 ); 11791 11792 require_once 'Modules/Test/classes/class.ilObjAssessmentFolder.php'; 11793 if (ilObjAssessmentFolder::_enabledAssessmentLogging()) { 11794 $this->logAction(sprintf($this->lng->txtlng("assessment", "log_added_extratime", ilObjAssessmentFolder::_getLogLanguage()), $minutes, $active_id)); 11795 } 11796 } 11797 } 11798 11799 /** 11800 * @param boolean $enable_archiving 11801 * 11802 * @return $this 11803 */ 11804 public function setEnableArchiving($enable_archiving) 11805 { 11806 $this->enable_archiving = $enable_archiving; 11807 return $this; 11808 } 11809 11810 /** 11811 * @return boolean 11812 */ 11813 public function getEnableArchiving() 11814 { 11815 return $this->enable_archiving; 11816 } 11817 11818 public function getMaxPassOfTest() 11819 { 11820 /** 11821 * @var $ilDB ilDBInterface 11822 */ 11823 global $DIC; 11824 $ilDB = $DIC['ilDB']; 11825 11826 $query = ' 11827 SELECT MAX(tst_pass_result.pass) + 1 max_res 11828 FROM tst_pass_result 11829 INNER JOIN tst_active ON tst_active.active_id = tst_pass_result.active_fi 11830 WHERE test_fi = ' . $ilDB->quote($this->getTestId(), 'integer') . ' 11831 '; 11832 $res = $ilDB->query($query); 11833 $data = $ilDB->fetchAssoc($res); 11834 return (int) $data['max_res']; 11835 } 11836 11837 /** 11838 * @param $active_id 11839 * @param $pass 11840 * @return array 11841 */ 11842 public static function lookupExamId($active_id, $pass) 11843 { 11844 global $DIC; 11845 $ilDB = $DIC['ilDB']; 11846 11847 $exam_id_query = 'SELECT exam_id FROM tst_pass_result WHERE active_fi = %s AND pass = %s'; 11848 $exam_id_result = $ilDB->queryF($exam_id_query, array( 'integer', 'integer' ), array( $active_id, $pass )); 11849 if ($ilDB->numRows($exam_id_result) == 1) { 11850 $exam_id_row = $ilDB->fetchAssoc($exam_id_result); 11851 11852 if ($exam_id_row['exam_id'] != null) { 11853 return $exam_id_row['exam_id']; 11854 } 11855 } 11856 11857 return null; 11858 } 11859 11860 /** 11861 * @param $active_id 11862 * @param $pass 11863 * @param $test_obj_id 11864 * @return array 11865 */ 11866 public static function buildExamId($active_id, $pass, $test_obj_id = null) 11867 { 11868 global $DIC; 11869 $ilSetting = $DIC['ilSetting']; 11870 11871 $inst_id = $ilSetting->get('inst_id', null); 11872 11873 if ($test_obj_id === null) { 11874 $obj_id = self::_getObjectIDFromActiveID($active_id); 11875 } else { 11876 $obj_id = $test_obj_id; 11877 } 11878 11879 $examId = 'I' . $inst_id . '_T' . $obj_id . '_A' . $active_id . '_P' . $pass; 11880 11881 return $examId; 11882 } 11883 11884 public function setShowExamIdInTestPassEnabled($show_exam_id_in_test_pass_enabled) 11885 { 11886 $this->show_exam_id_in_test_pass_enabled = $show_exam_id_in_test_pass_enabled; 11887 } 11888 11889 public function isShowExamIdInTestPassEnabled() 11890 { 11891 return $this->show_exam_id_in_test_pass_enabled; 11892 } 11893 11894 /** 11895 * @param boolean $show_exam_id 11896 */ 11897 public function setShowExamIdInTestResultsEnabled($show_exam_id_in_test_results_enabled) 11898 { 11899 $this->show_exam_id_in_test_results_enabled = $show_exam_id_in_test_results_enabled; 11900 } 11901 11902 /** 11903 * @return boolean 11904 */ 11905 public function isShowExamIdInTestResultsEnabled() 11906 { 11907 return $this->show_exam_id_in_test_results_enabled; 11908 } 11909 11910 /** 11911 * @param boolean $sign_submission 11912 */ 11913 public function setSignSubmission($sign_submission) 11914 { 11915 $this->sign_submission = $sign_submission; 11916 } 11917 11918 /** 11919 * @return boolean 11920 */ 11921 public function getSignSubmission() 11922 { 11923 return $this->sign_submission; 11924 } 11925 11926 /** 11927 * @param int availability of the special character selector 11928 */ 11929 public function setCharSelectorAvailability($availability) 11930 { 11931 $this->char_selector_availability = (int) $availability; 11932 } 11933 11934 /** 11935 * @return int availability of the special character selector 11936 */ 11937 public function getCharSelectorAvailability() 11938 { 11939 return (int) $this->char_selector_availability; 11940 } 11941 11942 /** 11943 * @param string definition of the special character selector 11944 */ 11945 public function setCharSelectorDefinition($definition = '') 11946 { 11947 $this->char_selector_definition = $definition; 11948 } 11949 11950 /** 11951 * @return string definition of the special character selector 11952 */ 11953 public function getCharSelectorDefinition() 11954 { 11955 return $this->char_selector_definition; 11956 } 11957 11958 11959 /** 11960 * setter for question set type 11961 * 11962 * @param string $questionSetType 11963 */ 11964 public function setQuestionSetType($questionSetType) 11965 { 11966 $this->questionSetType = $questionSetType; 11967 } 11968 11969 /** 11970 * getter for question set type 11971 * 11972 * @return string $questionSetType 11973 */ 11974 public function getQuestionSetType() 11975 { 11976 return $this->questionSetType; 11977 } 11978 11979 /** 11980 * lookup-er for question set type 11981 * 11982 * @global ilDBInterface $ilDB 11983 * @param integer $objId 11984 * @return string $questionSetType 11985 */ 11986 public static function lookupQuestionSetType($objId) 11987 { 11988 global $DIC; 11989 $ilDB = $DIC['ilDB']; 11990 11991 $query = "SELECT question_set_type FROM tst_tests WHERE obj_fi = %s"; 11992 11993 $res = $ilDB->queryF($query, array('integer'), array($objId)); 11994 11995 $questionSetType = null; 11996 11997 while ($row = $ilDB->fetchAssoc($res)) { 11998 $questionSetType = $row['question_set_type']; 11999 } 12000 12001 return $questionSetType; 12002 } 12003 12004 /** 12005 * Returns the fact wether this test is a fixed question set test or not 12006 * 12007 * @return boolean $isFixedTest 12008 */ 12009 public function isFixedTest() 12010 { 12011 return $this->getQuestionSetType() == self::QUESTION_SET_TYPE_FIXED; 12012 } 12013 12014 /** 12015 * Returns the fact wether this test is a random questions test or not 12016 * 12017 * @return boolean $isRandomTest 12018 */ 12019 public function isRandomTest() 12020 { 12021 return $this->getQuestionSetType() == self::QUESTION_SET_TYPE_RANDOM; 12022 } 12023 12024 /** 12025 * Returns the fact wether this test is a dynamic question set test or not 12026 * 12027 * @return boolean $isDynamicTest 12028 */ 12029 public function isDynamicTest() 12030 { 12031 return $this->getQuestionSetType() == self::QUESTION_SET_TYPE_DYNAMIC; 12032 } 12033 12034 /** 12035 * Returns the fact wether the test with passed obj id is a random questions test or not 12036 * 12037 * @param integer $a_obj_id 12038 * @return boolean $isRandomTest 12039 * @deprecated 12040 */ 12041 public static function _lookupRandomTest($a_obj_id) 12042 { 12043 return self::lookupQuestionSetType($a_obj_id) == self::QUESTION_SET_TYPE_RANDOM; 12044 } 12045 12046 public function getQuestionSetTypeTranslation(ilLanguage $lng, $questionSetType) 12047 { 12048 switch ($questionSetType) { 12049 case ilObjTest::QUESTION_SET_TYPE_FIXED: 12050 return $lng->txt('tst_question_set_type_fixed'); 12051 12052 case ilObjTest::QUESTION_SET_TYPE_RANDOM: 12053 return $lng->txt('tst_question_set_type_random'); 12054 12055 case ilObjTest::QUESTION_SET_TYPE_DYNAMIC: 12056 return $lng->txt('tst_question_set_type_dynamic'); 12057 } 12058 12059 throw new ilTestException('invalid question set type value given: ' . $questionSetType); 12060 } 12061 12062 public function participantDataExist() 12063 { 12064 if ($this->participantDataExist === null) { 12065 $this->participantDataExist = (bool) $this->evalTotalPersons(); 12066 } 12067 12068 return $this->participantDataExist; 12069 } 12070 12071 public function recalculateScores($preserve_manscoring = false) 12072 { 12073 require_once 'class.ilTestScoring.php'; 12074 $scoring = new ilTestScoring($this); 12075 $scoring->setPreserveManualScores($preserve_manscoring); 12076 $scoring->recalculateSolutions(); 12077 } 12078 12079 public static function getPoolQuestionChangeListeners(ilDBInterface $db, $poolObjId) 12080 { 12081 require_once 'Modules/Test/classes/class.ilObjTestDynamicQuestionSetConfig.php'; 12082 12083 $questionChangeListeners = array( 12084 ilObjTestDynamicQuestionSetConfig::getPoolQuestionChangeListener($db, $poolObjId) 12085 ); 12086 12087 return $questionChangeListeners; 12088 } 12089 12090 public static function getTestObjIdsWithActiveForUserId($userId) 12091 { 12092 global $DIC; 12093 $ilDB = $DIC['ilDB']; 12094 12095 $query = " 12096 SELECT obj_fi 12097 FROM tst_active 12098 INNER JOIN tst_tests 12099 ON test_id = test_fi 12100 WHERE user_fi = %s 12101 "; 12102 12103 $res = $ilDB->queryF($query, array('integer'), array($userId)); 12104 12105 $objIds = array(); 12106 12107 while ($row = $ilDB->fetchAssoc($res)) { 12108 $objIds[] = (int) $row['obj_fi']; 12109 } 12110 12111 return $objIds; 12112 } 12113 12114 public function setSkillServiceEnabled($skillServiceEnabled) 12115 { 12116 $this->skillServiceEnabled = $skillServiceEnabled; 12117 } 12118 12119 public function isSkillServiceEnabled() 12120 { 12121 return $this->skillServiceEnabled; 12122 } 12123 12124 public function setResultFilterTaxIds($resultFilterTaxIds) 12125 { 12126 $this->resultFilterTaxIds = $resultFilterTaxIds; 12127 } 12128 12129 public function getResultFilterTaxIds() 12130 { 12131 return $this->resultFilterTaxIds; 12132 } 12133 12134 public function isSkillServiceToBeConsidered() 12135 { 12136 if (!$this->isSkillServiceEnabled()) { 12137 return false; 12138 } 12139 12140 if (!self::isSkillManagementGloballyActivated()) { 12141 return false; 12142 } 12143 12144 return true; 12145 } 12146 12147 private static $isSkillManagementGloballyActivated = null; 12148 12149 public static function isSkillManagementGloballyActivated() 12150 { 12151 if (self::$isSkillManagementGloballyActivated === null) { 12152 include_once 'Services/Skill/classes/class.ilSkillManagementSettings.php'; 12153 $skmgSet = new ilSkillManagementSettings(); 12154 12155 self::$isSkillManagementGloballyActivated = $skmgSet->isActivated(); 12156 } 12157 12158 return self::$isSkillManagementGloballyActivated; 12159 } 12160 12161 public function setShowGradingStatusEnabled($showGradingStatusEnabled) 12162 { 12163 $this->showGradingStatusEnabled = $showGradingStatusEnabled; 12164 } 12165 12166 public function isShowGradingStatusEnabled() 12167 { 12168 return $this->showGradingStatusEnabled; 12169 } 12170 12171 public function setShowGradingMarkEnabled($showGradingMarkEnabled) 12172 { 12173 $this->showGradingMarkEnabled = $showGradingMarkEnabled; 12174 } 12175 12176 12177 public function isShowGradingMarkEnabled() 12178 { 12179 return $this->showGradingMarkEnabled; 12180 } 12181 12182 public function setFollowupQuestionAnswerFixationEnabled($followupQuestionAnswerFixationEnabled) 12183 { 12184 $this->followupQuestionAnswerFixationEnabled = $followupQuestionAnswerFixationEnabled; 12185 } 12186 12187 public function isFollowupQuestionAnswerFixationEnabled() 12188 { 12189 return $this->followupQuestionAnswerFixationEnabled; 12190 } 12191 12192 public function setInstantFeedbackAnswerFixationEnabled($instantFeedbackAnswerFixationEnabled) 12193 { 12194 $this->instantFeedbackAnswerFixationEnabled = $instantFeedbackAnswerFixationEnabled; 12195 } 12196 12197 public function isInstantFeedbackAnswerFixationEnabled() 12198 { 12199 return $this->instantFeedbackAnswerFixationEnabled; 12200 } 12201 12202 /** 12203 * @return boolean 12204 */ 12205 public function isForceInstantFeedbackEnabled() 12206 { 12207 return $this->forceInstantFeedbackEnabled; 12208 } 12209 12210 /** 12211 * @param boolean $forceInstantFeedbackEnabled 12212 */ 12213 public function setForceInstantFeedbackEnabled($forceInstantFeedbackEnabled) 12214 { 12215 $this->forceInstantFeedbackEnabled = $forceInstantFeedbackEnabled; 12216 } 12217 12218 public static function ensureParticipantsLastActivePassFinished($testObjId, $userId, $a_force_new_run = false) 12219 { 12220 global $DIC; 12221 $ilDB = $DIC['ilDB']; 12222 $lng = $DIC['lng']; 12223 $ilPluginAdmin = $DIC['ilPluginAdmin']; 12224 12225 /* @var ilObjTest $testOBJ */ 12226 12227 $testOBJ = ilObjectFactory::getInstanceByRefId($testObjId, false); 12228 12229 $activeId = $testOBJ->getActiveIdOfUser($userId); 12230 12231 require_once 'Modules/Test/classes/class.ilTestSessionFactory.php'; 12232 $testSessionFactory = new ilTestSessionFactory($testOBJ); 12233 12234 require_once 'Modules/Test/classes/class.ilTestSequenceFactory.php'; 12235 $testSequenceFactory = new ilTestSequenceFactory($ilDB, $lng, $ilPluginAdmin, $testOBJ); 12236 12237 $testSession = $testSessionFactory->getSession($activeId); 12238 $testSequence = $testSequenceFactory->getSequenceByActiveIdAndPass($activeId, $testSession->getPass()); 12239 $testSequence->loadFromDb(); 12240 12241 // begin-patch lok changed smeyer 12242 if ($a_force_new_run) { 12243 if ($testSequence->hasSequence()) { 12244 $testSession->increasePass(); 12245 } 12246 $testSession->setLastSequence(0); 12247 $testSession->saveToDb(); 12248 } 12249 // end-patch lok 12250 } 12251 12252 public static function isParticipantsLastPassActive($testRefId, $userId) 12253 { 12254 global $DIC; 12255 $ilDB = $DIC['ilDB']; 12256 $lng = $DIC['lng']; 12257 $ilPluginAdmin = $DIC['ilPluginAdmin']; 12258 12259 /* @var ilObjTest $testOBJ */ 12260 12261 $testOBJ = ilObjectFactory::getInstanceByRefId($testRefId, false); 12262 12263 12264 $activeId = $testOBJ->getActiveIdOfUser($userId); 12265 12266 require_once 'Modules/Test/classes/class.ilTestSessionFactory.php'; 12267 $testSessionFactory = new ilTestSessionFactory($testOBJ); 12268 // Added temporarily bugfix smeyer 12269 $testSessionFactory->reset(); 12270 12271 require_once 'Modules/Test/classes/class.ilTestSequenceFactory.php'; 12272 $testSequenceFactory = new ilTestSequenceFactory($ilDB, $lng, $ilPluginAdmin, $testOBJ); 12273 12274 $testSession = $testSessionFactory->getSession($activeId); 12275 $testSequence = $testSequenceFactory->getSequenceByActiveIdAndPass($activeId, $testSession->getPass()); 12276 $testSequence->loadFromDb(); 12277 12278 return $testSequence->hasSequence(); 12279 } 12280 12281 /** 12282 * @return boolean 12283 */ 12284 public function isTestFinalBroken() 12285 { 12286 return $this->testFinalBroken; 12287 } 12288 12289 /** 12290 * @param boolean $testFinalBroken 12291 */ 12292 public function setTestFinalBroken($testFinalBroken) 12293 { 12294 $this->testFinalBroken = $testFinalBroken; 12295 } 12296 12297 public function adjustTestSequence() 12298 { 12299 /** 12300 * @var $ilDB ilDB 12301 */ 12302 global $DIC; 12303 $ilDB = $DIC['ilDB']; 12304 12305 $query = " 12306 SELECT COUNT(test_question_id) cnt 12307 FROM tst_test_question 12308 WHERE test_fi = %s 12309 ORDER BY sequence 12310 "; 12311 12312 $questRes = $ilDB->queryF($query, array('integer'), array($this->getTestId())); 12313 12314 $row = $ilDB->fetchAssoc($questRes); 12315 $questCount = $row['cnt']; 12316 12317 if ($this->getShuffleQuestions()) { 12318 $query = " 12319 SELECT tseq.* 12320 FROM tst_active tac 12321 INNER JOIN tst_sequence tseq 12322 ON tseq.active_fi = tac.active_id 12323 WHERE tac.test_fi = %s 12324 "; 12325 12326 $partRes = $ilDB->queryF( 12327 $query, 12328 array('integer'), 12329 array($this->getTestId()) 12330 ); 12331 12332 while ($row = $ilDB->fetchAssoc($partRes)) { 12333 $sequence = @unserialize($row['sequence']); 12334 12335 if (!$sequence) { 12336 $sequence = array(); 12337 } 12338 12339 $sequence = array_filter($sequence, function ($value) use ($questCount) { 12340 return $value <= $questCount; 12341 }); 12342 12343 $num_seq = count($sequence); 12344 if ($questCount > $num_seq) { 12345 $diff = $questCount - $num_seq; 12346 for ($i = 1; $i <= $diff; $i++) { 12347 $sequence[$num_seq + $i - 1] = $num_seq + $i; 12348 } 12349 } 12350 12351 $new_sequence = serialize($sequence); 12352 12353 $ilDB->update('tst_sequence', array( 12354 'sequence' => array('clob', $new_sequence) 12355 ), array( 12356 'active_fi' => array('integer', $row['active_fi']), 12357 'pass' => array('integer', $row['pass']) 12358 )); 12359 } 12360 } else { 12361 $new_sequence = serialize($questCount > 0 ? range(1, $questCount) : array()); 12362 12363 $query = " 12364 SELECT tseq.* 12365 FROM tst_active tac 12366 INNER JOIN tst_sequence tseq 12367 ON tseq.active_fi = tac.active_id 12368 WHERE tac.test_fi = %s 12369 "; 12370 12371 $part_rest = $ilDB->queryF( 12372 $query, 12373 array('integer'), 12374 array($this->getTestId()) 12375 ); 12376 12377 while ($row = $ilDB->fetchAssoc($part_rest)) { 12378 $ilDB->update('tst_sequence', array( 12379 'sequence' => array('clob', $new_sequence) 12380 ), array( 12381 'active_fi' => array('integer', $row['active_fi']), 12382 'pass' => array('integer', $row['pass']) 12383 )); 12384 } 12385 } 12386 } 12387} 12388