ILIAS  Release_4_1_x_branch Revision 61804
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilObjTest.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
5 include_once "./classes/class.ilObject.php";
6 include_once "./Modules/Test/classes/inc.AssessmentConstants.php";
7 
17 class ilObjTest extends ilObject
18 {
26  protected $_kiosk;
27 
33  var $test_id;
34 
41 
42 
49  var $author;
50 
56  var $metadata;
57 
64 
72 
79 
88 
99 
108 
115 
125 
132 
140 
148 
158 
165 
172 
179 
186 
193 
200 
206  var $ects_fx;
207 
214 
223 
230 
238 
245 
252 
260 
267 
274 
281 
288 
295 
302 
309 
316 
323 
330 
337 
344 
351 
358 
365 
372 
378  private $_showinfo;
379 
385  private $_forcejs;
386 
392  private $_customStyle;
393 
394  protected $mailnotification;
395  protected $mailnottype;
397  protected $exportsettings;
398 
403  private $import_dir;
404 
411  function ilObjTest($a_id = 0,$a_call_by_reference = true)
412  {
413  global $ilUser;
414  $this->type = "tst";
415  include_once "./Modules/Test/classes/class.assMarkSchema.php";
416  $this->mark_schema = new ASS_MarkSchema();
417  $this->test_id = -1;
418  $this->author = $ilUser->fullname;
419  $this->introduction = "";
420  $this->questions = array();
421  $this->sequence_settings = TEST_FIXED_SEQUENCE;
422  $this->score_reporting = REPORT_AFTER_TEST;
423  $this->instant_verification = 0;
424  $this->answer_feedback_points = 0;
425  $this->reporting_date = "";
426  $this->nr_of_tries = 0;
427  $this->_kiosk = 0;
428  $this->use_previous_answers = 1;
429  $this->title_output = 0;
430  $this->starting_time = "";
431  $this->ending_time = "";
432  $this->processing_time = "00:00:00";
433  $this->enable_processing_time = "0";
434  $this->reset_processing_time = 0;
435  $this->ects_output = 0;
436  $this->ects_fx = NULL;
437  $this->random_test = 0;
438  $this->shuffle_questions = FALSE;
439  $this->mailnottype = 0;
440  $this->exportsettings = 0;
441  $this->show_summary = 8;
442  $this->random_question_count = "";
443  $this->count_system = COUNT_PARTIAL_SOLUTIONS;
444  $this->mc_scoring = SCORE_ZERO_POINTS_WHEN_UNANSWERED;
445  $this->score_cutting = SCORE_CUT_QUESTION;
446  $this->pass_scoring = SCORE_LAST_PASS;
447  $this->answer_feedback = 0;
448  $this->password = "";
449  $this->certificate_visibility = 0;
450  $this->allowedUsers = "";
451  $this->_showfinalstatement = FALSE;
452  $this->_finalstatement = "";
453  $this->_showinfo = TRUE;
454  $this->_forcejs = FALSE;
455  $this->_customStyle = "";
456  $this->allowedUsersTimeGap = "";
457  $this->anonymity = 0;
458  $this->show_cancel = 1;
459  $this->show_marker = 0;
460  $this->fixed_participants = 0;
461  $this->setShowPassDetails(TRUE);
462  $this->setShowSolutionDetails(TRUE);
463  $this->setShowSolutionAnswersOnly(FALSE);
464  $this->setShowSolutionSignature(FALSE);
465  $this->testSession = FALSE;
466  $this->testSequence = FALSE;
467  $this->mailnotification = 0;
468  global $lng;
469  $lng->loadLanguageModule("assessment");
470  $this->mark_schema->createSimpleSchema($lng->txt("failed_short"), $lng->txt("failed_official"), 0, 0, $lng->txt("passed_short"), $lng->txt("passed_official"), 50, 1);
471  $this->ects_grades = array(
472  "A" => 90,
473  "B" => 65,
474  "C" => 35,
475  "D" => 10,
476  "E" => 0
477  );
478  $this->ilObject($a_id, $a_call_by_reference);
479  }
480 
484  function create($a_upload = false)
485  {
486  parent::create();
487 
488  // meta data will be created by
489  // import parser
490  if (!$a_upload)
491  {
492  $this->createMetaData();
493  }
494  }
495 
502  function update()
503  {
504  if (!parent::update())
505  {
506  return false;
507  }
508 
509  // put here object specific stuff
510 
511  return true;
512  }
513 
520  function createReference() {
522  $this->saveToDb();
523  return $result;
524  }
525 
531  function read($a_force_db = false)
532  {
533  parent::read($a_force_db);
534  $this->loadFromDb();
535  }
536 
537 
544  function delete()
545  {
546  // always call parent delete function first!!
547  if (!parent::delete())
548  {
549  return false;
550  }
551 
552  // delet meta data
553  $this->deleteMetaData();
554 
555  //put here your module specific stuff
556  $this->deleteTest();
557 
558  return true;
559  }
560 
566  function deleteTest()
567  {
568  global $ilDB;
569 
570  // first of all remove all test editings, because the delete statements used for this
571  // contain a subquery for active ids, that are deleted in the next steps
572  $this->removeAllTestEditings();
573 
574  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s",
575  array('integer'),
576  array($this->getTestId())
577  );
578  $active_array = array();
579  while ($row = $ilDB->fetchAssoc($result))
580  {
581  array_push($active_array, $row["active_id"]);
582  }
583 
584  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
585  array('integer'),
586  array($this->getTestId())
587  );
588 
589  if (count($active_array))
590  {
591  foreach ($active_array as $active_id)
592  {
593  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_times WHERE active_fi = %s",
594  array('integer'),
595  array($active_id)
596  );
597 
598  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
599  array('integer'),
600  array($active_id)
601  );
602  }
603  }
604 
605  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_mark WHERE test_fi = %s",
606  array('integer'),
607  array($this->getTestId())
608  );
609 
610  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s",
611  array('integer'),
612  array($this->getTestId())
613  );
614  while ($row = $ilDB->fetchAssoc($result))
615  {
616  $this->removeQuestion($row["question_fi"]);
617  }
618 
619  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_tests WHERE test_id = %s",
620  array('integer'),
621  array($this->getTestId())
622  );
623 
624  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
625  array('integer'),
626  array($this->getTestId())
627  );
628 
629  // this delete is allready done by call to removeAllTestEditings some lines above
630  /*$affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE tst_test_rnd_qst.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
631  array('integer'),
632  array($this->getTestId())
633  );*/
634 
635  // moved to top of this method because this method performs delete statements,
636  // that use a subquery for active ids, that are allready deleted some lines above
637  //$this->removeAllTestEditings();
638 
639  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
640  array('integer'),
641  array($this->getTestId())
642  );
643 
644  if ($this->isRandomTest())
645  {
647  }
648 
649 
650  // delete export files
651  include_once "./Services/Utilities/classes/class.ilUtil.php";
652  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
653  $directory = $tst_data_dir."/tst_".$this->getId();
654  if (is_dir($directory))
655  {
656  include_once "./Services/Utilities/classes/class.ilUtil.php";
657  ilUtil::delDir($directory);
658  }
659  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
660  $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId());
661  // remaining usages are not in text anymore -> delete them
662  // and media objects (note: delete method of ilObjMediaObject
663  // checks whether object is used in another context; if yes,
664  // the object is not deleted!)
665  foreach($mobs as $mob)
666  {
667  ilObjMediaObject::_removeUsage($mob, "tst:html", $this->getId());
668  if (ilObjMediaObject::_exists($mob))
669  {
670  $mob_obj =& new ilObjMediaObject($mob);
671  $mob_obj->delete();
672  }
673  }
674  }
675 
685  function initDefaultRoles()
686  {
687  global $rbacadmin;
688  return array();
689  }
690 
704  function notify($a_event,$a_ref_id,$a_parent_non_rbac_id,$a_node_id,$a_params = 0)
705  {
706  global $tree;
707 
708  switch ($a_event)
709  {
710  case "link":
711 
712  //var_dump("<pre>",$a_params,"</pre>");
713  //echo "Module name ".$this->getRefId()." triggered by link event. Objects linked into target object ref_id: ".$a_ref_id;
714  //exit;
715  break;
716 
717  case "cut":
718 
719  //echo "Module name ".$this->getRefId()." triggered by cut event. Objects are removed from target object ref_id: ".$a_ref_id;
720  //exit;
721  break;
722 
723  case "copy":
724 
725  //var_dump("<pre>",$a_params,"</pre>");
726  //echo "Module name ".$this->getRefId()." triggered by copy event. Objects are copied into target object ref_id: ".$a_ref_id;
727  //exit;
728  break;
729 
730  case "paste":
731 
732  //echo "Module name ".$this->getRefId()." triggered by paste (cut) event. Objects are pasted into target object ref_id: ".$a_ref_id;
733  //exit;
734  break;
735 
736  case "new":
737 
738  //echo "Module name ".$this->getRefId()." triggered by paste (new) event. Objects are applied to target object ref_id: ".$a_ref_id;
739  //exit;
740  break;
741  }
742 
743  // At the beginning of the recursive process it avoids second call of the notify function with the same parameter
744  if ($a_node_id==$_GET["ref_id"])
745  {
746  $parent_obj =& $this->ilias->obj_factory->getInstanceByRefId($a_node_id);
747  $parent_type = $parent_obj->getType();
748  if ($parent_type == $this->getType())
749  {
750  $a_node_id = (int) $tree->getParentId($a_node_id);
751  }
752  }
753 
754  parent::notify($a_event,$a_ref_id,$a_parent_non_rbac_id,$a_node_id,$a_params);
755  }
756 
763  {
764  include_once "./Services/Utilities/classes/class.ilUtil.php";
765  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
766  ilUtil::makeDir($tst_data_dir);
767  if (!is_writable($tst_data_dir))
768  {
769  $this->ilias->raiseError("Test Data Directory (".$tst_data_dir
770  .") not writeable.",$this->ilias->error_obj->MESSAGE);
771  }
772 
773  // create learning module directory (data_dir/lm_data/lm_<id>)
774  $tst_dir = $tst_data_dir."/tst_".$this->getId();
775  ilUtil::makeDir($tst_dir);
776  if (!@is_dir($tst_dir))
777  {
778  $this->ilias->raiseError("Creation of Test Directory failed.",$this->ilias->error_obj->MESSAGE);
779  }
780  // create Export subdirectory (data_dir/lm_data/lm_<id>/Export)
781  $export_dir = $tst_dir."/export";
782  ilUtil::makeDir($export_dir);
783  if (!@is_dir($export_dir))
784  {
785  $this->ilias->raiseError("Creation of Export Directory failed.",$this->ilias->error_obj->MESSAGE);
786  }
787  }
788 
795  {
796  include_once "./Services/Utilities/classes/class.ilUtil.php";
797  $export_dir = ilUtil::getDataDir()."/tst_data"."/tst_".$this->getId()."/export";
798  return $export_dir;
799  }
800 
808  {
809  // quit if import dir not available
810  if (!@is_dir($dir) or
811  !is_writeable($dir))
812  {
813  return array();
814  }
815 
816  // open directory
817  $dir = dir($dir);
818 
819  // initialize array
820  $file = array();
821 
822  // get files and save the in the array
823  while ($entry = $dir->read())
824  {
825  if ($entry != "." and
826  $entry != ".." and
827  //substr($entry, -4) == ".zip" and
828  ereg("^[0-9]{10}_{2}[0-9]+_{2}(test(__results)?__)*[0-9]+\.[a-z]{1,3}\$", $entry))
829  {
830  $file[] = $entry;
831  }
832  }
833 
834  // close import directory
835  $dir->close();
836 
837  // sort files
838  sort ($file);
839  reset ($file);
840 
841  return $file;
842  }
843 
844 
851  {
852  global $ilias;
853 
854  include_once "./Services/Utilities/classes/class.ilUtil.php";
855  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
856  ilUtil::makeDir($tst_data_dir);
857 
858  if (!is_writable($tst_data_dir))
859  {
860  $ilias->raiseError("Test data directory (".$tst_data_dir
861  .") not writeable.",$ilias->error_obj->FATAL);
862  }
863 
864  // create test directory (data_dir/tst_data/tst_import)
865  $tst_dir = $tst_data_dir."/tst_import";
866  ilUtil::makeDir($tst_dir);
867  if (!@is_dir($tst_dir))
868  {
869  $ilias->raiseError("Creation of test import directory failed.",$ilias->error_obj->FATAL);
870  }
871  }
872 
880  {
881  include_once "./Services/Utilities/classes/class.ilUtil.php";
882  $import_dir = ilUtil::getDataDir()."/tst_data/tst_import";
883  if (@is_dir($import_dir))
884  {
885  return $import_dir;
886  }
887  else
888  {
889  return false;
890  }
891  }
892 
899  {
900  include_once "./Services/Utilities/classes/class.ilUtil.php";
901  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
902  ilUtil::makeDir($tst_data_dir);
903 
904  if (!is_writable($tst_data_dir))
905  {
906  $this->ilias->raiseError("Test Data Directory (".$tst_data_dir
907  .") not writeable.",$this->ilias->error_obj->FATAL);
908  }
909 
910  // create test directory (data_dir/tst_data/tst_import)
911  $tst_dir = $tst_data_dir."/tst_import";
912  ilUtil::makeDir($tst_dir);
913  if (!@is_dir($tst_dir))
914  {
915  $ilias->raiseError("Creation of test import directory failed.",$ilias->error_obj->FATAL);
916  }
917  }
918 
926  {
927  if(strlen($this->import_dir))
928  {
929  return $this->import_dir;
930  }
931 
932  include_once "./Services/Utilities/classes/class.ilUtil.php";
933  $import_dir = ilUtil::getDataDir()."/tst_data/tst_import";
934  if (@is_dir($import_dir))
935  {
936  return $import_dir;
937  }
938  else
939  {
940  return false;
941  }
942  }
943 
949  public function setImportDirectory($a_import_dir)
950  {
951  $this->import_dir = $a_import_dir;
952  }
953 
954 
962  {
963  global $ilDB;
964 
965  $result = $ilDB->queryF("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",
966  array('integer'),
967  array($this->getTestId())
968  );
969  $hasSC = false;
970  while ($row = $ilDB->fetchAssoc($result))
971  {
972  if (strcmp($row['foundtypes'], 'assSingleChoice') == 0)
973  {
974  $hasSC = true;
975  }
976  }
977  return $hasSC;
978  }
979 
987  {
988  global $ilDB;
989 
990  $result = $ilDB->queryF("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",
991  array('integer'),
992  array($this->getTestId())
993  );
994  if ($result->numRows() == 1)
995  {
996  $row = $ilDB->fetchAssoc($result);
997  if (strcmp($row['foundtypes'], 'assSingleChoice') == 0)
998  {
999  return TRUE;
1000  }
1001  else
1002  {
1003  return false;
1004  }
1005  }
1006  return FALSE;
1007  }
1008 
1016  {
1017  global $ilDB;
1018 
1019  if (!$this->hasSingleChoiceQuestions()) return false;
1020 
1021  $result = $ilDB->queryF("SELECT DISTINCT(qpl_qst_sc.shuffle) foundshuffles FROM qpl_questions, qpl_qst_sc, 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 qpl_questions.question_id = qpl_qst_sc.question_fi AND tst_active.test_fi = %s AND qpl_qst_type.type_tag = %s",
1022  array('integer', 'text'),
1023  array($this->getTestId(), 'assSingleChoice')
1024  );
1025  if ($result->numRows() == 1)
1026  {
1027  $row = $ilDB->fetchAssoc($result);
1028  return ($row['foundshuffles'] == 0);
1029  }
1030  return FALSE;
1031  }
1032 
1039  function isComplete()
1040  {
1041  if ((count($this->mark_schema->mark_steps)) and (count($this->questions)))
1042  {
1043  return 1;
1044  }
1045  else
1046  {
1047  if ($this->isRandomTest())
1048  {
1049  $arr = $this->getRandomQuestionpools();
1050  if (count($arr) && ($this->getRandomQuestionCount() > 0))
1051  {
1052  return 1;
1053  }
1054  $count = 0;
1055  foreach ($arr as $array)
1056  {
1057  $count += $array["count"];
1058  }
1059  if ($count)
1060  {
1061  return 1;
1062  }
1063  }
1064  return 0;
1065  }
1066  return 0;
1067  }
1068 
1075  function _isComplete($obj_id)
1076  {
1077  $test = new ilObjTest($obj_id, false);
1078  $test->loadFromDb();
1079  return $test->isComplete();
1080  }
1081 
1087  function saveECTSStatus($ects_output = 0, $fx_support = "", $ects_a = 90, $ects_b = 65, $ects_c = 35, $ects_d = 10, $ects_e = 0)
1088  {
1089  global $ilDB;
1090  if ($this->test_id > 0)
1091  {
1092  $fx_support = preg_replace("/,/", ".", $fx_support);
1093  if (preg_match("/\d+/", $fx_support))
1094  {
1095  $fx_support = $fx_support;
1096  }
1097  else
1098  {
1099  $fx_support = NULL;
1100  }
1101  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET ects_output = %s, ects_a = %s, ects_b = %s, ects_c = %s, ects_d = %s, ects_e = %s, ects_fx = %s WHERE test_id = %s",
1102  array('text','float','float','float','float','float','float','integer'),
1103  array($ects_output, $ects_a, $ects_b, $ects_c, $ects_d, $ects_e, $fx_support, $this->getTestId())
1104  );
1105  $this->ects_output = $ects_output;
1106  $this->ects_fx = $fx_support;
1107  }
1108  }
1109 
1116  {
1117  global $ilDB;
1118 
1119  $complete = 0;
1120  if ($this->isComplete())
1121  {
1122  $complete = 1;
1123  }
1124  if ($this->test_id > 0)
1125  {
1126  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET complete = %s WHERE test_id = %s",
1127  array('text','integer'),
1128  array($complete, $this->test_id)
1129  );
1130  }
1131  }
1132 
1138  function getAllRTEContent()
1139  {
1140  $result = array();
1141  array_push($result, $this->getIntroduction());
1142  array_push($result, $this->getFinalStatement());
1143  return $result;
1144  }
1145 
1152  {
1153  include_once("./Services/RTE/classes/class.ilRTE.php");
1154  $completecontent = "";
1155  foreach ($this->getAllRTEContent() as $content)
1156  {
1157  $completecontent .= $content;
1158  }
1159  ilRTE::_cleanupMediaObjectUsage($completecontent, $this->getType() . ":html",
1160  $this->getId());
1161  }
1162 
1169  function saveToDb($properties_only = FALSE)
1170  {
1171  global $ilDB, $ilLog;
1172 
1173  // cleanup RTE images
1174  $this->cleanupMediaobjectUsage();
1175 
1176  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1177  if ($this->test_id == -1)
1178  {
1179  // Create new dataset
1180  $next_id = $ilDB->nextId('tst_tests');
1181  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_tests (test_id, obj_fi, author, introduction, " .
1182  "finalstatement, showinfo, forcejs, customstyle, showfinalstatement, sequence_settings, " .
1183  "score_reporting, instant_verification, answer_feedback_points, answer_feedback, anonymity, show_cancel, show_marker, " .
1184  "fixed_participants, nr_of_tries, kiosk, use_previous_answers, title_output, processing_time, enable_processing_time, " .
1185  "reset_processing_time, reporting_date, starting_time, ending_time, complete, ects_output, ects_a, ects_b, ects_c, ects_d, " .
1186  "ects_e, ects_fx, random_test, random_question_count, count_system, mc_scoring, score_cutting, pass_scoring, " .
1187  "shuffle_questions, results_presentation, show_summary, password, allowedusers, mailnottype, exportsettings, " .
1188  "alloweduserstimegap, certificate_visibility, mailnotification, created, tstamp) " .
1189  "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, " .
1190  "%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
1191  array(
1192  'integer', 'integer', 'text', 'text',
1193  'text', 'integer', 'integer', 'text', 'integer', 'integer',
1194  'integer', 'text', 'text', 'text', 'text', 'text', 'integer',
1195  'text', 'integer', 'integer', 'text', 'text', 'text', 'text',
1196  'integer', 'text', 'text', 'text', 'text', 'text', 'float', 'float', 'float', 'float',
1197  'float', 'float', 'text', 'integer', 'text', 'text', 'text', 'text',
1198  'text', 'integer', 'integer', 'text', 'integer', 'integer', 'integer',
1199  'integer', 'text', 'integer', 'integer', 'integer'
1200  ),
1201  array(
1202  $next_id,
1203  $this->getId(),
1204  $this->getAuthor(),
1207  $this->getShowInfo(),
1208  $this->getForceJS(),
1209  $this->getCustomStyle(),
1210  $this->getShowFinalStatement(),
1211  $this->getSequenceSettings(),
1212  $this->getScoreReporting(),
1213  $this->getInstantFeedbackSolution(),
1214  $this->getAnswerFeedbackPoints(),
1215  $this->getAnswerFeedback(),
1216  $this->getAnonymity(),
1217  $this->getShowCancel(),
1218  $this->getShowMarker(),
1219  $this->getFixedParticipants(),
1220  $this->getNrOfTries(),
1221  $this->getKiosk(),
1222  $this->getUsePreviousAnswers(),
1223  $this->getTitleOutput(),
1224  $this->getProcessingTime(),
1225  $this->getEnableProcessingTime(),
1226  $this->getResetProcessingTime(),
1227  $this->getReportingDate(),
1228  $this->getStartingTime(),
1229  $this->getEndingTime(),
1230  $this->isComplete(),
1231  $this->getECTSOutput(),
1232  strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : NULL,
1233  strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : NULL,
1234  strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : NULL,
1235  strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : NULL,
1236  strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : NULL,
1237  $this->getECTSFX(),
1238  $this->isRandomTest(),
1239  $this->getRandomQuestionCount(),
1240  $this->getCountSystem(),
1241  $this->getMCScoring(),
1242  $this->getScoreCutting(),
1243  $this->getPassScoring(),
1244  $this->getShuffleQuestions(),
1245  $this->getResultsPresentation(),
1246  $this->getListOfQuestionsSettings(),
1247  $this->getPassword(),
1248  $this->getAllowedUsers(),
1249  $this->getMailNotificationType(),
1250  $this->getExportSettings(),
1251  $this->getAllowedUsersTimeGap(),
1252  $this->getCertificateVisibility(),
1253  $this->getMailNotification(),
1254  time(),
1255  time()
1256  )
1257  );
1258  $this->test_id = $next_id;
1259 
1261  {
1262  $this->logAction($this->lng->txtlng("assessment", "log_create_new_test", ilObjAssessmentFolder::_getLogLanguage()));
1263  }
1264  }
1265  else
1266  {
1267  // Modify existing dataset
1268  $oldrow = array();
1270  {
1271  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
1272  array('integer'),
1273  array($this->test_id)
1274  );
1275  if ($result->numRows() == 1)
1276  {
1277  $oldrow = $ilDB->fetchAssoc($result);
1278  }
1279  }
1280 
1281  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET author = %s, introduction = %s, " .
1282  "finalstatement = %s, showinfo = %s, forcejs = %s, customstyle = %s, showfinalstatement = %s, sequence_settings = %s, " .
1283  "score_reporting = %s, instant_verification = %s, answer_feedback_points = %s, answer_feedback = %s, anonymity = %s, show_cancel = %s, show_marker = %s, " .
1284  "fixed_participants = %s, nr_of_tries = %s, kiosk = %s, use_previous_answers = %s, title_output = %s, processing_time = %s, enable_processing_time = %s, " .
1285  "reset_processing_time = %s, reporting_date = %s, starting_time = %s, ending_time = %s, complete = %s, ects_output = %s, ects_a = %s, ects_b = %s, ects_c = %s, ects_d = %s, " .
1286  "ects_e = %s, ects_fx = %s, random_test = %s, random_question_count = %s, count_system = %s, mc_scoring = %s, score_cutting = %s, pass_scoring = %s, " .
1287  "shuffle_questions = %s, results_presentation = %s, show_summary = %s, password = %s, allowedusers = %s, mailnottype = %s, exportsettings = %s, " .
1288  "alloweduserstimegap = %s, certificate_visibility = %s, mailnotification = %s, tstamp = %s WHERE test_id = %s",
1289  array(
1290  'text', 'text',
1291  'text', 'integer', 'integer', 'text', 'integer', 'integer',
1292  'integer', 'text', 'text', 'text', 'text', 'text', 'integer',
1293  'text', 'integer', 'integer', 'text', 'text', 'text', 'text',
1294  'integer', 'text', 'text', 'text', 'text', 'text', 'float', 'float', 'float', 'float',
1295  'float', 'float', 'text', 'integer', 'text', 'text', 'text', 'text',
1296  'text', 'integer', 'integer', 'text', 'integer','integer', 'integer',
1297  'integer', 'text', 'integer', 'integer', 'integer'
1298  ),
1299  array(
1300  $this->getAuthor(),
1303  $this->getShowInfo(),
1304  $this->getForceJS(),
1305  $this->getCustomStyle(),
1306  $this->getShowFinalStatement(),
1307  $this->getSequenceSettings(),
1308  $this->getScoreReporting(),
1309  $this->getInstantFeedbackSolution(),
1310  $this->getAnswerFeedbackPoints(),
1311  $this->getAnswerFeedback(),
1312  $this->getAnonymity(),
1313  $this->getShowCancel(),
1314  $this->getShowMarker(),
1315  $this->getFixedParticipants(),
1316  $this->getNrOfTries(),
1317  $this->getKiosk(),
1318  $this->getUsePreviousAnswers(),
1319  $this->getTitleOutput(),
1320  $this->getProcessingTime(),
1321  $this->getEnableProcessingTime(),
1322  $this->getResetProcessingTime(),
1323  $this->getReportingDate(),
1324  $this->getStartingTime(),
1325  $this->getEndingTime(),
1326  $this->isComplete(),
1327  $this->getECTSOutput(),
1328  strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : NULL,
1329  strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : NULL,
1330  strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : NULL,
1331  strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : NULL,
1332  strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : NULL,
1333  $this->getECTSFX(),
1334  $this->isRandomTest(),
1335  $this->getRandomQuestionCount(),
1336  $this->getCountSystem(),
1337  $this->getMCScoring(),
1338  $this->getScoreCutting(),
1339  $this->getPassScoring(),
1340  $this->getShuffleQuestions(),
1341  $this->getResultsPresentation(),
1342  $this->getListOfQuestionsSettings(),
1343  $this->getPassword(),
1344  $this->getAllowedUsers(),
1345  $this->getMailNotificationType(),
1346  $this->getExportSettings(),
1347  $this->getAllowedUsersTimeGap(),
1348  $this->getCertificateVisibility(),
1349  $this->getMailNotification(),
1350  time(),
1351  $this->getTestId()
1352  )
1353  );
1354 
1355  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1357  {
1358  $logresult = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
1359  array('integer'),
1360  array($this->getTestId())
1361  );
1362  $newrow = array();
1363  if ($logresult->numRows() == 1)
1364  {
1365  $newrow = $ilDB->fetchAssoc($logresult);
1366  }
1367  $changed_fields = array();
1368  foreach ($oldrow as $key => $value)
1369  {
1370  if (strcmp($oldrow[$key], $newrow[$key]) != 0)
1371  {
1372  array_push($changed_fields, "$key: " . $oldrow[$key] . " => " . $newrow[$key]);
1373  }
1374  }
1375  $changes = join($changed_fields, ", ");
1376  if (count($changed_fields) > 0)
1377  {
1378  $this->logAction($this->lng->txtlng("assessment", "log_modified_test", ilObjAssessmentFolder::_getLogLanguage()) . " [".$changes."]");
1379  }
1380  }
1381  if ($this->evalTotalPersons() > 0)
1382  {
1383  // reset the finished status of participants if the nr of test passes did change
1384  if ($this->getNrOfTries() > 0)
1385  {
1386  // set all unfinished tests with nr of passes >= allowed passes finished
1387  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND tries >= %s AND submitted = %s",
1388  array('integer', 'integer', 'integer'),
1389  array($this->getTestId(), $this->getNrOfTries(), 0)
1390  );
1391  while ($row = $ilDB->fetchAssoc($aresult))
1392  {
1393  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1394  array('integer', 'timestamp', 'integer'),
1395  array(1, date('Y-m-d H:i:s'), $row["active_id"])
1396  );
1397  }
1398 
1399  // set all finished tests with nr of passes < allowed passes not finished
1400  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND tries < %s AND submitted = %s",
1401  array('integer', 'integer', 'integer'),
1402  array($this->getTestId(), $this->getNrOfTries()-1, 1)
1403  );
1404  while ($row = $ilDB->fetchAssoc($aresult))
1405  {
1406  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1407  array('integer', 'timestamp', 'integer'),
1408  array(0, NULL, $row["active_id"])
1409  );
1410  }
1411  }
1412  else
1413  {
1414  // set all finished tests with nr of passes >= allowed passes not finished
1415  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND submitted = %s",
1416  array('integer', 'integer'),
1417  array($this->getTestId(), 1)
1418  );
1419  while ($row = $ilDB->fetchAssoc($aresult))
1420  {
1421  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1422  array('integer', 'timestamp', 'integer'),
1423  array(0, NULL, $row["active_id"])
1424  );
1425  }
1426  }
1427  }
1428  }
1429  if (!$this->isRandomTest())
1430  {
1432  }
1433  if (!$properties_only)
1434  {
1435  if (PEAR::isError($result))
1436  {
1437  global $ilias;
1438  $ilias->raiseError($result->getMessage());
1439  }
1440  else
1441  {
1442  if (!$this->isRandomTest())
1443  {
1444  $this->saveQuestionsToDb();
1445  }
1446  $this->mark_schema->saveToDb($this->test_id);
1447  }
1448  }
1449  }
1450 
1458  {
1459  global $ilDB;
1460 
1461  $oldquestions = array();
1462  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
1464  {
1465  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence",
1466  array('integer'),
1467  array($this->getTestId())
1468  );
1469  if ($result->numRows() > 0)
1470  {
1471  while ($row = $ilDB->fetchAssoc($result))
1472  {
1473  array_push($oldquestions, $row["question_fi"]);
1474  }
1475  }
1476  }
1477 
1478  // delete existing category relations
1479  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
1480  array('integer'),
1481  array($this->getTestId())
1482  );
1483  // create new category relations
1484  foreach ($this->questions as $key => $value)
1485  {
1486  $next_id = $ilDB->nextId('tst_test_question');
1487  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
1488  array('integer','integer', 'integer', 'integer', 'integer'),
1489  array($next_id, $this->getTestId(), $value, $key, time())
1490  );
1491  }
1492  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1494  {
1495  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence",
1496  array('integer'),
1497  array($this->getTestId())
1498  );
1499  $newquestions = array();
1500  if ($result->numRows() > 0)
1501  {
1502  while ($row = $ilDB->fetchAssoc($result))
1503  {
1504  array_push($newquestions, $row["question_fi"]);
1505  }
1506  }
1507  foreach ($oldquestions as $index => $question_id)
1508  {
1509  if (strcmp($newquestions[$index], $question_id) != 0)
1510  {
1511  $pos = array_search($question_id, $newquestions);
1512  if ($pos === FALSE)
1513  {
1514  $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id);
1515  }
1516  else
1517  {
1518  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index+1) . " => " . ($pos+1), $question_id);
1519  }
1520  }
1521  }
1522  foreach ($newquestions as $index => $question_id)
1523  {
1524  if (array_search($question_id, $oldquestions) === FALSE)
1525  {
1526  $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index+1), $question_id);
1527  }
1528  }
1529  }
1530  }
1531 
1535  protected function isNewRandomTest()
1536  {
1537  global $ilDB;
1538  $result = $ilDB->queryF('SELECT copy_id FROM tst_rnd_cpy WHERE tst_fi = %s',
1539  array('integer'),
1540  array($this->getTestId())
1541  );
1542  return $result->numRows() > 0;
1543  }
1544 
1551  function saveRandomQuestion($active_id, $question_id, $pass = NULL, $maxcount)
1552  {
1553  global $ilUser;
1554  global $ilDB;
1555 
1556  if (is_null($pass)) $pass = 0;
1557  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s",
1558  array('integer','integer'),
1559  array($active_id, $pass)
1560  );
1561  if ($result->numRows() < $maxcount)
1562  {
1563  $duplicate_id = $question_id;
1564  if (!$this->isNewRandomTest())
1565  {
1566  $duplicate_id = $this->getRandomQuestionDuplicate($question_id, $active_id);
1567  if ($duplicate_id === FALSE)
1568  {
1569  $duplicate_id = $this->duplicateQuestionForTest($question_id);
1570  }
1571  }
1572  $next_id = $ilDB->nextId('tst_test_rnd_qst');
1573  $result = $ilDB->manipulateF("INSERT INTO tst_test_rnd_qst (test_random_question_id, active_fi, question_fi, sequence, pass, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
1574  array('integer','integer','integer','integer','integer','integer'),
1575  array($next_id,$active_id, $duplicate_id, $result->numRows()+1, $pass, time())
1576  );
1577  }
1578  }
1579 
1589  function getRandomQuestionDuplicate($question_id, $active_id)
1590  {
1591  global $ilDB;
1592 
1593  $result = $ilDB->queryF("SELECT qpl_questions.question_id FROM qpl_questions, tst_test_rnd_qst WHERE qpl_questions.original_id = %s AND tst_test_rnd_qst.question_fi = qpl_questions.question_id AND tst_test_rnd_qst.active_fi = %s",
1594  array('integer', 'integer'),
1595  array($question_id, $active_id)
1596  );
1597  $num = $result->numRows();
1598  if ($num > 0)
1599  {
1600  $row = $ilDB->fetchAssoc($result);
1601  return $row["question_id"];
1602  }
1603  else
1604  {
1605  return FALSE;
1606  }
1607  }
1608 
1614  function getNrOfResultsForPass($active_id, $pass)
1615  {
1616  global $ilDB;
1617 
1618  $result = $ilDB->queryF("SELECT test_result_id FROM tst_test_result WHERE active_fi = %s AND pass = %s",
1619  array('integer','integer'),
1620  array($active_id, $pass)
1621  );
1622  return $result->numRows();
1623  }
1624 
1633  function hasRandomQuestionsForPass($active_id, $pass)
1634  {
1635  global $ilDB;
1636  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s",
1637  array('integer','integer'),
1638  array($active_id, $pass)
1639  );
1640  return ($result->numRows() > 0) ? true : false;
1641  }
1642 
1649  function generateRandomQuestions($active_id, $pass = NULL)
1650  {
1651  global $ilUser;
1652  global $ilDB;
1653 
1654  if ($active_id > 0)
1655  {
1656  if ($this->hasRandomQuestionsForPass($active_id, $pass) > 0)
1657  {
1658  // Something went wrong. Maybe the user pressed the start button twice
1659  // Questions already exist so there is no need to create new questions
1660  return;
1661  }
1662  if ($pass > 0)
1663  {
1664  if ($this->getNrOfResultsForPass($active_id, $pass - 1) == 0)
1665  {
1666  // This means that someone maybe reloaded the test submission page
1667  // If there are no existing results for the previous test, it makes
1668  // no sense to create a new set of random questions
1669  return;
1670  }
1671  }
1672  }
1673  else
1674  {
1675  // This may not happen! If it happens, raise a fatal error...
1676  global $ilias, $ilErr;
1677  $ilias->raiseError(sprintf($this->lng->txt("error_random_question_generation"), $ilUser->getId(), $this->getTestId()), $ilErr->FATAL);
1678  }
1679 
1680  $num = $this->getRandomQuestionCount();
1681  if ($num > 0)
1682  {
1683  $qpls =& $this->getRandomQuestionpools();
1684  $rndquestions = $this->generateRandomPass($num, $qpls, $pass);
1685  $allquestions = array();
1686  foreach ($rndquestions as $question_id)
1687  {
1688  array_push($allquestions, $question_id);
1689  }
1690  if ($this->getShuffleQuestions())
1691  {
1692  srand ((float)microtime()*1000000);
1693  shuffle($allquestions);
1694  }
1695 
1696  $maxcount = 0;
1697  foreach ($qpls as $data)
1698  {
1699  $maxcount += $data["contains"];
1700  }
1701  if ($num > $maxcount) $num = $maxcount;
1702  foreach ($allquestions as $question_id)
1703  {
1704  $this->saveRandomQuestion($active_id, $question_id, $pass, $num);
1705  }
1706  }
1707  else
1708  {
1709  $qpls =& $this->getRandomQuestionpools();
1710  $allquestions = array();
1711  $maxcount = 0;
1712  foreach ($qpls as $key => $value)
1713  {
1714  if ($value["count"] > 0)
1715  {
1716  $rndquestions = $this->generateRandomPass($value["count"], array($value), $pass);
1717  foreach ($rndquestions as $question_id)
1718  {
1719  array_push($allquestions, $question_id);
1720  }
1721  }
1722  $add = ($value["count"] <= $value["contains"]) ? $value["count"] : $value["contains"];
1723  $maxcount += $add;
1724  }
1725  if ($this->getShuffleQuestions())
1726  {
1727  srand ((float)microtime()*1000000);
1728  shuffle($allquestions);
1729  }
1730  foreach ($allquestions as $question_id)
1731  {
1732  $this->saveRandomQuestion($active_id, $question_id, $pass, $maxcount);
1733  }
1734  }
1735  }
1736 
1743  function saveRandomQuestionCount($total_questions = NULL)
1744  {
1745  global $ilDB;
1746 
1747  if (strlen($total_questions))
1748  {
1749  $this->setRandomQuestionCount($total_questions);
1750  }
1751  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET random_question_count = %s, tstamp = %s WHERE test_id = %s",
1752  array('integer', 'integer', 'integer'),
1753  array($this->getRandomQuestionCount(), time(), $this->getTestId())
1754  );
1755  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1757  {
1758  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_total_amount_of_questions", ilObjAssessmentFolder::_getLogLanguage()), $this->getRandomQuestionCount()));
1759  }
1760  }
1761 
1770  {
1771  global $ilDB;
1772 
1773  $qpls = array();
1774  $counter = 0;
1775  $result = $ilDB->queryF("SELECT tst_test_random.*, qpl_questionpool.questioncount FROM tst_test_random, qpl_questionpool WHERE tst_test_random.test_fi = %s AND tst_test_random.questionpool_fi = qpl_questionpool.obj_fi ORDER BY sequence, test_random_id",
1776  array("integer"),
1777  array($this->getTestId())
1778  );
1779  if ($result->numRows())
1780  {
1781  while ($row = $ilDB->fetchAssoc($result))
1782  {
1783  $qpls[$counter] = array(
1784  "index" => $counter,
1785  "count" => $row["num_of_q"],
1786  "qpl" => $row["questionpool_fi"],
1787  "contains" => $row["questioncount"]
1788  );
1789  $counter++;
1790  }
1791  }
1792  return $qpls;
1793  }
1794 
1798  public function saveRandomQuestionpools()
1799  {
1800  global $ilDB;
1801 
1802  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1803  // delete existing random questionpools
1804  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
1805  array('integer'),
1806  array($this->getTestId())
1807  );
1809  {
1810  $this->logAction($this->lng->txtlng("assessment", "log_random_question_pool_deleted", ilObjAssessmentFolder::_getLogLanguage()));
1811  }
1812  // delete existing duplicated questions
1814 
1815  // create new random questionpools
1816  foreach ($this->random_questionpool_data as $idx => $data)
1817  {
1818  if ($data->qpl > 0)
1819  {
1820  // save questionpool information
1821  $next_id = $ilDB->nextId('tst_test_random');
1822  $result = $ilDB->manipulateF("INSERT INTO tst_test_random (test_random_id, test_fi, questionpool_fi, num_of_q, tstamp, sequence) VALUES (%s, %s, %s, %s, %s, %s)",
1823  array('integer','integer', 'integer', 'integer', 'integer', 'integer'),
1824  array($next_id, $this->getTestId(), $data->qpl, $data->count, time(), $idx)
1825  );
1827  {
1828  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_random_question_pool_added", ilObjAssessmentFolder::_getLogLanguage()), $value["title"] . " (" . $value["qpl"] . ")", $value["count"]));
1829  }
1830  // duplicate all questions of the questionpools
1831  $this->duplicateQuestionpoolForTest($data->qpl);
1832  }
1833  }
1834  }
1835 
1839  protected function removeDuplicatedQuestionpools()
1840  {
1841  global $ilDB;
1842 
1843  $result = $ilDB->queryF('SELECT * FROM tst_rnd_cpy WHERE tst_fi = %s',
1844  array('integer'),
1845  array($this->getTestId())
1846  );
1847  while ($row = $ilDB->fetchAssoc($result))
1848  {
1849  $question =& ilObjTest::_instanciateQuestion($row['qst_fi']);
1850  $question->delete($row['qst_fi']);
1851  }
1852 
1853  $affectedRows = $ilDB->manipulateF('DELETE FROM tst_rnd_cpy WHERE tst_fi = %s',
1854  array('integer'),
1855  array($this->getTestId())
1856  );
1857 
1858  $affectedRows = $ilDB->manipulateF('DELETE FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1859  array('integer'),
1860  array($this->getTestId())
1861  );
1862  }
1863 
1867  public function getUsedRandomQuestionpools()
1868  {
1869  global $ilDB;
1870  if ($this->isNewRandomTest())
1871  {
1872  $result = $ilDB->queryF('SELECT tst_rnd_cpy.*, tst_test_random.num_of_q FROM tst_rnd_cpy, tst_test_random WHERE tst_rnd_cpy.tst_fi = %s AND tst_rnd_cpy.tst_fi = tst_test_random.test_fi AND tst_rnd_cpy.qpl_fi = tst_test_random.questionpool_fi',
1873  array('integer'),
1874  array($this->getTestId())
1875  );
1876  $pools = array();
1877  while ($row = $ilDB->fetchAssoc($result))
1878  {
1879  if (is_array($pools[$row['qpl_fi']]))
1880  {
1881  $pools[$row['qpl_fi']]['count']++;
1882  }
1883  else
1884  {
1885  $pools[$row['qpl_fi']]['count'] = 1;
1886  }
1887  $pools[$row['qpl_fi']]['num_of_q'] = $row['num_of_q'];
1888  }
1889  $result = $ilDB->queryF('SELECT * FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1890  array('integer'),
1891  array($this->getTestId())
1892  );
1893 
1894  while ($row = $ilDB->fetchAssoc($result))
1895  {
1896  $pools[$row['qpl_fi']]['title'] = $row['qpl_title'];
1897  }
1898  return $pools;
1899  }
1900  else
1901  {
1902  $result = $ilDB->queryF('SELECT tst_test_random.* FROM tst_test_random WHERE tst_test_random.test_fi = %s ORDER BY sequence, test_random_id',
1903  array('integer'),
1904  array($this->getTestId())
1905  );
1906  $pools = array();
1907  while ($row = $ilDB->fetchAssoc($result))
1908  {
1909  $pools[$row['questionpool_fi']]['count'] = $row['num_of_q'];
1910  $pools[$row['questionpool_fi']]['num_of_q'] = $row['num_of_q'];
1911  }
1912  $result = $ilDB->queryF('SELECT * FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1913  array('integer'),
1914  array($this->getTestId())
1915  );
1916 
1917  while ($row = $ilDB->fetchAssoc($result))
1918  {
1919  $pools[$row['qpl_fi']]['title'] = $row['qpl_title'];
1920  }
1921  return $pools;
1922  }
1923  }
1924 
1928  protected function duplicateQuestionpoolForTest($questionpool_id)
1929  {
1930  global $ilDB;
1931  $result = $ilDB->queryF('SELECT question_id FROM qpl_questions WHERE obj_fi = %s AND complete = %s AND original_id IS NULL',
1932  array('integer', 'text'),
1933  array($questionpool_id, 1)
1934  );
1935  $saved_titles = array();
1936  while ($row = $ilDB->fetchAssoc($result))
1937  {
1938  $question =& ilObjTest::_instanciateQuestion($row['question_id']);
1939  $duplicate_id = $question->duplicate(true);
1940  if ($duplicate_id > 0)
1941  {
1942  $next_id = $ilDB->nextId('tst_rnd_cpy');
1943  $ilDB->manipulateF('INSERT INTO tst_rnd_cpy (copy_id, tst_fi, qst_fi, qpl_fi) VALUES (%s, %s, %s, %s)',
1944  array('integer', 'integer', 'integer', 'integer'),
1945  array($next_id, $this->getTestId(), $duplicate_id, $questionpool_id)
1946  );
1947  if (!array_key_exists($questionpool_id, $saved_titles))
1948  {
1949  $next_id = $ilDB->nextId('tst_rnd_qpl_title');
1950  $ilDB->manipulateF('INSERT INTO tst_rnd_qpl_title (title_id, tst_fi, qpl_fi, qpl_title) VALUES (%s, %s, %s, %s)',
1951  array('integer', 'integer', 'integer', 'text'),
1952  array($next_id, $this->getTestId(), $questionpool_id, ilObject::_lookupTitle($questionpool_id))
1953  );
1954  $saved_titles[$questionpool_id] = 1;
1955  }
1956  }
1957  }
1958  }
1959 
1960  function addRandomQuestionpoolData($count = 0, $qpl = 0, $position)
1961  {
1962  include_once "./Modules/Test/classes/class.ilRandomTestData.php";
1963  if (array_key_exists($position, $this->random_questionpool_data))
1964  {
1965  $newitems = array();
1966  for ($i = 0; $i < $position; $i++)
1967  {
1968  array_push($newitems, $this->random_questionpool_data[$i]);
1969  }
1970  array_push($newitems, new ilRandomTestData($count, $qpl));
1971  for ($i = $position; $i < count($this->random_questionpool_data); $i++)
1972  {
1973  array_push($newitems, $this->random_questionpool_data[$i]);
1974  }
1975  $this->random_questionpool_data = $newitems;
1976  }
1977  else
1978  {
1979  array_push($this->random_questionpool_data, new ilRandomTestData($count, $qpl));
1980  }
1981  }
1982 
1983  function removeRandomQuestionpoolData($position)
1984  {
1985  if (array_key_exists($position, $this->random_questionpool_data))
1986  {
1987  unset($this->random_questionpool_data[$position]);
1988  }
1989  }
1990 
1991  function setRandomQuestionpoolData($a_data)
1992  {
1993  $this->random_questionpool_data = $a_data;
1994  }
1995 
2004  {
2005  if (is_array($this->random_questionpool_data) && count($this->random_questionpool_data)) return $this->random_questionpool_data;
2006 
2007  global $ilDB;
2008 
2009  $qpls = array();
2010  $counter = 0;
2011  $result = $ilDB->queryF("SELECT tst_test_random.*, qpl_questionpool.questioncount FROM tst_test_random, qpl_questionpool WHERE tst_test_random.test_fi = %s AND tst_test_random.questionpool_fi = qpl_questionpool.obj_fi ORDER BY sequence, test_random_id",
2012  array("integer"),
2013  array($this->getTestId())
2014  );
2015  include_once "./Modules/Test/classes/class.ilRandomTestData.php";
2016  if ($result->numRows())
2017  {
2018  while ($row = $ilDB->fetchAssoc($result))
2019  {
2020  array_push($qpls, new ilRandomTestData($row['num_of_q'], $row['questionpool_fi']));
2021  }
2022  }
2023  return $qpls;
2024  }
2025 
2033  function loadFromDb()
2034  {
2035  global $ilDB;
2036 
2037  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE obj_fi = %s",
2038  array('integer'),
2039  array($this->getId())
2040  );
2041  if ($result->numRows() == 1)
2042  {
2043  $data = $ilDB->fetchObject($result);
2044  $this->setTestId($data->test_id);
2045  if (strlen($this->getAuthor()) == 0)
2046  {
2047  $this->saveAuthorToMetadata($data->author);
2048  }
2049  $this->setAuthor($data->author);
2050  include_once("./Services/RTE/classes/class.ilRTE.php");
2051  $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc($data->introduction, 1));
2052  $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc($data->finalstatement, 1));
2053  $this->setShowInfo($data->showinfo);
2054  $this->setForceJS($data->forcejs);
2055  $this->setCustomStyle($data->customstyle);
2056  $this->setShowFinalStatement($data->showfinalstatement);
2057  $this->setSequenceSettings($data->sequence_settings);
2058  $this->setScoreReporting($data->score_reporting);
2059  $this->setInstantFeedbackSolution($data->instant_verification);
2060  $this->setAnswerFeedbackPoints($data->answer_feedback_points);
2061  $this->setAnswerFeedback($data->answer_feedback);
2062  $this->setAnonymity($data->anonymity);
2063  $this->setShowCancel($data->show_cancel);
2064  $this->setShowMarker($data->show_marker);
2065  $this->setFixedParticipants($data->fixed_participants);
2066  $this->setNrOfTries($data->nr_of_tries);
2067  $this->setKiosk($data->kiosk);
2068  $this->setUsePreviousAnswers($data->use_previous_answers);
2069  $this->setTitleOutput($data->title_output);
2070  $this->setProcessingTime($data->processing_time);
2071  $this->setEnableProcessingTime($data->enable_processing_time);
2072  $this->setResetProcessingTime($data->reset_processing_time);
2073  $this->setReportingDate($data->reporting_date);
2074  $this->setShuffleQuestions($data->shuffle_questions);
2075  $this->setResultsPresentation($data->results_presentation);
2076  $this->setListOfQuestionsSettings($data->show_summary);
2077  $this->setStartingTime($data->starting_time);
2078  $this->setEndingTime($data->ending_time);
2079  $this->setECTSOutput($data->ects_output);
2080  $this->setECTSGrades(
2081  array(
2082  "A" => $data->ects_a,
2083  "B" => $data->ects_b,
2084  "C" => $data->ects_c,
2085  "D" => $data->ects_d,
2086  "E" => $data->ects_e
2087  )
2088  );
2089  $this->setECTSFX($data->ects_fx);
2090  $this->setRandomTest($data->random_test);
2091  $this->setRandomQuestionCount($data->random_question_count);
2092  $this->mark_schema->flush();
2093  $this->mark_schema->loadFromDb($this->getTestId());
2094  $this->setCountSystem($data->count_system);
2095  $this->setMCScoring($data->mc_scoring);
2096  $this->setMailNotification($data->mailnotification);
2097  $this->setMailNotificationType($data->mailnottype);
2098  $this->setExportSettings($data->exportsettings);
2099  $this->setScoreCutting($data->score_cutting);
2100  $this->setPassword($data->password);
2101  $this->setAllowedUsers($data->allowedusers);
2102  $this->setAllowedUsersTimeGap($data->alloweduserstimegap);
2103  $this->setPassScoring($data->pass_scoring);
2104  $this->setCertificateVisibility($data->certificate_visibility);
2105  $this->loadQuestions();
2106  }
2107  }
2108 
2115 function loadQuestions($active_id = "", $pass = NULL)
2116 {
2117  global $ilUser;
2118  global $ilDB;
2119 
2120  $this->questions = array();
2121  if (strcmp($active_id, "") == 0)
2122  {
2123  $active_id = $this->getActiveIdOfUser($ilUser->getId());
2124  }
2125  if ($this->isRandomTest())
2126  {
2127  if (is_null($pass))
2128  {
2129  $pass = $this->_getPass($active_id);
2130  }
2131  $result = $ilDB->queryF("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",
2132  array('integer', 'integer'),
2133  array($active_id, $pass)
2134  );
2135  // The following is a fix for random tests prior to ILIAS 3.8. If someone started a random test in ILIAS < 3.8, there
2136  // is only one test pass (pass = 0) in tst_test_rnd_qst while with ILIAS 3.8 there are questions for every test pass.
2137  // To prevent problems with tests started in an older version and continued in ILIAS 3.8, the first pass should be taken if
2138  // no questions are present for a newer pass.
2139  if ($result->numRows() == 0)
2140  {
2141  $result = $ilDB->queryF("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",
2142  array('integer'),
2143  array($active_id)
2144  );
2145  }
2146  }
2147  else
2148  {
2149  $result = $ilDB->queryF("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",
2150  array('integer'),
2151  array($this->test_id)
2152  );
2153  }
2154  $index = 1;
2155  while ($data = $ilDB->fetchAssoc($result))
2156  {
2157  $this->questions[$index++] = $data["question_fi"];
2158  }
2159 }
2160 
2169  {
2170  $this->introduction = $introduction;
2171  }
2172 
2180  public function setFinalStatement($a_statement = "")
2181  {
2182  $this->_finalstatement = $a_statement;
2183  }
2184 
2192  public function setShowInfo($a_info = 1)
2193  {
2194  $this->_showinfo = ($a_info) ? 1 : 0;
2195  }
2196 
2204  public function setForceJS($a_js = 1)
2205  {
2206  $this->_forcejs = ($a_js) ? 1 : 0;
2207  }
2208 
2216  public function setCustomStyle($a_customStyle = NULL)
2217  {
2218  $this->_customStyle = $a_customStyle;
2219  }
2220 
2228  public function getCustomStyle()
2229  {
2230  return (strlen($this->_customStyle)) ? $this->_customStyle : NULL;
2231  }
2232 
2240  public function getCustomStyles()
2241  {
2242  $css_path = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test");
2243  $css_path = str_replace("ta.css", "customstyles", $css_path) . "/";
2244  $customstyles = array();
2245  if (is_dir($css_path))
2246  {
2247  $results = array();
2248  include_once "./Services/Utilities/classes/class.ilFileUtils.php";
2250  if (is_array($results["file"]))
2251  {
2252  foreach ($results["file"] as $filename)
2253  {
2254  if (strpos($filename, ".css"))
2255  {
2256  array_push($customstyles, $filename);
2257  }
2258  }
2259  }
2260  }
2261  return $customstyles;
2262  }
2263 
2271  public function getTestStyleLocation($mode = "output")
2272  {
2273  if (strlen($this->getCustomStyle()))
2274  {
2275  $default = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test");
2276  $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $default);
2277  if (file_exists($custom))
2278  {
2279  $custom = ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2280  $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $custom);
2281  return $custom;
2282  }
2283  else
2284  {
2285  return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2286  }
2287  }
2288  else
2289  {
2290  return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2291  }
2292  }
2293 
2301  public function setShowFinalStatement($show = 0)
2302  {
2303  $this->_showfinalstatement = ($show) ? 1 : 0;
2304  }
2305 
2306 
2314  function isRandomTest()
2315  {
2316  return ($this->random_test) ? 1 : 0;
2317  }
2318 
2319 
2325  public static function _lookupRandomTest($a_obj_id)
2326  {
2327  global $ilDB;
2328 
2329  $query = "SELECT random_test FROM tst_tests ".
2330  "WHERE obj_fi = ".$ilDB->quote($a_obj_id,'integer');
2331  $res = $ilDB->query($query);
2332  while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
2333  {
2334  return $row->random_test ? true : false;
2335  }
2336  return false;
2337  }
2338 
2347  {
2348  return ($this->random_question_count) ? $this->random_question_count : 0;
2349  }
2350 
2357  public function getIntroduction()
2358  {
2359  return (strlen($this->introduction)) ? $this->introduction : NULL;
2360  }
2361 
2368  public function getFinalStatement()
2369  {
2370  return (strlen($this->_finalstatement)) ? $this->_finalstatement : NULL;
2371  }
2372 
2380  public function getShowInfo()
2381  {
2382  return ($this->_showinfo) ? 1 : 0;
2383  }
2384 
2392  public function getForceJS()
2393  {
2394  return ($this->_forcejs) ? 1 : 0;
2395  }
2396 
2404  public function getShowFinalStatement()
2405  {
2406  return ($this->_showfinalstatement) ? 1 : 0;
2407  }
2408 
2416  function getTestId()
2417  {
2418  return $this->test_id;
2419  }
2420 
2428  function getECTSOutput()
2429  {
2430  return ($this->ects_output) ? 1 : 0;
2431  }
2432 
2440  function setECTSOutput($a_ects_output)
2441  {
2442  $this->ects_output = $a_ects_output ? 1 : 0;
2443  }
2444 
2452  function getECTSFX()
2453  {
2454  return (strlen($this->ects_fx)) ? $this->ects_fx : NULL;
2455  }
2456 
2464  function setECTSFX($a_ects_fx)
2465  {
2466  $this->ects_fx = $a_ects_fx;
2467  }
2468 
2476  function &getECTSGrades()
2477  {
2478  return $this->ects_grades;
2479  }
2480 
2488  function setECTSGrades($a_ects_grades)
2489  {
2490  if (is_array($a_ects_grades))
2491  {
2492  $this->ects_grades = $a_ects_grades;
2493  }
2494  }
2495 
2504  {
2505  $this->sequence_settings = $sequence_settings;
2506  }
2507 
2516  {
2517  $this->score_reporting = $score_reporting;
2518  }
2519 
2527  function setInstantFeedbackSolution($instant_feedback = 0)
2528  {
2529  switch ($instant_feedback)
2530  {
2531  case 1:
2532  $this->instant_verification = 1;
2533  break;
2534  default:
2535  $this->instant_verification = 0;
2536  break;
2537  }
2538  }
2539 
2548  {
2549  switch ($answer_feedback)
2550  {
2551  case 1:
2552  $this->answer_feedback = 1;
2553  break;
2554  default:
2555  $this->answer_feedback = 0;
2556  break;
2557  }
2558  }
2559 
2568  {
2569  switch ($answer_feedback_points)
2570  {
2571  case 1:
2572  $this->answer_feedback_points = 1;
2573  break;
2574  default:
2575  $this->answer_feedback_points = 0;
2576  break;
2577  }
2578  }
2579 
2587  function setRandomTest($a_random_test = 0)
2588  {
2589  $this->random_test = $a_random_test;
2590  }
2591 
2599  function setRandomQuestionCount($a_random_question_count = "")
2600  {
2601  $this->random_question_count = $a_random_question_count;
2602  }
2603 
2612  {
2613  if (!$reporting_date)
2614  {
2615  $this->reporting_date = "";
2616  $this->ects_output = 0;
2617  }
2618  else
2619  {
2620  $this->reporting_date = $reporting_date;
2621  }
2622  }
2623 
2632  {
2633  return ($this->sequence_settings) ? $this->sequence_settings : 0;
2634  }
2635 
2644  {
2645  return ($this->score_reporting) ? $this->score_reporting : 0;
2646  }
2647 
2656  {
2657  return ($this->instant_verification) ? $this->instant_verification : 0;
2658  }
2659 
2668  {
2669  return ($this->answer_feedback) ? $this->answer_feedback : 0;
2670  }
2671 
2680  {
2681  return ($this->answer_feedback_points) ? $this->answer_feedback_points : 0;
2682  }
2683 
2691  function getCountSystem()
2692  {
2693  return ($this->count_system) ? $this->count_system : 0;
2694  }
2695 
2703  function _getCountSystem($active_id)
2704  {
2705  global $ilDB;
2706  $result = $ilDB->queryF("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",
2707  array('integer'),
2708  array($active_id)
2709  );
2710  if ($result->numRows())
2711  {
2712  $row = $ilDB->fetchAssoc($result);
2713  return $row["count_system"];
2714  }
2715  return FALSE;
2716  }
2717 
2725  function getMCScoring()
2726  {
2727  return ($this->mc_scoring) ? $this->mc_scoring : 0;
2728  }
2729 
2737  function getScoreCutting()
2738  {
2739  return ($this->score_cutting) ? $this->score_cutting : 0;
2740  }
2741 
2749  function getPassword()
2750  {
2751  return (strlen($this->password)) ? $this->password : NULL;
2752  }
2753 
2761  function getPassScoring()
2762  {
2763  return ($this->pass_scoring) ? $this->pass_scoring : 0;
2764  }
2765 
2773  function _getPassScoring($active_id)
2774  {
2775  global $ilDB;
2776  $result = $ilDB->queryF("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",
2777  array('integer'),
2778  array($active_id)
2779  );
2780  if ($result->numRows())
2781  {
2782  $row = $ilDB->fetchAssoc($result);
2783  return $row["pass_scoring"];
2784  }
2785  return 0;
2786  }
2787 
2795  function _getMCScoring($active_id)
2796  {
2797  global $ilDB;
2798  $result = $ilDB->queryF("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",
2799  array('integer'),
2800  array($active_id)
2801  );
2802  if ($result->numRows())
2803  {
2804  $row = $ilDB->fetchAssoc($result);
2805  return $row["mc_scoring"];
2806  }
2807  return FALSE;
2808  }
2809 
2817  function _getScoreCutting($active_id)
2818  {
2819  global $ilDB;
2820  $result = $ilDB->queryF("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",
2821  array('integer'),
2822  array($active_id)
2823  );
2824  if ($result->numRows())
2825  {
2826  $row = $ilDB->fetchAssoc($result);
2827  return $row["score_cutting"];
2828  }
2829  return FALSE;
2830  }
2831 
2839  function getReportingDate()
2840  {
2841  return (strlen($this->reporting_date)) ? $this->reporting_date : NULL;
2842  }
2843 
2851  function getNrOfTries()
2852  {
2853  return ($this->nr_of_tries) ? $this->nr_of_tries : 0;
2854  }
2855 
2863  function getKiosk()
2864  {
2865  return ($this->_kiosk) ? $this->_kiosk : 0;
2866  }
2867 
2868 
2876  function setKiosk($kiosk = 0)
2877  {
2878  $this->_kiosk = $kiosk;
2879  }
2880 
2888  function getKioskMode()
2889  {
2890  if (($this->_kiosk & 1) > 0)
2891  {
2892  return TRUE;
2893  }
2894  else
2895  {
2896  return FALSE;
2897  }
2898  }
2899 
2907  public function setKioskMode($a_kiosk = FALSE)
2908  {
2909  if ($a_kiosk)
2910  {
2911  $this->_kiosk = $this->_kiosk | 1;
2912  }
2913  else
2914  {
2915  if ($this->getKioskMode())
2916  {
2917  $this->_kiosk = $this->_kiosk ^ 1;
2918  }
2919  }
2920  }
2921 
2929  public function getShowKioskModeTitle()
2930  {
2931  if (($this->_kiosk & 2) > 0)
2932  {
2933  return TRUE;
2934  }
2935  else
2936  {
2937  return FALSE;
2938  }
2939  }
2940 
2947  public function setShowKioskModeTitle($a_title = FALSE)
2948  {
2949  if ($a_title)
2950  {
2951  $this->_kiosk = $this->_kiosk | 2;
2952  }
2953  else
2954  {
2955  if ($this->getShowKioskModeTitle())
2956  {
2957  $this->_kiosk = $this->_kiosk ^ 2;
2958  }
2959  }
2960  }
2961 
2970  {
2971  if (($this->_kiosk & 4) > 0)
2972  {
2973  return TRUE;
2974  }
2975  else
2976  {
2977  return FALSE;
2978  }
2979  }
2980 
2987  public function setShowKioskModeParticipant($a_participant = FALSE)
2988  {
2989  if ($a_participant)
2990  {
2991  $this->_kiosk = $this->_kiosk | 4;
2992  }
2993  else
2994  {
2995  if ($this->getShowKioskModeParticipant())
2996  {
2997  $this->_kiosk = $this->_kiosk ^ 4;
2998  }
2999  }
3000  }
3001 
3010  {
3011  return ($this->use_previous_answers) ? $this->use_previous_answers : 0;
3012  }
3013 
3021  function getTitleOutput()
3022  {
3023  return ($this->title_output) ? $this->title_output : 0;
3024  }
3025 
3034  function _getTitleOutput($active_id)
3035  {
3036  global $ilDB;
3037 
3038  $result = $ilDB->queryF("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",
3039  array('integer'),
3040  array($active_id)
3041  );
3042  if ($result->numRows())
3043  {
3044  $row = $ilDB->fetchAssoc($result);
3045  return $row["title_output"];
3046  }
3047  return 0;
3048  }
3049 
3059  function _getUsePreviousAnswers($active_id, $user_active_user_setting = false)
3060  {
3061  global $ilDB;
3062  global $ilUser;
3063 
3065 
3066  $result = $ilDB->queryF("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",
3067  array("integer"),
3068  array($active_id)
3069  );
3070  if ($result->numRows())
3071  {
3072  $row = $ilDB->fetchAssoc($result);
3073  $use_previous_answers = $row["use_previous_answers"];
3074  }
3075 
3076  if ($use_previous_answers == 1)
3077  {
3078  if ($user_active_user_setting)
3079  {
3080  $res = $ilUser->getPref("tst_use_previous_answers");
3081  if ($res !== FALSE)
3082  {
3084  }
3085  }
3086  }
3087  return $use_previous_answers;
3088  }
3089 
3098  {
3099  return (strlen($this->processing_time)) ? $this->processing_time : NULL;
3100  }
3101 
3108  public function getProcessingTimeAsArray()
3109  {
3110  if (strlen($this->processing_time))
3111  {
3112  if (preg_match("/(\d{2}):(\d{2}):(\d{2})/is", $this->processing_time, $matches))
3113  {
3114  if ((int)$matches[1]+(int)$matches[2]+(int)$matches[3] == 0)
3115  {
3116  return $this->getEstimatedWorkingTime();
3117  }
3118  else
3119  {
3120  return array(
3121  'hh' => $matches[1],
3122  'mm' => $matches[2],
3123  'ss' => $matches[3],
3124  );
3125  }
3126  }
3127  }
3128  return $this->getEstimatedWorkingTime();
3129  }
3130 
3139  {
3140  if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $this->getProcessingTime(), $matches))
3141  {
3142  return ($matches[1] * 3600) + ($matches[2] * 60) + $matches[3];
3143  }
3144  else
3145  {
3146  return 0;
3147  }
3148  }
3149 
3158  {
3159  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndingTime(), $matches))
3160  {
3161  $ending = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
3162  $now = time();
3163  return $ending - $now;
3164  }
3165  else
3166  {
3167  return 0;
3168  }
3169  }
3170 
3179  {
3180  return ($this->enable_processing_time) ? $this->enable_processing_time : 0;
3181  }
3182 
3191  {
3192  return ($this->reset_processing_time) ? $this->reset_processing_time : 0;
3193  }
3194 
3202  function getStartingTime()
3203  {
3204  return (strlen($this->starting_time)) ? $this->starting_time : NULL;
3205  }
3206 
3214  function getEndingTime()
3215  {
3216  return (strlen($this->ending_time)) ? $this->ending_time : NULL;
3217  }
3218 
3227  {
3228  $this->nr_of_tries = $nr_of_tries;
3229  }
3230 
3239  {
3241  {
3242  $this->use_previous_answers = 1;
3243  }
3244  else
3245  {
3246  $this->use_previous_answers = 0;
3247  }
3248  }
3249 
3258  {
3259  switch ($title_output)
3260  {
3261  case 1:
3262  $this->title_output = 1;
3263  break;
3264  case 2:
3265  $this->title_output = 2;
3266  break;
3267  default:
3268  $this->title_output = 0;
3269  break;
3270  }
3271  }
3272 
3280  function setProcessingTime($processing_time = "00:00:00")
3281  {
3282  $this->processing_time = $processing_time;
3283  }
3284 
3292  function setEnableProcessingTime($enable = 0)
3293  {
3294  if ($enable) {
3295  $this->enable_processing_time = "1";
3296  } else {
3297  $this->enable_processing_time = "0";
3298  }
3299  }
3300 
3308  function setResetProcessingTime($reset = 0)
3309  {
3310  if ($reset)
3311  {
3312  $this->reset_processing_time = 1;
3313  }
3314  else
3315  {
3316  $this->reset_processing_time = 0;
3317  }
3318  }
3319 
3328  {
3329  $this->starting_time = $starting_time;
3330  }
3331 
3339  function setEndingTime($ending_time = NULL)
3340  {
3341  $this->ending_time = $ending_time;
3342  }
3343 
3351  function setCountSystem($a_count_system = COUNT_PARTIAL_SOLUTIONS)
3352  {
3353  $this->count_system = $a_count_system;
3354  }
3355 
3363  function setPassword($a_password = NULL)
3364  {
3365  $this->password = $a_password;
3366  }
3367 
3375  function setScoreCutting($a_score_cutting = SCORE_CUT_QUESTION)
3376  {
3377  $this->score_cutting = $a_score_cutting;
3378  }
3379 
3388  {
3389  $this->mc_scoring = $a_mc_scoring;
3390  }
3391 
3399  function setPassScoring($a_pass_scoring = SCORE_LAST_PASS)
3400  {
3401  switch ($a_pass_scoring)
3402  {
3403  case SCORE_BEST_PASS:
3404  $this->pass_scoring = SCORE_BEST_PASS;
3405  break;
3406  default:
3407  $this->pass_scoring = SCORE_LAST_PASS;
3408  break;
3409  }
3410  }
3411 
3419  function removeQuestion($question_id)
3420  {
3421  $question =& ilObjTest::_instanciateQuestion($question_id);
3422  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3424  {
3425  $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id);
3426  }
3427  $question->delete($question_id);
3428  $this->removeAllTestEditings($question_id);
3429  $this->loadQuestions();
3430  $this->saveQuestionsToDb();
3431  }
3432 
3440  public function removeAllTestEditings($question_id = "")
3441  {
3442  global $ilDB;
3443  // remove the question from tst_solutions
3444  if ($question_id)
3445  {
3446  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE tst_solutions.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s) AND tst_solutions.question_fi = %s",
3447  array('integer','integer'),
3448  array($this->getTestId(), $question_id)
3449  );
3450  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE tst_qst_solved.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s) AND tst_qst_solved.question_fi = %s",
3451  array('integer','integer'),
3452  array($this->getTestId(), $question_id)
3453  );
3454  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE tst_test_result.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s) AND tst_test_result.question_fi = %s",
3455  array('integer','integer'),
3456  array($this->getTestId(), $question_id)
3457  );
3458  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE tst_pass_result.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3459  array('integer'),
3460  array($this->getTestId())
3461  );
3462  }
3463  else
3464  {
3465  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE tst_solutions.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3466  array('integer'),
3467  array($this->getTestId())
3468  );
3469  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE tst_qst_solved.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3470  array('integer'),
3471  array($this->getTestId())
3472  );
3473  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE tst_test_result.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3474  array('integer'),
3475  array($this->getTestId())
3476  );
3477  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE tst_pass_result.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3478  array('integer'),
3479  array($this->getTestId())
3480  );
3481  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3483  {
3484  $this->logAction($this->lng->txtlng("assessment", "log_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()));
3485  }
3486  }
3487  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE tst_sequence.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3488  array('integer'),
3489  array($this->getTestId())
3490  );
3491 
3492  if ($this->isRandomTest())
3493  {
3494  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE tst_test_rnd_qst.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3495  array('integer'),
3496  array($this->getTestId())
3497  );
3498  }
3499 
3500  // remove test_active entries, because test has changed
3501  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
3502  array('integer'),
3503  array($this->getTestId())
3504  );
3505 
3506  // remove saved user passwords
3507  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE keyword = %s",
3508  array('text'),
3509  array("tst_password_".$this->getTestId())
3510  );
3511 
3512  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3513  // remove file uploads
3514  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId()))
3515  {
3516  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId());
3517  }
3518  }
3519 
3520  function removeSelectedTestResults($active_ids)
3521  {
3522  global $ilDB;
3523 
3524  // remove the question from tst_solutions
3525  foreach ($active_ids as $active_id)
3526  {
3527  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s",
3528  array('integer'),
3529  array($active_id)
3530  );
3531  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s",
3532  array('integer'),
3533  array($active_id)
3534  );
3535  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE active_fi = %s",
3536  array('integer'),
3537  array($active_id)
3538  );
3539  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE active_fi = %s",
3540  array('integer'),
3541  array($active_id)
3542  );
3543 
3544  if ($this->isRandomTest())
3545  {
3546  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE active_fi = %s",
3547  array('integer'),
3548  array($active_id)
3549  );
3550  }
3551 
3552  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3554  {
3555  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id))));
3556  }
3557  }
3558 
3559  // remove test_active entries of selected users
3560  foreach ($active_ids as $active_id)
3561  {
3562  $usr_id = $this->_getUserIdFromActiveId($active_id);
3563 
3564  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE active_id = %s",
3565  array('integer'),
3566  array($active_id)
3567  );
3568 
3569  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
3570  array('integer'),
3571  array($active_id)
3572  );
3573 
3574  // remove saved user password
3575  if ($usr_id > 0)
3576  {
3577  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE usr_id = %s AND keyword = %s",
3578  array('integer', 'text'),
3579  array($usr_id, "tst_password_".$this->getTestId())
3580  );
3581  }
3582 
3583  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3584  // remove file uploads
3585  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"))
3586  {
3587  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id");
3588  }
3589  }
3590  }
3591 
3592  function removeTestResultsForUser($user_id)
3593  {
3594  global $ilDB;
3595 
3596  $active_id = $this->getActiveIdOfUser($user_id);
3597 
3598  // remove the question from tst_solutions
3599  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s",
3600  array('integer'),
3601  array($active_id)
3602  );
3603  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s",
3604  array('integer'),
3605  array($active_id)
3606  );
3607  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE active_fi = %s",
3608  array('integer'),
3609  array($active_id)
3610  );
3611  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE active_fi = %s",
3612  array('integer'),
3613  array($active_id)
3614  );
3615 
3616  if ($this->isRandomTest())
3617  {
3618  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE active_fi = %s",
3619  array('integer'),
3620  array($active_id)
3621  );
3622  }
3623 
3624  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3626  {
3627  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id))));
3628  }
3629 
3630  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
3631  array('integer'),
3632  array($active_id)
3633  );
3634 
3635  // remove test_active entry
3636  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE active_id = %s",
3637  array('integer'),
3638  array($active_id)
3639  );
3640 
3641  // remove saved user password
3642  if ($user_id > 0)
3643  {
3644  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE usr_id = %s AND keyword = %s",
3645  array('integer', 'text'),
3646  array($user_id, "tst_password_".$this->getTestId())
3647  );
3648  }
3649 
3650  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3651  // remove file uploads
3652  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"))
3653  {
3654  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id");
3655  }
3656  }
3657 
3665  function questionMoveUp($question_id)
3666  {
3667  global $ilDB;
3668 
3669  // Move a question up in sequence
3670  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
3671  array('integer', 'integer'),
3672  array($this->getTestId(), $question_id)
3673  );
3674  $data = $ilDB->fetchObject($result);
3675  if ($data->sequence > 1)
3676  {
3677  // OK, it's not the top question, so move it up
3678  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
3679  array('integer','integer'),
3680  array($this->getTestId(), $data->sequence - 1)
3681  );
3682  $data_previous = $ilDB->fetchObject($result);
3683  // change previous dataset
3684  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3685  array('integer','integer'),
3686  array($data->sequence, $data_previous->test_question_id)
3687  );
3688  // move actual dataset up
3689  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3690  array('integer','integer'),
3691  array($data->sequence - 1, $data->test_question_id)
3692  );
3693  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3695  {
3696  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence-1), $question_id);
3697  }
3698  }
3699  $this->loadQuestions();
3700  }
3701 
3709  function questionMoveDown($question_id)
3710  {
3711  global $ilDB;
3712 
3713  // Move a question down in sequence
3714  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
3715  array('integer','integer'),
3716  array($this->getTestId(), $question_id)
3717  );
3718  $data = $ilDB->fetchObject($result);
3719  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
3720  array('integer','integer'),
3721  array($this->getTestId(), $data->sequence + 1)
3722  );
3723  if ($result->numRows() == 1)
3724  {
3725  // OK, it's not the last question, so move it down
3726  $data_next = $ilDB->fetchObject($result);
3727  // change next dataset
3728  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3729  array('integer','integer'),
3730  array($data->sequence, $data_next->test_question_id)
3731  );
3732  // move actual dataset down
3733  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3734  array('integer','integer'),
3735  array($data->sequence + 1, $data->test_question_id)
3736  );
3737  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3739  {
3740  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence+1), $question_id);
3741  }
3742  }
3743  $this->loadQuestions();
3744  }
3745 
3753  function duplicateQuestionForTest($question_id)
3754  {
3755  global $ilUser;
3756  $question =& ilObjTest::_instanciateQuestion($question_id);
3757  $duplicate_id = $question->duplicate(true);
3758 
3759  return $duplicate_id;
3760  }
3761 
3768  function insertQuestion($question_id)
3769  {
3770  global $ilDB;
3771 
3772  $duplicate_id = $this->duplicateQuestionForTest($question_id);
3773 
3774  // get maximum sequence index in test
3775  $result = $ilDB->queryF("SELECT MAX(sequence) seq FROM tst_test_question WHERE test_fi=%s",
3776  array('integer'),
3777  array($this->getTestId())
3778  );
3779  $sequence = 1;
3780 
3781  if ($result->numRows() == 1)
3782  {
3783  $data = $ilDB->fetchObject($result);
3784  $sequence = $data->seq + 1;
3785  }
3786 
3787  $next_id = $ilDB->nextId('tst_test_question');
3788  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
3789  array('integer', 'integer','integer','integer','integer'),
3790  array($next_id, $this->getTestId(), $duplicate_id, $sequence, time())
3791  );
3792  if ($affectedRows == 1)
3793  {
3794  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3796  {
3797  $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . $sequence, $duplicate_id);
3798  }
3799  }
3800  // remove test_active entries, because test has changed
3801  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
3802  array('integer'),
3803  array($this->getTestId())
3804  );
3805  $this->loadQuestions();
3806  $this->saveCompleteStatus();
3807  }
3808 
3816  function &getQuestionTitles()
3817  {
3818  $titles = array();
3819  if (!$this->isRandomTest())
3820  {
3821  global $ilDB;
3822  $result = $ilDB->queryF("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",
3823  array('integer'),
3824  array($this->getTestId())
3825  );
3826  while ($row = $ilDB->fetchAssoc($result))
3827  {
3828  array_push($titles, $row["title"]);
3829  }
3830  }
3831  return $titles;
3832  }
3833 
3842  {
3843  $titles = array();
3844  if (!$this->isRandomTest())
3845  {
3846  global $ilDB;
3847  $result = $ilDB->queryF("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",
3848  array('integer'),
3849  array($this->getTestId())
3850  );
3851  while ($row = $ilDB->fetchAssoc($result))
3852  {
3853  $titles[$row['question_id']] = $row["title"];
3854  }
3855  }
3856  return $titles;
3857  }
3858 
3868  {
3869  if ($this->getTitleOutput() == 2)
3870  {
3871  return $this->lng->txt("ass_question");
3872  }
3873  else
3874  {
3875  return $title;
3876  }
3877  }
3878 
3887  function getQuestionDataset($question_id)
3888  {
3889  global $ilDB;
3890 
3891  $result = $ilDB->queryF("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",
3892  array('integer'),
3893  array($question_id)
3894  );
3895  $row = $ilDB->fetchObject($result);
3896  return $row;
3897  }
3898 
3905  function &getExistingQuestions($pass = NULL)
3906  {
3907  global $ilUser;
3908  global $ilDB;
3909 
3910  $existing_questions = array();
3911  $active_id = $this->getActiveIdOfUser($ilUser->getId());
3912  if ($this->isRandomTest())
3913  {
3914  if (is_null($pass)) $pass = 0;
3915  $result = $ilDB->queryF("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",
3916  array('integer','integer'),
3917  array($active_id, $pass)
3918  );
3919  }
3920  else
3921  {
3922  $result = $ilDB->queryF("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",
3923  array('integer'),
3924  array($this->getTestId())
3925  );
3926  }
3927  while ($data = $ilDB->fetchObject($result))
3928  {
3929  array_push($existing_questions, $data->original_id);
3930  }
3931  return $existing_questions;
3932  }
3933 
3941  function getQuestionType($question_id)
3942  {
3943  global $ilDB;
3944 
3945  if ($question_id < 1) return -1;
3946  $result = $ilDB->queryF("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",
3947  array('integer'),
3948  array($question_id)
3949  );
3950  if ($result->numRows() == 1)
3951  {
3952  $data = $ilDB->fetchObject($result);
3953  return $data->type_tag;
3954  }
3955  else
3956  {
3957  return "";
3958  }
3959  }
3960 
3967  function startWorkingTime($active_id, $pass)
3968  {
3969  global $ilDB;
3970 
3971  $next_id = $ilDB->nextId('tst_times');
3972  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_times (times_id, active_fi, started, finished, pass, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
3973  array('integer', 'integer', 'timestamp', 'timestamp', 'integer', 'integer'),
3974  array($next_id, $active_id, strftime("%Y-%m-%d %H:%M:%S"), strftime("%Y-%m-%d %H:%M:%S"), $pass, time())
3975  );
3976  return $next_id;
3977  }
3978 
3985  function updateWorkingTime($times_id)
3986  {
3987  global $ilDB;
3988 
3989  $affectedRows = $ilDB->manipulateF("UPDATE tst_times SET finished = %s, tstamp = %s WHERE times_id = %s",
3990  array('timestamp', 'integer', 'integer'),
3991  array(strftime("%Y-%m-%d %H:%M:%S"), time(), $times_id)
3992  );
3993  }
3994 
4001  function &getWorkedQuestions($active_id, $pass = NULL)
4002  {
4003  global $ilUser;
4004  global $ilDB;
4005 
4006  if (is_null($pass))
4007  {
4008  $result = $ilDB->queryF("SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
4009  array('integer','integer'),
4010  array($active_id, 0)
4011  );
4012  }
4013  else
4014  {
4015  $result = $ilDB->queryF("SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
4016  array('integer','integer'),
4017  array($active_id, $pass)
4018  );
4019  }
4020  $result_array = array();
4021  while ($row = $ilDB->fetchAssoc($result))
4022  {
4023  array_push($result_array, $row["question_fi"]);
4024  }
4025  return $result_array;
4026  }
4027 
4036  function isTestFinishedToViewResults($active_id, $currentpass)
4037  {
4038  $num = $this->getPassFinishDate($active_id, $currentpass);
4039  return ((($currentpass > 0) && ($num == 0)) || $this->isTestFinished($active_id)) ? true : false;
4040  }
4041 
4048  function &getAllQuestions($pass = NULL)
4049  {
4050  global $ilUser;
4051  global $ilDB;
4052 
4053  $result_array = array();
4054  if ($this->isRandomTest())
4055  {
4056  $active_id = $this->getActiveIdOfUser($ilUser->getId());
4057  $this->loadQuestions($active_id, $pass);
4058  if (count($this->questions) == 0) return $result_array;
4059  if (is_null($pass))
4060  {
4061  $pass = $this->_getPass($active_id);
4062  }
4063  $result = $ilDB->queryF("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'),
4064  array('integer','integer'),
4065  array($active_id, $pass)
4066  );
4067  }
4068  else
4069  {
4070  if (count($this->questions) == 0) return $result_array;
4071  $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'));
4072  }
4073  while ($row = $ilDB->fetchAssoc($result))
4074  {
4075  $result_array[$row["question_id"]] = $row;
4076  }
4077  return $result_array;
4078  }
4079 
4088  function getActiveIdOfUser($user_id = "", $anonymous_id = "")
4089  {
4090  global $ilDB;
4091  global $ilUser;
4092 
4093  if (!$user_id) $user_id = $ilUser->getId();
4094  if (($_SESSION["AccountId"] == ANONYMOUS_USER_ID) && (strlen($_SESSION["tst_access_code"][$this->getTestId()])))
4095  {
4096  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
4097  array('integer','integer','text'),
4098  array($user_id, $this->test_id, $_SESSION["tst_access_code"][$this->getTestId()])
4099  );
4100  }
4101  else if (strlen($anonymous_id))
4102  {
4103  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
4104  array('integer','integer','text'),
4105  array($user_id, $this->test_id, $anonymous_id)
4106  );
4107  }
4108  else
4109  {
4110  if ($_SESSION["AccountId"] == ANONYMOUS_USER_ID)
4111  {
4112  return NULL;
4113  }
4114  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
4115  array('integer','integer'),
4116  array($user_id, $this->test_id)
4117  );
4118  }
4119  if ($result->numRows())
4120  {
4121  $row = $ilDB->fetchAssoc($result);
4122  return $row["active_id"];
4123  }
4124  else
4125  {
4126  return 0;
4127  }
4128  }
4129 
4138  function _getActiveIdOfUser($user_id = "", $test_id = "")
4139  {
4140  global $ilDB;
4141  global $ilUser;
4142 
4143  if (!$user_id) {
4144  $user_id = $ilUser->id;
4145  }
4146  if (!$test_id)
4147  {
4148  return "";
4149  }
4150  $result = $ilDB->queryF("SELECT tst_active.active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
4151  array('integer', 'integer'),
4152  array($user_id, $test_id)
4153  );
4154  if ($result->numRows())
4155  {
4156  $row = $ilDB->fetchAssoc($result);
4157  return $row["active_id"];
4158  }
4159  else
4160  {
4161  return "";
4162  }
4163  }
4164 
4171  function pcArrayShuffle($array)
4172  {
4173  $keys = array_keys($array);
4174  shuffle($keys);
4175  $result = array();
4176  foreach ($keys as $key)
4177  {
4178  $result[$key] = $array[$key];
4179  }
4180  return $result;
4181  }
4182 
4190  function &getTestResult($active_id, $pass = NULL, $ordered_sequence = FALSE)
4191  {
4192  global $ilDB;
4193 
4194  $results = $this->getResultsForActiveId($active_id);
4195  if (is_null($pass))
4196  {
4197  $pass = $results['pass'];
4198  }
4199  include_once "./Modules/Test/classes/class.ilTestSequence.php";
4200  $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
4201  $sequence = array();
4202  if ($ordered_sequence)
4203  {
4204  $sequence = $testSequence->getOrderedSequenceQuestions();
4205  }
4206  else
4207  {
4208  $sequence = $testSequence->getUserSequenceQuestions();
4209  }
4210  $arrResults = array();
4211  $solutionresult = $ilDB->queryF("SELECT tst_test_result.question_fi, tst_test_result.points reached, tst_solutions.solution_id workedthru FROM tst_test_result LEFT JOIN tst_solutions ON tst_solutions.active_fi = tst_test_result.active_fi AND tst_solutions.question_fi = tst_test_result.question_fi WHERE tst_test_result.active_fi = %s AND tst_test_result.pass = %s",
4212  array('integer', 'integer'),
4213  array($active_id, $pass)
4214  );
4215  while ($row = $ilDB->fetchAssoc($solutionresult))
4216  {
4217  $arrResults[$row['question_fi']] = $row;
4218  }
4219 
4220  require_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
4221  $result = $ilDB->query("SELECT qpl_questions.*, qpl_qst_type.type_tag, qpl_sol_sug.question_fi has_sug_sol FROM qpl_qst_type, qpl_questions LEFT JOIN qpl_sol_sug ON qpl_sol_sug.question_fi = qpl_questions.question_id WHERE qpl_qst_type.question_type_id = qpl_questions.question_type_fi AND " . $ilDB->in('qpl_questions.question_id', $sequence, false, 'integer'));
4222  $found = array();
4223  $unordered = array();
4224  $key = 1;
4225  while ($row = $ilDB->fetchAssoc($result))
4226  {
4227  $percentvalue = ($row['points']) ? $arrResults[$row['question_id']]['reached'] / $row['points'] : 0;
4228  if ($percentvalue < 0) $percentvalue = 0.0;
4229  $data = array(
4230  "nr" => "$key",
4231  "title" => ilUtil::prepareFormOutput($row['title']),
4232  "max" => round($row['points'], 2),
4233  "reached" => round($arrResults[$row['question_id']]['reached'],2),
4234  "percent" => sprintf("%2.2f ", ($percentvalue) * 100) . "%",
4235  "solution" => ($row['has_sug_sol']) ? assQuestion::_getSuggestedSolutionOutput($row['question_id']) : '',
4236  "type" => $row["type_tag"],
4237  "qid" => $row['question_id'],
4238  "original_id" => $row["original_id"],
4239  "workedthrough" => ($arrResults[$row['question_id']]['workedthru']) ? 1 : 0
4240  );
4241  $unordered[$row['question_id']] = $data;
4242  $key++;
4243  }
4244 
4245  $pass_max = 0;
4246  $pass_reached = 0;
4247  $key = 1;
4248  foreach ($sequence as $qid)
4249  {
4250  // building pass point sums based on prepared data
4251  // for question that exists in users qst sequence
4252  $pass_max += round($unordered[$qid]['max'], 2);
4253  $pass_reached += round($unordered[$qid]['reached'], 2);
4254 
4255  // pickup prepared data for question
4256  // that exists in users qst sequence
4257  $unordered[$qid]['nr'] = $key;
4258  array_push($found, $unordered[$qid]);
4259 
4260  // increment key counter
4261  $key++;
4262  }
4263  $unordered = null;
4264  if ($this->getScoreCutting() == 1)
4265  {
4266  if ($results['reached_points'] < 0)
4267  {
4268  $results['reached_points'] = 0;
4269  }
4270  if ($pass_reached < 0) $pass_reached = 0;
4271  }
4272  $found['pass']['total_max_points'] = $pass_max;
4273  $found['pass']['total_reached_points'] = $pass_reached;
4274  $found['pass']['percent'] = ($pass_max > 0) ? $pass_reached / $pass_max : 0;
4275  $found["test"]["total_max_points"] = $results['max_points'];
4276  $found["test"]["total_reached_points"] = $results['reached_points'];
4277  $found["test"]["result_pass"] = $results['pass'];
4278  if ((!$total_reached_points) or (!$total_max_points))
4279  {
4280  $percentage = 0.0;
4281  }
4282  else
4283  {
4284  $percentage = ($total_reached_points / $total_max_points) * 100.0;
4285  if ($percentage < 0) $percentage = 0.0;
4286  }
4287  $found["test"]["passed"] = $results['passed'];
4288  return $found;
4289  }
4290 
4297  function evalTotalPersons()
4298  {
4299  global $ilDB;
4300 
4301  $result = $ilDB->queryF("SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s",
4302  array('integer'),
4303  array($this->getTestId())
4304  );
4305  $row = $ilDB->fetchAssoc($result);
4306  return $row["total"];
4307  }
4308 
4315  function getCompleteWorkingTime($user_id)
4316  {
4317  global $ilDB;
4318 
4319  $result = $ilDB->queryF("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",
4320  array('integer','integer'),
4321  array($this->getTestId(), $user_id)
4322  );
4323  $time = 0;
4324  while ($row = $ilDB->fetchAssoc($result))
4325  {
4326  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4327  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4328  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4329  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4330  $time += ($epoch_2 - $epoch_1);
4331  }
4332  return $time;
4333  }
4334 
4342  {
4343  return $this->_getCompleteWorkingTimeOfParticipants($this->getTestId());
4344  }
4345 
4354  {
4355  global $ilDB;
4356 
4357  $result = $ilDB->queryF("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  {
4365  if (!array_key_exists($row["active_fi"], $times))
4366  {
4367  $times[$row["active_fi"]] = 0;
4368  }
4369  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4370  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4371  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4372  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4373  $times[$row["active_fi"]] += ($epoch_2 - $epoch_1);
4374  }
4375  return $times;
4376  }
4377 
4385  {
4386  global $ilDB;
4387 
4388  $result = $ilDB->queryF("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  {
4395  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4396  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4397  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4398  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4399  $time += ($epoch_2 - $epoch_1);
4400  }
4401  return $time;
4402  }
4403 
4411  {
4412  global $ilDB;
4413 
4414  $result = $ilDB->queryF("SELECT * FROM tst_times WHERE active_fi = %s AND pass = %s ORDER BY started",
4415  array('integer','integer'),
4416  array($active_id, $pass)
4417  );
4418  $time = 0;
4419  while ($row = $ilDB->fetchAssoc($result))
4420  {
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 
4437  function getVisitTimeOfParticipant($active_id)
4438  {
4439  return ilObjTest::_getVisitTimeOfParticipant($this->getTestId(), $active_id);
4440  }
4441 
4450  function _getVisitTimeOfParticipant($test_id, $active_id)
4451  {
4452  global $ilDB;
4453 
4454  $result = $ilDB->queryF("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",
4455  array('integer','integer'),
4456  array($test_id, $active_id)
4457  );
4458  $firstvisit = 0;
4459  $lastvisit = 0;
4460  while ($row = $ilDB->fetchAssoc($result))
4461  {
4462  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4463  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4464  if ($firstvisit == 0 || $epoch_1 < $firstvisit) $firstvisit = $epoch_1;
4465  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4466  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4467  if ($epoch_2 > $lastvisit) $lastvisit = $epoch_2;
4468  }
4469  return array("firstvisit" => $firstvisit, "lastvisit" => $lastvisit);
4470  }
4471 
4478  function &evalStatistical($active_id)
4479  {
4480  global $ilDB;
4481 // global $ilBench;
4482  $pass = ilObjTest::_getResultPass($active_id);
4483  $test_result =& $this->getTestResult($active_id, $pass);
4484  $result = $ilDB->queryF("SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.active_id = %s AND tst_active.active_id = tst_times.active_fi",
4485  array('integer'),
4486  array($active_id)
4487  );
4488  $times = array();
4489  $first_visit = 0;
4490  $last_visit = 0;
4491  while ($row = $ilDB->fetchObject($result))
4492  {
4493  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
4494  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4495  if (!$first_visit) {
4496  $first_visit = $epoch_1;
4497  }
4498  if ($epoch_1 < $first_visit) {
4499  $first_visit = $epoch_1;
4500  }
4501  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
4502  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4503  if (!$last_visit) {
4504  $last_visit = $epoch_2;
4505  }
4506  if ($epoch_2 > $last_visit) {
4507  $last_visit = $epoch_2;
4508  }
4509  $times[$row->active_fi] += ($epoch_2 - $epoch_1);
4510  }
4511  $max_time = 0;
4512  foreach ($times as $key => $value) {
4513  $max_time += $value;
4514  }
4515  if ((!$test_result["test"]["total_reached_points"]) or (!$test_result["test"]["total_max_points"]))
4516  {
4517  $percentage = 0.0;
4518  }
4519  else
4520  {
4521  $percentage = ($test_result["test"]["total_reached_points"] / $test_result["test"]["total_max_points"]) * 100.0;
4522  if ($percentage < 0) $percentage = 0.0;
4523  }
4524  $mark_obj = $this->mark_schema->getMatchingMark($percentage);
4525  $first_date = getdate($first_visit);
4526  $last_date = getdate($last_visit);
4527  $qworkedthrough = 0;
4528  foreach ($test_result as $key => $value)
4529  {
4530  if (preg_match("/\d+/", $key))
4531  {
4532  $qworkedthrough += $value["workedthrough"];
4533  }
4534  }
4535  if (!$qworkedthrough)
4536  {
4537  $atimeofwork = 0;
4538  }
4539  else
4540  {
4541  $atimeofwork = $max_time / $qworkedthrough;
4542  }
4543  $result_mark = "";
4544  $passed = "";
4545  if ($mark_obj)
4546  {
4547  $result_mark = $mark_obj->getShortName();
4548  if ($mark_obj->getPassed())
4549  {
4550  $passed = 1;
4551  }
4552  else
4553  {
4554  $passed = 0;
4555  }
4556  }
4557  $percent_worked_through = 0;
4558  if (count($this->questions))
4559  {
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  {
4578  if (preg_match("/\d+/", $key))
4579  {
4580  $result_array[$key] = $value;
4581  }
4582  }
4583  return $result_array;
4584  }
4585 
4594  {
4595  $totalpoints_array = array();
4596  $all_users =& $this->evalTotalParticipantsArray();
4597  foreach ($all_users as $active_id => $user_name)
4598  {
4599  $test_result =& $this->getTestResult($active_id);
4600  $reached = $test_result["test"]["total_reached_points"];
4601  $total = $test_result["test"]["total_max_points"];
4602  $percentage = $total != 0 ? $reached/$total : 0;
4603  $mark = $this->mark_schema->getMatchingMark($percentage*100.0);
4604  if ($mark)
4605  {
4606  if ($mark->getPassed())
4607  {
4608  array_push($totalpoints_array, $test_result["test"]["total_reached_points"]);
4609  }
4610  }
4611  }
4612  return $totalpoints_array;
4613  }
4614 
4621  function &getParticipants()
4622  {
4623  global $ilDB;
4624  $result = $ilDB->queryF("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",
4625  array('integer'),
4626  array($this->getTestId())
4627  );
4628  $persons_array = array();
4629  while ($row = $ilDB->fetchAssoc($result))
4630  {
4631  $name = $this->lng->txt("unknown");
4632  $fullname = $this->lng->txt("unknown");
4633  $login = "";
4634  if (!$this->getAnonymity())
4635  {
4636  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4637  {
4638  $name = $this->lng->txt("deleted_user");
4639  $fullname = $this->lng->txt("deleted_user");
4640  $login = $this->lng->txt("unknown");
4641  }
4642  else
4643  {
4644  $login = $row["login"];
4645  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4646  {
4647  $name = $this->lng->txt("unknown");
4648  $fullname = $this->lng->txt("unknown");
4649  }
4650  else
4651  {
4652  $name = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]);
4653  $fullname = trim($row["title"] . " " . $row["firstname"] . " " . $row["lastname"]);
4654  }
4655  }
4656  }
4657  $persons_array[$row["active_id"]] = array(
4658  "name" => $name,
4659  "fullname" => $fullname,
4660  "login" => $login
4661  );
4662  }
4663  return $persons_array;
4664  }
4665 
4672  function &evalTotalPersonsArray($name_sort_order = "asc")
4673  {
4674  global $ilDB;
4675  $result = $ilDB->queryF("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),
4676  array('integer'),
4677  array($this->getTestId())
4678  );
4679  $persons_array = array();
4680  while ($row = $ilDB->fetchAssoc($result))
4681  {
4682  if ($this->getAnonymity())
4683  {
4684  $persons_array[$row["active_id"]] = $this->lng->txt("unknown");
4685  }
4686  else
4687  {
4688  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4689  {
4690  $persons_array[$row["active_id"]] = $this->lng->txt("deleted_user");
4691  }
4692  else
4693  {
4694  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4695  {
4696  $persons_array[$row["active_id"]] = $row["lastname"];
4697  }
4698  else
4699  {
4700  $persons_array[$row["active_id"]] = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]);
4701  }
4702  }
4703  }
4704  }
4705  return $persons_array;
4706  }
4707 
4714  function &evalTotalParticipantsArray($name_sort_order = "asc")
4715  {
4716  global $ilDB;
4717  $result = $ilDB->queryF("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),
4718  array('integer'),
4719  array($this->getTestId())
4720  );
4721  $persons_array = array();
4722  while ($row = $ilDB->fetchAssoc($result))
4723  {
4724  if ($this->getAnonymity())
4725  {
4726  $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("unknown"));
4727  }
4728  else
4729  {
4730  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4731  {
4732  $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("deleted_user"));
4733  }
4734  else
4735  {
4736  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4737  {
4738  $persons_array[$row["active_id"]] = array("name" => $row["lastname"]);
4739  }
4740  else
4741  {
4742  $persons_array[$row["active_id"]] = array("name" => trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]), "login" => $row["login"]);
4743  }
4744  }
4745  }
4746  }
4747  return $persons_array;
4748  }
4749 
4757  {
4758  global $ilDB;
4759 
4760  $result = $ilDB->queryF("SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s AND submitted = %s",
4761  array('integer', 'integer'),
4762  array($this->getTestId(), 1)
4763  );
4764  $row = $ilDB->fetchAssoc($result);
4765  return $row["total"];
4766  }
4767 
4774  function &getQuestionsOfTest($active_id)
4775  {
4776  global $ilDB;
4777  if ($this->isRandomTest())
4778  {
4779  $ilDB->setLimit($this->getQuestionCount(), 0);
4780  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " .
4781  "tst_test_rnd_qst.pass, qpl_questions.points " .
4782  "FROM tst_test_rnd_qst, qpl_questions " .
4783  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4784  "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence",
4785  array('integer'),
4786  array($active_id)
4787  );
4788  }
4789  else
4790  {
4791  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4792  "qpl_questions.points " .
4793  "FROM tst_test_question, tst_active, qpl_questions " .
4794  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4795  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi",
4796  array('integer'),
4797  array($active_id)
4798  );
4799  }
4800  $qtest = array();
4801  if ($result->numRows())
4802  {
4803  while ($row = $ilDB->fetchAssoc($result))
4804  {
4805  array_push($qtest, $row);
4806  }
4807  }
4808  return $qtest;
4809  }
4810 
4817  function &getQuestionsOfPass($active_id, $pass)
4818  {
4819  global $ilDB;
4820  if ($this->isRandomTest())
4821  {
4822  $ilDB->setLimit($this->getQuestionCount(), 0);
4823  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " .
4824  "qpl_questions.points " .
4825  "FROM tst_test_rnd_qst, qpl_questions " .
4826  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4827  "AND tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.pass = %s " .
4828  "ORDER BY tst_test_rnd_qst.sequence",
4829  array('integer', 'integer'),
4830  array($active_id, $pass)
4831  );
4832  }
4833  else
4834  {
4835  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4836  "qpl_questions.points " .
4837  "FROM tst_test_question, tst_active, qpl_questions " .
4838  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4839  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi",
4840  array('integer'),
4841  array($active_id)
4842  );
4843  }
4844  $qpass = array();
4845  if ($result->numRows())
4846  {
4847  while ($row = $ilDB->fetchAssoc($result))
4848  {
4849  array_push($qpass, $row);
4850  }
4851  }
4852  return $qpass;
4853  }
4854 
4856  {
4857  global $ilDB;
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  $data = new ilTestEvaluationData($this);
4862  $result = $ilDB->queryF("SELECT tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
4863  "qpl_questions.points maxpoints " .
4864  "FROM tst_test_result, qpl_questions, tst_active " .
4865  "WHERE tst_active.active_id = tst_test_result.active_fi " .
4866  "AND qpl_questions.question_id = tst_test_result.question_fi " .
4867  "AND tst_active.test_fi = %s " .
4868  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
4869  array('integer'),
4870  array($this->getTestId())
4871  );
4872  $pass = NULL;
4873  $checked = array();
4874  $datasets = 0;
4875  while ($row = $ilDB->fetchAssoc($result))
4876  {
4877  $data->getParticipant($row["active_fi"])->getPass($row["pass"])->addAnsweredQuestion($row["original_id"] ? $row["original_id"] : $row["question_fi"], $row["maxpoints"], $row["points"]);
4878  }
4879 
4880  foreach (array_keys($data->getParticipants()) as $active_id)
4881  {
4882  if ($this->isRandomTest())
4883  {
4884  for ($testpass = 0; $testpass <= $data->getParticipant($active_id)->getLastPass(); $testpass++)
4885  {
4886  $ilDB->setLimit($this->getQuestionCount(), 0);
4887  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, qpl_questions.original_id, " .
4888  "tst_test_rnd_qst.pass, qpl_questions.points, qpl_questions.title " .
4889  "FROM tst_test_rnd_qst, qpl_questions " .
4890  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4891  "AND tst_test_rnd_qst.pass = %s " .
4892  "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence",
4893  array('integer','integer'),
4894  array($testpass, $active_id)
4895  );
4896  if ($result->numRows())
4897  {
4898  while ($row = $ilDB->fetchAssoc($result))
4899  {
4900  $tpass = array_key_exists("pass", $row) ? $row["pass"] : 0;
4901  $data->getParticipant($active_id)->addQuestion($row["original_id"] ? $row["original_id"] : $row["question_fi"], $row["question_fi"], $row["points"], $row["sequence"], $tpass);
4902  $data->addQuestionTitle($row["original_id"] ? $row["original_id"] : $row["question_fi"], $row["title"]);
4903  }
4904  }
4905  }
4906  }
4907  else
4908  {
4909  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4910  "qpl_questions.points, qpl_questions.title, qpl_questions.original_id " .
4911  "FROM tst_test_question, tst_active, qpl_questions " .
4912  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4913  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi ORDER BY tst_test_question.sequence",
4914  array('integer'),
4915  array($active_id)
4916  );
4917  if ($result->numRows())
4918  {
4919  $questionsbysequence = array();
4920  while ($row = $ilDB->fetchAssoc($result))
4921  {
4922  $questionsbysequence[$row["sequence"]] = $row;
4923  }
4924  $seqresult = $ilDB->queryF("SELECT * FROM tst_sequence WHERE active_fi = %s",
4925  array('integer'),
4926  array($active_id)
4927  );
4928  while ($seqrow = $ilDB->fetchAssoc($seqresult))
4929  {
4930  $questionsequence = unserialize($seqrow["sequence"]);
4931  foreach ($questionsequence as $sidx => $seq)
4932  {
4933  $qsid = $questionsbysequence[$seq]["original_id"] ? $questionsbysequence[$seq]["original_id"] : $questionsbysequence[$seq]["question_fi"];
4934  $data->getParticipant($active_id)->addQuestion($qsid, $questionsbysequence[$seq]["question_fi"], $questionsbysequence[$seq]["points"], $sidx + 1, $seqrow["pass"]);
4935  $data->addQuestionTitle($qsid, $questionsbysequence[$seq]["title"]);
4936  }
4937  }
4938  }
4939  }
4940  }
4941 
4942  if ($this->ects_output)
4943  {
4944  $passed_array =& $this->getTotalPointsPassedArray();
4945  }
4946  foreach (array_keys($data->getParticipants()) as $active_id)
4947  {
4948  $percentage = $data->getParticipant($active_id)->getReachedPointsInPercent();
4949  $mark = $this->mark_schema->getMatchingMark($percentage);
4950  if (is_object($mark))
4951  {
4952  $data->getParticipant($active_id)->setMark($mark->getShortName());
4953  $data->getParticipant($active_id)->setMarkOfficial($mark->getOfficialName());
4954  $data->getParticipant($active_id)->setPassed($mark->getPassed());
4955  }
4956  if ($this->ects_output)
4957  {
4958  $ects_mark = $this->getECTSGrade($passed_array, $data->getParticipant($active_id)->getReached(), $data->getParticipant($active_id)->getMaxPoints());
4959  $data->getParticipant($active_id)->setECTSMark($ects_mark);
4960  }
4961  $visitingTime =& $this->getVisitTimeOfParticipant($active_id);
4962  $data->getParticipant($active_id)->setFirstVisit($visitingTime["firstvisit"]);
4963  $data->getParticipant($active_id)->setLastVisit($visitingTime["lastvisit"]);
4964  }
4965  return $data;
4966  }
4967 
4969  {
4970  global $ilDB;
4971  $random = ilObjTest::_lookupRandomTestFromActiveId($active_id);
4972  if ($random)
4973  {
4974  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.pass, COUNT(tst_test_rnd_qst.question_fi) qcount, " .
4975  "SUM(qpl_questions.points) qsum FROM tst_test_rnd_qst, qpl_questions " .
4976  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id AND " .
4977  "tst_test_rnd_qst.active_fi = %s and pass = %s GROUP BY tst_test_rnd_qst.active_fi, " .
4978  "tst_test_rnd_qst.pass",
4979  array('integer', 'integer'),
4980  array($active_id, $pass)
4981  );
4982  }
4983  else
4984  {
4985  $result = $ilDB->queryF("SELECT COUNT(tst_test_question.question_fi) qcount, " .
4986  "SUM(qpl_questions.points) qsum FROM tst_test_question, qpl_questions, tst_active " .
4987  "WHERE tst_test_question.question_fi = qpl_questions.question_id AND tst_test_question.test_fi = tst_active.test_fi AND " .
4988  "tst_active.active_id = %s GROUP BY tst_test_question.test_fi",
4989  array('integer'),
4990  array($active_id)
4991  );
4992  }
4993  if ($result->numRows())
4994  {
4995  $row = $ilDB->fetchAssoc($result);
4996  return array("count" => $row["qcount"], "points" => $row["qsum"]);
4997  }
4998  else
4999  {
5000  return array("count" => 0, "points" => 0);
5001  }
5002  }
5003 
5004  function &getCompleteEvaluationData($withStatistics = TRUE, $filterby = "", $filtertext = "")
5005  {
5006  include_once "./Modules/Test/classes/class.ilTestEvaluationData.php";
5007  include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php";
5008  include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php";
5010  if ($withStatistics)
5011  {
5012  $data->calculateStatistics();
5013  }
5014  $data->setFilter($filterby, $filtertext);
5015  return $data;
5016  }
5017 
5025  {
5026  return $this->_evalResultsOverview($this->getTestId());
5027  }
5028 
5036  {
5037  global $ilDB;
5038 
5039  $result = $ilDB->queryF("SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " .
5040  "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
5041  "qpl_questions.points maxpoints " .
5042  "FROM tst_test_result, qpl_questions, tst_active " .
5043  "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " .
5044  "WHERE tst_active.active_id = tst_test_result.active_fi " .
5045  "AND qpl_questions.question_id = tst_test_result.question_fi " .
5046  "AND tst_active.test_fi = %s " .
5047  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
5048  array('integer'),
5049  array($test_id)
5050  );
5051  $overview = array();
5052  while ($row = $ilDB->fetchAssoc($result))
5053  {
5054  if (!array_key_exists($row["active_fi"], $overview))
5055  {
5056  $overview[$row["active_fi"]] = array();
5057  $overview[$row["active_fi"]]["firstname"] = $row["firstname"];
5058  $overview[$row["active_fi"]]["lastname"] = $row["lastname"];
5059  $overview[$row["active_fi"]]["title"] = $row["title"];
5060  $overview[$row["active_fi"]]["login"] = $row["login"];
5061  $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"];
5062  $overview[$row["active_fi"]]["started"] = $row["started"];
5063  $overview[$row["active_fi"]]["finished"] = $row["finished"];
5064  }
5065  if (!array_key_exists($row["pass"], $overview[$row["active_fi"]]))
5066  {
5067  $overview[$row["active_fi"]][$row["pass"]] = array();
5068  $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0;
5069  $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"];
5070  }
5071  array_push($overview[$row["active_fi"]][$row["pass"]], $row);
5072  $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"];
5073  }
5074  return $overview;
5075  }
5076 
5084  function &evalResultsOverviewOfParticipant($active_id)
5085  {
5086  global $ilDB;
5087 
5088  $result = $ilDB->queryF("SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " .
5089  "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
5090  "qpl_questions.points maxpoints " .
5091  "FROM tst_test_result, qpl_questions, tst_active " .
5092  "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " .
5093  "WHERE tst_active.active_id = tst_test_result.active_fi " .
5094  "AND qpl_questions.question_id = tst_test_result.question_fi " .
5095  "AND tst_active.test_fi = %s AND tst_active.active_id = %s" .
5096  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
5097  array('integer', 'integer'),
5098  array($this->getTestId(), $active_id)
5099  );
5100  $overview = array();
5101  while ($row = $ilDB->fetchAssoc($result))
5102  {
5103  if (!array_key_exists($row["active_fi"], $overview))
5104  {
5105  $overview[$row["active_fi"]] = array();
5106  $overview[$row["active_fi"]]["firstname"] = $row["firstname"];
5107  $overview[$row["active_fi"]]["lastname"] = $row["lastname"];
5108  $overview[$row["active_fi"]]["title"] = $row["title"];
5109  $overview[$row["active_fi"]]["login"] = $row["login"];
5110  $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"];
5111  $overview[$row["active_fi"]]["started"] = $row["started"];
5112  $overview[$row["active_fi"]]["finished"] = $row["finished"];
5113  }
5114  if (!array_key_exists($row["pass"], $overview[$row["active_fi"]]))
5115  {
5116  $overview[$row["active_fi"]][$row["pass"]] = array();
5117  $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0;
5118  $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"];
5119  }
5120  array_push($overview[$row["active_fi"]][$row["pass"]], $row);
5121  $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"];
5122  }
5123  return $overview;
5124  }
5125 
5137  function buildName($user_id, $firstname, $lastname, $title)
5138  {
5139  $name = "";
5140  if (strlen($firstname.$lastname.$title) == 0)
5141  {
5142  $name = $this->lng->txt("deleted_user");
5143  }
5144  else
5145  {
5146  if ($user_id == ANONYMOUS_USER_ID)
5147  {
5148  $name = $lastname;
5149  }
5150  else
5151  {
5152  $name = trim($lastname . ", " . $firstname . " " . $title);
5153  }
5154  if ($this->getAnonymity())
5155  {
5156  $name = $this->lng->txt("anonymous");
5157  }
5158  }
5159  return $name;
5160  }
5161 
5174  function _buildName($is_anonymous, $user_id, $firstname, $lastname, $title)
5175  {
5176  global $lng;
5177  $name = "";
5178  if (strlen($firstname.$lastname.$title) == 0)
5179  {
5180  $name = $lng->txt("deleted_user");
5181  }
5182  else
5183  {
5184  if ($user_id == ANONYMOUS_USER_ID)
5185  {
5186  $name = $lastname;
5187  }
5188  else
5189  {
5190  $name = trim($lastname . ", " . $firstname . " " . $title);
5191  }
5192  if ($is_anonymous)
5193  {
5194  $name = $lng->txt("anonymous");
5195  }
5196  }
5197  return $name;
5198  }
5199 
5207  {
5208  global $ilDB;
5209 
5210  $result = $ilDB->queryF("SELECT tst_times.* FROM tst_active, tst_times WHERE tst_active.test_fi = %s AND tst_active.active_id = tst_times.active_fi",
5211  array('integer'),
5212  array($this->getTestId())
5213  );
5214  $times = array();
5215  while ($row = $ilDB->fetchObject($result))
5216  {
5217  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
5218  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5219  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
5220  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5221  $times[$row->active_fi] += ($epoch_2 - $epoch_1);
5222  }
5223  $max_time = 0;
5224  $counter = 0;
5225  foreach ($times as $key => $value)
5226  {
5227  $max_time += $value;
5228  $counter++;
5229  }
5230  if ($counter)
5231  {
5232  $average_time = round($max_time / $counter);
5233  }
5234  else
5235  {
5236  $average_time = 0;
5237  }
5238  return $average_time;
5239  }
5240 
5247  function &getAvailableQuestionpools($use_object_id = false, $equal_points = false, $could_be_offline = false, $show_path = FALSE, $with_questioncount = FALSE, $permission = "read")
5248  {
5249  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5250  return ilObjQuestionPool::_getAvailableQuestionpools($use_object_id, $equal_points, $could_be_offline, $show_path, $with_questioncount, $permission);
5251  }
5252 
5260  {
5261  $time_in_seconds = 0;
5262  foreach ($this->questions as $question_id)
5263  {
5264  $question =& ilObjTest::_instanciateQuestion($question_id);
5265  $est_time = $question->getEstimatedWorkingTime();
5266  $time_in_seconds += $est_time["h"] * 3600 + $est_time["m"] * 60 + $est_time["s"];
5267  }
5268  $hours = (int)($time_in_seconds / 3600) ;
5269  $time_in_seconds = $time_in_seconds - ($hours * 3600);
5270  $minutes = (int)($time_in_seconds / 60);
5271  $time_in_seconds = $time_in_seconds - ($minutes * 60);
5272  $result = array("hh" => $hours, "mm" => $minutes, "ss" => $time_in_seconds);
5273  return $result;
5274  }
5275 
5284  public function generateRandomPass($nr, $qpls, $pass = NULL)
5285  {
5286  global $ilDB;
5287  $qplids = array();
5288  foreach ($qpls as $arr) array_push($qplids, $arr['qpl']);
5289  $result = $ilDB->queryF('SELECT * FROM tst_rnd_cpy WHERE tst_fi = %s AND ' . $ilDB->in('qpl_fi', $qplids, false, 'integer'),
5290  array('integer'),
5291  array($this->getTestId())
5292  );
5293  if ($result->numRows())
5294  {
5295  $ids = array();
5296  while ($row = $ilDB->fetchAssoc($result)) array_push($ids, $row['qst_fi']);
5297  $nr = ($nr > count($ids)) ? count($ids) : $nr;
5298  if ($nr == 0) return array();
5299  $rand_keys = array_rand($ids, $nr);
5300  $selection = array();
5301  if (is_array($rand_keys))
5302  {
5303  foreach ($rand_keys as $key)
5304  {
5305  $selection[$ids[$key]] = $ids[$key];
5306  }
5307  }
5308  else
5309  {
5310  $selection[$ids[$rand_keys]] = $ids[$rand_keys];
5311  }
5312  return $selection;
5313  }
5314  else
5315  {
5316  // old style random questions
5317  return $this->randomSelectQuestions($nr, 0, 1, $qplids, $pass);
5318  }
5319  }
5320 
5331  function randomSelectQuestions($nr_of_questions, $questionpool, $use_obj_id = 0, $qpls = "", $pass = NULL)
5332  {
5333  global $rbacsystem;
5334  global $ilDB;
5335 
5336  // retrieve object id instead of ref id if necessary
5337  if (($questionpool != 0) && (!$use_obj_id)) $questionpool = ilObject::_lookupObjId($questionpool);
5338 
5339  // get original ids of all existing questions in the test
5340  $result = $ilDB->queryF("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",
5341  array("integer"),
5342  array($this->getTestId())
5343  );
5344  $original_ids = array();
5345  $paramtypes = array();
5346  $paramvalues = array();
5347  while ($row = $ilDB->fetchAssoc($result))
5348  {
5349  array_push($original_ids, $row['original_id']);
5350  }
5351 
5352  $available = "";
5353  // get a list of all available questionpools
5354  if (($questionpool == 0) && (!is_array($qpls)))
5355  {
5356  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5357  $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())));
5358  if (count($available_pools))
5359  {
5360  $available = " AND " . $ilDB->in('obj_fi', $available_pools, false, 'integer');
5361  }
5362  else
5363  {
5364  return array();
5365  }
5366  }
5367 
5368  $constraint_qpls = "";
5369  $result_array = array();
5370  if ($questionpool == 0)
5371  {
5372  if (is_array($qpls))
5373  {
5374  if (count($qpls) > 0)
5375  {
5376  $constraint_qpls = " AND " . $ilDB->in('obj_fi', $qpls, false, 'integer');
5377  }
5378  }
5379  }
5380 
5381  $original_clause = "";
5382  if (count($original_ids))
5383  {
5384  $original_clause = " AND " . $ilDB->in('question_id', $original_ids, true, 'integer');
5385  }
5386 
5387  if ($questionpool == 0)
5388  {
5389  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id IS NULL $available $constraint_qpls AND owner > %s AND complete = %s $original_clause",
5390  array('integer', 'text'),
5391  array(0, "1")
5392  );
5393  }
5394  else
5395  {
5396  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id IS NULL AND obj_fi = %s AND owner > %s AND complete = %s $original_clause",
5397  array('integer','integer', 'text'),
5398  array($questionpool, 0, "1")
5399  );
5400  }
5401  $found_ids = array();
5402  while ($row = $ilDB->fetchAssoc($result))
5403  {
5404  array_push($found_ids, $row['question_id']);
5405  }
5406  $nr_of_questions = ($nr_of_questions > count($found_ids)) ? count($found_ids) : $nr_of_questions;
5407  if ($nr_of_questions == 0) return array();
5408  $rand_keys = array_rand($found_ids, $nr_of_questions);
5409  $result = array();
5410  if (is_array($rand_keys))
5411  {
5412  foreach ($rand_keys as $key)
5413  {
5414  $result[$found_ids[$key]] = $found_ids[$key];
5415  }
5416  }
5417  else
5418  {
5419  $result[$found_ids[$rand_keys]] = $found_ids[$rand_keys];
5420  }
5421  return $result;
5422  }
5423 
5430  function getImagePath()
5431  {
5432  return CLIENT_WEB_DIR . "/assessment/" . $this->getId() . "/images/";
5433  }
5434 
5441  function getImagePathWeb()
5442  {
5443  include_once "./Services/Utilities/classes/class.ilUtil.php";
5444  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/" . $this->getId() . "/images/";
5446  }
5447 
5456  function &createQuestionGUI($question_type, $question_id = -1)
5457  {
5458  if ((!$question_type) and ($question_id > 0))
5459  {
5460  $question_type = $this->getQuestionType($question_id);
5461  }
5462  if (!strlen($question_type)) return null;
5463  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
5464  assQuestion::_includeClass($question_type, 1);
5465  $question_type_gui = $question_type . "GUI";
5466  $question = new $question_type_gui();
5467  if ($question_id > 0)
5468  {
5469  $question->object->loadFromDb($question_id);
5470  }
5471  return $question;
5472  }
5473 
5481  function &_instanciateQuestion($question_id)
5482  {
5483  if (strcmp($question_id, "") != 0)
5484  {
5485  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
5486  return assQuestion::_instanciateQuestion($question_id);
5487  }
5488  }
5489 
5498  function moveQuestions($move_questions, $target_index, $insert_mode)
5499  {
5500  $this->questions = array_values($this->questions);
5501  $array_pos = array_search($target_index, $this->questions);
5502  if ($insert_mode == 0)
5503  {
5504  $part1 = array_slice($this->questions, 0, $array_pos);
5505  $part2 = array_slice($this->questions, $array_pos);
5506  }
5507  else if ($insert_mode == 1)
5508  {
5509  $part1 = array_slice($this->questions, 0, $array_pos + 1);
5510  $part2 = array_slice($this->questions, $array_pos + 1);
5511  }
5512  foreach ($move_questions as $question_id)
5513  {
5514  if (!(array_search($question_id, $part1) === FALSE))
5515  {
5516  unset($part1[array_search($question_id, $part1)]);
5517  }
5518  if (!(array_search($question_id, $part2) === FALSE))
5519  {
5520  unset($part2[array_search($question_id, $part2)]);
5521  }
5522  }
5523  $part1 = array_values($part1);
5524  $part2 = array_values($part2);
5525  $new_array = array_values(array_merge($part1, $move_questions, $part2));
5526  $this->questions = array();
5527  $counter = 1;
5528  foreach ($new_array as $question_id)
5529  {
5530  $this->questions[$counter] = $question_id;
5531  $counter++;
5532  }
5533  $this->saveQuestionsToDb();
5534  }
5535 
5536 
5545  {
5546  if ($this->getStartingTime())
5547  {
5548  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getStartingTime(), $matches))
5549  {
5550  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5551  $now = mktime();
5552  if ($now < $epoch_time)
5553  {
5554  // starting time not reached
5555  return false;
5556  }
5557  }
5558  }
5559  return true;
5560  }
5561 
5570  {
5571  if ($this->getEndingTime())
5572  {
5573  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndingTime(), $matches))
5574  {
5575  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5576  $now = mktime();
5577  if ($now > $epoch_time)
5578  {
5579  // ending time reached
5580  return true;
5581  }
5582  }
5583  }
5584  return false;
5585  }
5586 
5592  function getAvailableQuestions($arrFilter, $completeonly = 0)
5593  {
5594  global $ilUser;
5595  global $ilDB;
5596 
5597  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5598  $available_pools = array_keys(ilObjQuestionPool::_getAvailableQuestionpools($use_object_id = TRUE, $equal_points = FALSE, $could_be_offline = FALSE, $showPath = FALSE, $with_questioncount = FALSE));
5599  $available = "";
5600  if (count($available_pools))
5601  {
5602  $available = " AND " . $ilDB->in('qpl_questions.obj_fi', $available_pools, false, 'integer');
5603  }
5604  else
5605  {
5606  return array();
5607  }
5608  if ($completeonly)
5609  {
5610  $available .= " AND qpl_questions.complete = " . $ilDB->quote("1", 'text');
5611  }
5612 
5613  $where = "";
5614  if (is_array($arrFilter))
5615  {
5616  if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title']))
5617  {
5618  $where .= " AND " . $ilDB->like('qpl_questions.title', 'text', "%%" . $arrFilter['title'] . "%%");
5619  }
5620  if (array_key_exists('description', $arrFilter) && strlen($arrFilter['description']))
5621  {
5622  $where .= " AND " . $ilDB->like('qpl_questions.description', 'text', "%%" . $arrFilter['description'] . "%%");
5623  }
5624  if (array_key_exists('author', $arrFilter) && strlen($arrFilter['author']))
5625  {
5626  $where .= " AND " . $ilDB->like('qpl_questions.author', 'text', "%%" . $arrFilter['author'] . "%%");
5627  }
5628  if (array_key_exists('type', $arrFilter) && strlen($arrFilter['type']))
5629  {
5630  $where .= " AND qpl_qst_type.type_tag = " . $ilDB->quote($arrFilter['type'], 'text');
5631  }
5632  if (array_key_exists('qpl', $arrFilter) && strlen($arrFilter['qpl']))
5633  {
5634  $where .= " AND " . $ilDB->like('object_data.title', 'text', "%%" . $arrFilter['qpl'] . "%%");
5635  }
5636  }
5637 
5638  $original_ids =& $this->getExistingQuestions();
5639  $original_clause = " qpl_questions.original_id IS NULL";
5640  if (count($original_ids))
5641  {
5642  $original_clause = " qpl_questions.original_id IS NULL AND " . $ilDB->in('qpl_questions.question_id', $original_ids, true, 'integer');
5643  }
5644 
5645  $query_result = $ilDB->query(
5646  "SELECT qpl_questions.*, qpl_questions.tstamp, qpl_qst_type.type_tag, qpl_qst_type.plugin, object_data.title qpl " .
5647  "FROM qpl_questions, qpl_qst_type, object_data WHERE $original_clause $available AND " .
5648  "object_data.obj_id = qpl_questions.obj_fi AND qpl_questions.tstamp > 0 AND " .
5649  "qpl_questions.question_type_fi = qpl_qst_type.question_type_id$where");
5650  $rows = array();
5651  $types = $this->getQuestionTypeTranslations();
5652  if ($query_result->numRows())
5653  {
5654  while ($row = $ilDB->fetchAssoc($query_result))
5655  {
5656  $row['ttype'] = $types[$row['type_tag']];
5657  if ($row["plugin"])
5658  {
5659  if ($this->isPluginActive($row["type_tag"]))
5660  {
5661  array_push($rows, $row);
5662  }
5663  }
5664  else
5665  {
5666  array_push($rows, $row);
5667  }
5668  }
5669  }
5670  return $rows;
5671  }
5672 
5673  public function &getQuestionTypeTranslations()
5674  {
5675  global $ilDB;
5676  global $lng;
5677  global $ilLog;
5678  global $ilPluginAdmin;
5679 
5680  $lng->loadLanguageModule("assessment");
5681  $result = $ilDB->query("SELECT * FROM qpl_qst_type");
5682  $types = array();
5683  while ($row = $ilDB->fetchAssoc($result))
5684  {
5685  if ($row["plugin"] == 0)
5686  {
5687  $types[$row['type_tag']] = $lng->txt($row["type_tag"]);
5688  }
5689  else
5690  {
5691  $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
5692  foreach ($pl_names as $pl_name)
5693  {
5694  $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
5695  if (strcmp($pl->getQuestionType(), $row["type_tag"]) == 0)
5696  {
5697  $types[$row['type_tag']] = $pl->getQuestionTypeTranslation();
5698  }
5699  }
5700  }
5701  }
5702  ksort($types);
5703  return $types;
5704  }
5705 
5712  function fromXML(&$assessment)
5713  {
5714  unset($_SESSION["import_mob_xhtml"]);
5715 
5716  $this->setDescription($assessment->getComment());
5717  $this->setTitle($assessment->getTitle());
5718 
5719  foreach ($assessment->objectives as $objectives)
5720  {
5721  foreach ($objectives->materials as $material)
5722  {
5723  $this->setIntroduction($this->QTIMaterialToString($material));
5724  }
5725  }
5726  if ($assessment->getPresentationMaterial())
5727  {
5728  $this->setFinalStatement($this->QTIMaterialToString($assessment->getPresentationMaterial()->getMaterial(0)));
5729  }
5730 
5731  foreach ($assessment->assessmentcontrol as $assessmentcontrol)
5732  {
5733  switch ($assessmentcontrol->getSolutionswitch())
5734  {
5735  case "Yes":
5736  $this->setInstantFeedbackSolution(1);
5737  break;
5738  default:
5739  $this->setInstantFeedbackSolution(0);
5740  break;
5741  }
5742  }
5743 
5744  foreach ($assessment->qtimetadata as $metadata)
5745  {
5746  switch ($metadata["label"])
5747  {
5748  case "test_type":
5749  // for old tests with a test type
5750  $type = $metadata["entry"];
5751  switch ($type)
5752  {
5753  case 1:
5754  // assessment
5755  $this->setAnonymity(1);
5756  break;
5757  case 2:
5758  // self assessment
5759  break;
5760  case 4:
5761  // online exam
5762  $this->setFixedParticipants(1);
5763  $this->setListOfQuestionsSettings(7);
5764  $this->setShowSolutionPrintview(1);
5765  break;
5766  case 5:
5767  // varying random test
5768  break;
5769  }
5770  break;
5771  case "sequence_settings":
5772  $this->setSequenceSettings($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 "kiosk":
5781  $this->setKiosk($metadata["entry"]);
5782  break;
5783  case "showfinalstatement":
5784  $this->setShowFinalStatement($metadata["entry"]);
5785  break;
5786  case "showinfo":
5787  $this->setShowInfo($metadata["entry"]);
5788  break;
5789  case "forcejs":
5790  $this->setForceJS($metadata["entry"]);
5791  break;
5792  case "customstyle":
5793  $this->setCustomStyle($metadata["entry"]);
5794  break;
5795  case "hide_previous_results":
5796  if ($metadata["entry"] == 0)
5797  {
5798  $this->setUsePreviousAnswers(1);
5799  }
5800  else
5801  {
5802  $this->setUsePreviousAnswers(0);
5803  }
5804  break;
5805  case "use_previous_answers":
5806  $this->setUsePreviousAnswers($metadata["entry"]);
5807  break;
5808  case "answer_feedback":
5809  $this->setAnswerFeedback($metadata["entry"]);
5810  break;
5811  case "hide_title_points":
5812  $this->setTitleOutput($metadata["entry"]);
5813  break;
5814  case "title_output":
5815  $this->setTitleOutput($metadata["entry"]);
5816  break;
5817  case "random_test":
5818  $this->setRandomTest($metadata["entry"]);
5819  break;
5820  case "random_question_count":
5821  $this->setRandomQuestionCount($metadata["entry"]);
5822  break;
5823  case "results_presentation":
5824  $this->setResultsPresentation($metadata["entry"]);
5825  break;
5826  case "reset_processing_time":
5827  $this->setResetProcessingTime($metadata["entry"]);
5828  break;
5829  case "instant_verification":
5830  $this->setInstantFeedbackSolution($metadata["entry"]);
5831  break;
5832  case "answer_feedback_points":
5833  $this->setAnswerFeedbackPoints($metadata["entry"]);
5834  break;
5835  case "anonymity":
5836  $this->setAnonymity($metadata["entry"]);
5837  break;
5838  case "show_cancel":
5839  $this->setShowCancel($metadata["entry"]);
5840  break;
5841  case "show_marker":
5842  $this->setShowMarker($metadata["entry"]);
5843  break;
5844  case "fixed_participants":
5845  $this->setFixedParticipants($metadata["entry"]);
5846  break;
5847  case "score_reporting":
5848  $this->setScoreReporting($metadata["entry"]);
5849  break;
5850  case "shuffle_questions":
5851  $this->setShuffleQuestions($metadata["entry"]);
5852  break;
5853  case "count_system":
5854  $this->setCountSystem($metadata["entry"]);
5855  break;
5856  case "mc_scoring":
5857  $this->setMCScoring($metadata["entry"]);
5858  break;
5859  case "mailnotification":
5860  $this->setMailNotification($metadata["entry"]);
5861  break;
5862  case "mailnottype":
5863  $this->setMailNotificationType($metadata["entry"]);
5864  break;
5865  case "exportsettings":
5866  $this->setExportSettings($metadata['exportsettings']);
5867  break;
5868  case "score_cutting":
5869  $this->setScoreCutting($metadata["entry"]);
5870  break;
5871  case "password":
5872  $this->setPassword($metadata["entry"]);
5873  break;
5874  case "allowedUsers":
5875  $this->setAllowedUsers($metadata["entry"]);
5876  break;
5877  case "allowedUsersTimeGap":
5878  $this->setAllowedUsersTimeGap($metadata["entry"]);
5879  break;
5880  case "pass_scoring":
5881  $this->setPassScoring($metadata["entry"]);
5882  break;
5883  case "show_summary":
5884  $this->setListOfQuestionsSettings($metadata["entry"]);
5885  break;
5886  case "reporting_date":
5887  $iso8601period = $metadata["entry"];
5888  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
5889  {
5890  $this->setReportingDate(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
5891  }
5892  break;
5893  case "processing_time":
5894  $this->setProcessingTime($metadata['entry']);
5895  break;
5896  case "starting_time":
5897  $iso8601period = $metadata["entry"];
5898  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
5899  {
5900  $this->setStartingTime(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
5901  }
5902  break;
5903  case "ending_time":
5904  $iso8601period = $metadata["entry"];
5905  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
5906  {
5907  $this->setEndingTime(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
5908  }
5909  break;
5910  }
5911  if (preg_match("/mark_step_\d+/", $metadata["label"]))
5912  {
5913  $xmlmark = $metadata["entry"];
5914  preg_match("/<short>(.*?)<\/short>/", $xmlmark, $matches);
5915  $mark_short = $matches[1];
5916  preg_match("/<official>(.*?)<\/official>/", $xmlmark, $matches);
5917  $mark_official = $matches[1];
5918  preg_match("/<percentage>(.*?)<\/percentage>/", $xmlmark, $matches);
5919  $mark_percentage = $matches[1];
5920  preg_match("/<passed>(.*?)<\/passed>/", $xmlmark, $matches);
5921  $mark_passed = $matches[1];
5922  $this->mark_schema->addMarkStep($mark_short, $mark_official, $mark_percentage, $mark_passed);
5923  }
5924  }
5925  // handle the import of media objects in XHTML code
5926  if (is_array($_SESSION["import_mob_xhtml"]))
5927  {
5928  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
5929  include_once "./Services/RTE/classes/class.ilRTE.php";
5930  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5931  foreach ($_SESSION["import_mob_xhtml"] as $mob)
5932  {
5933  $importfile = $this->getImportDirectory() . "/" . $_SESSION["tst_import_subdir"] . "/" . $mob["uri"];
5934  if (file_exists($importfile))
5935  {
5936  $media_object =& ilObjMediaObject::_saveTempFileAsMediaObject(basename($importfile), $importfile, FALSE);
5937  ilObjMediaObject::_saveUsage($media_object->getId(), "tst:html", $this->getId());
5938  $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getIntroduction()), 1));
5939  $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getFinalStatement()), 1));
5940  }
5941  else
5942  {
5943  global $ilLog;
5944  $ilLog->write("Error: Could not open XHTML mob file for test introduction during test import. File $importfile does not exist!");
5945  }
5946  }
5947  $this->saveToDb();
5948  }
5949  }
5950 
5957  function toXML()
5958  {
5959  include_once("./Services/Xml/classes/class.ilXmlWriter.php");
5960  $a_xml_writer = new ilXmlWriter;
5961  // set xml header
5962  $a_xml_writer->xmlHeader();
5963  $a_xml_writer->xmlSetDtdDef("<!DOCTYPE questestinterop SYSTEM \"ims_qtiasiv1p2p1.dtd\">");
5964  $a_xml_writer->xmlStartTag("questestinterop");
5965 
5966  $attrs = array(
5967  "ident" => "il_".IL_INST_ID."_tst_".$this->getTestId(),
5968  "title" => $this->getTitle()
5969  );
5970  $a_xml_writer->xmlStartTag("assessment", $attrs);
5971  // add qti comment
5972  $a_xml_writer->xmlElement("qticomment", NULL, $this->getDescription());
5973 
5974  // add qti duration
5975  if ($this->enable_processing_time)
5976  {
5977  preg_match("/(\d+):(\d+):(\d+)/", $this->processing_time, $matches);
5978  $a_xml_writer->xmlElement("duration", NULL, sprintf("P0Y0M0DT%dH%dM%dS", $matches[1], $matches[2], $matches[3]));
5979  }
5980 
5981  // add the rest of the preferences in qtimetadata tags, because there is no correspondent definition in QTI
5982  $a_xml_writer->xmlStartTag("qtimetadata");
5983  $a_xml_writer->xmlStartTag("qtimetadatafield");
5984  $a_xml_writer->xmlElement("fieldlabel", NULL, "ILIAS_VERSION");
5985  $a_xml_writer->xmlElement("fieldentry", NULL, $this->ilias->getSetting("ilias_version"));
5986  $a_xml_writer->xmlEndTag("qtimetadatafield");
5987 
5988  // anonymity
5989  $a_xml_writer->xmlStartTag("qtimetadatafield");
5990  $a_xml_writer->xmlElement("fieldlabel", NULL, "anonymity");
5991  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnonymity()));
5992  $a_xml_writer->xmlEndTag("qtimetadatafield");
5993 
5994  // random test
5995  $a_xml_writer->xmlStartTag("qtimetadatafield");
5996  $a_xml_writer->xmlElement("fieldlabel", NULL, "random_test");
5997  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->isRandomTest()));
5998  $a_xml_writer->xmlEndTag("qtimetadatafield");
5999 
6000  // sequence settings
6001  $a_xml_writer->xmlStartTag("qtimetadatafield");
6002  $a_xml_writer->xmlElement("fieldlabel", NULL, "sequence_settings");
6003  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getSequenceSettings());
6004  $a_xml_writer->xmlEndTag("qtimetadatafield");
6005 
6006  // author
6007  $a_xml_writer->xmlStartTag("qtimetadatafield");
6008  $a_xml_writer->xmlElement("fieldlabel", NULL, "author");
6009  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAuthor());
6010  $a_xml_writer->xmlEndTag("qtimetadatafield");
6011 
6012  // reset processing time
6013  $a_xml_writer->xmlStartTag("qtimetadatafield");
6014  $a_xml_writer->xmlElement("fieldlabel", NULL, "reset_processing_time");
6015  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getResetProcessingTime());
6016  $a_xml_writer->xmlEndTag("qtimetadatafield");
6017 
6018  // count system
6019  $a_xml_writer->xmlStartTag("qtimetadatafield");
6020  $a_xml_writer->xmlElement("fieldlabel", NULL, "count_system");
6021  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getCountSystem());
6022  $a_xml_writer->xmlEndTag("qtimetadatafield");
6023 
6024  // multiple choice scoring
6025  $a_xml_writer->xmlStartTag("qtimetadatafield");
6026  $a_xml_writer->xmlElement("fieldlabel", NULL, "mc_scoring");
6027  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMCScoring());
6028  $a_xml_writer->xmlEndTag("qtimetadatafield");
6029 
6030  // multiple choice scoring
6031  $a_xml_writer->xmlStartTag("qtimetadatafield");
6032  $a_xml_writer->xmlElement("fieldlabel", NULL, "score_cutting");
6033  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getScoreCutting());
6034  $a_xml_writer->xmlEndTag("qtimetadatafield");
6035 
6036  // multiple choice scoring
6037  $a_xml_writer->xmlStartTag("qtimetadatafield");
6038  $a_xml_writer->xmlElement("fieldlabel", NULL, "password");
6039  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getPassword());
6040  $a_xml_writer->xmlEndTag("qtimetadatafield");
6041 
6042  // allowed users
6043  $a_xml_writer->xmlStartTag("qtimetadatafield");
6044  $a_xml_writer->xmlElement("fieldlabel", NULL, "allowedUsers");
6045  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAllowedUsers());
6046  $a_xml_writer->xmlEndTag("qtimetadatafield");
6047 
6048  // allowed users time gap
6049  $a_xml_writer->xmlStartTag("qtimetadatafield");
6050  $a_xml_writer->xmlElement("fieldlabel", NULL, "allowedUsersTimeGap");
6051  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAllowedUsersTimeGap());
6052  $a_xml_writer->xmlEndTag("qtimetadatafield");
6053 
6054  // pass scoring
6055  $a_xml_writer->xmlStartTag("qtimetadatafield");
6056  $a_xml_writer->xmlElement("fieldlabel", NULL, "pass_scoring");
6057  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getPassScoring());
6058  $a_xml_writer->xmlEndTag("qtimetadatafield");
6059 
6060  // score reporting date
6061  if ($this->getReportingDate())
6062  {
6063  $a_xml_writer->xmlStartTag("qtimetadatafield");
6064  $a_xml_writer->xmlElement("fieldlabel", NULL, "reporting_date");
6065  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->reporting_date, $matches);
6066  $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]));
6067  $a_xml_writer->xmlEndTag("qtimetadatafield");
6068  }
6069  // number of tries
6070  $a_xml_writer->xmlStartTag("qtimetadatafield");
6071  $a_xml_writer->xmlElement("fieldlabel", NULL, "nr_of_tries");
6072  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getNrOfTries()));
6073  $a_xml_writer->xmlEndTag("qtimetadatafield");
6074 
6075  // kiosk
6076  $a_xml_writer->xmlStartTag("qtimetadatafield");
6077  $a_xml_writer->xmlElement("fieldlabel", NULL, "kiosk");
6078  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getKiosk()));
6079  $a_xml_writer->xmlEndTag("qtimetadatafield");
6080 
6081  // use previous answers
6082  $a_xml_writer->xmlStartTag("qtimetadatafield");
6083  $a_xml_writer->xmlElement("fieldlabel", NULL, "use_previous_answers");
6084  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getUsePreviousAnswers());
6085  $a_xml_writer->xmlEndTag("qtimetadatafield");
6086 
6087  // hide title points
6088  $a_xml_writer->xmlStartTag("qtimetadatafield");
6089  $a_xml_writer->xmlElement("fieldlabel", NULL, "title_output");
6090  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getTitleOutput()));
6091  $a_xml_writer->xmlEndTag("qtimetadatafield");
6092 
6093  // random question count
6094  $a_xml_writer->xmlStartTag("qtimetadatafield");
6095  $a_xml_writer->xmlElement("fieldlabel", NULL, "random_question_count");
6096  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getRandomQuestionCount()));
6097  $a_xml_writer->xmlEndTag("qtimetadatafield");
6098 
6099  // results presentation
6100  $a_xml_writer->xmlStartTag("qtimetadatafield");
6101  $a_xml_writer->xmlElement("fieldlabel", NULL, "results_presentation");
6102  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getResultsPresentation()));
6103  $a_xml_writer->xmlEndTag("qtimetadatafield");
6104 
6105  // solution details
6106  $a_xml_writer->xmlStartTag("qtimetadatafield");
6107  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_summary");
6108  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getListOfQuestionsSettings()));
6109  $a_xml_writer->xmlEndTag("qtimetadatafield");
6110 
6111  // solution details
6112  $a_xml_writer->xmlStartTag("qtimetadatafield");
6113  $a_xml_writer->xmlElement("fieldlabel", NULL, "score_reporting");
6114  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getScoreReporting()));
6115  $a_xml_writer->xmlEndTag("qtimetadatafield");
6116 
6117  // solution details
6118  $a_xml_writer->xmlStartTag("qtimetadatafield");
6119  $a_xml_writer->xmlElement("fieldlabel", NULL, "instant_verification");
6120  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getInstantFeedbackSolution()));
6121  $a_xml_writer->xmlEndTag("qtimetadatafield");
6122 
6123  // answer specific feedback
6124  $a_xml_writer->xmlStartTag("qtimetadatafield");
6125  $a_xml_writer->xmlElement("fieldlabel", NULL, "answer_feedback");
6126  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnswerFeedback()));
6127  $a_xml_writer->xmlEndTag("qtimetadatafield");
6128 
6129  // answer specific feedback of reached points
6130  $a_xml_writer->xmlStartTag("qtimetadatafield");
6131  $a_xml_writer->xmlElement("fieldlabel", NULL, "answer_feedback_points");
6132  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnswerFeedbackPoints()));
6133  $a_xml_writer->xmlEndTag("qtimetadatafield");
6134 
6135  // show cancel
6136  $a_xml_writer->xmlStartTag("qtimetadatafield");
6137  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_cancel");
6138  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShowCancel()));
6139  $a_xml_writer->xmlEndTag("qtimetadatafield");
6140 
6141  // show marker
6142  $a_xml_writer->xmlStartTag("qtimetadatafield");
6143  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_marker");
6144  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShowMarker()));
6145  $a_xml_writer->xmlEndTag("qtimetadatafield");
6146 
6147  // fixed participants
6148  $a_xml_writer->xmlStartTag("qtimetadatafield");
6149  $a_xml_writer->xmlElement("fieldlabel", NULL, "fixed_participants");
6150  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getFixedParticipants()));
6151  $a_xml_writer->xmlEndTag("qtimetadatafield");
6152 
6153  // show final statement
6154  $a_xml_writer->xmlStartTag("qtimetadatafield");
6155  $a_xml_writer->xmlElement("fieldlabel", NULL, "showfinalstatement");
6156  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getShowFinalStatement()) ? "1" : "0")));
6157  $a_xml_writer->xmlEndTag("qtimetadatafield");
6158 
6159  // show introduction only
6160  $a_xml_writer->xmlStartTag("qtimetadatafield");
6161  $a_xml_writer->xmlElement("fieldlabel", NULL, "showinfo");
6162  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getShowInfo()) ? "1" : "0")));
6163  $a_xml_writer->xmlEndTag("qtimetadatafield");
6164 
6165  // mail notification
6166  $a_xml_writer->xmlStartTag("qtimetadatafield");
6167  $a_xml_writer->xmlElement("fieldlabel", NULL, "mailnotification");
6168  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMailNotification());
6169  $a_xml_writer->xmlEndTag("qtimetadatafield");
6170 
6171  // mail notification type
6172  $a_xml_writer->xmlStartTag("qtimetadatafield");
6173  $a_xml_writer->xmlElement("fieldlabel", NULL, "mailnottype");
6174  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMailNotificationType());
6175  $a_xml_writer->xmlEndTag("qtimetadatafield");
6176 
6177  // export settings
6178  $a_xml_writer->xmlStartTag("qtimetadatafield");
6179  $a_xml_writer->xmlElement("fieldlabel", NULL, "exportsettings");
6180  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getExportSettings());
6181  $a_xml_writer->xmlEndTag("qtimetadatafield");
6182 
6183  // force JavaScript
6184  $a_xml_writer->xmlStartTag("qtimetadatafield");
6185  $a_xml_writer->xmlElement("fieldlabel", NULL, "forcejs");
6186  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getForceJS()) ? "1" : "0")));
6187  $a_xml_writer->xmlEndTag("qtimetadatafield");
6188 
6189  // custom style
6190  $a_xml_writer->xmlStartTag("qtimetadatafield");
6191  $a_xml_writer->xmlElement("fieldlabel", NULL, "customstyle");
6192  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getCustomStyle());
6193  $a_xml_writer->xmlEndTag("qtimetadatafield");
6194 
6195  // shuffle questions
6196  $a_xml_writer->xmlStartTag("qtimetadatafield");
6197  $a_xml_writer->xmlElement("fieldlabel", NULL, "shuffle_questions");
6198  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShuffleQuestions()));
6199  $a_xml_writer->xmlEndTag("qtimetadatafield");
6200 
6201  // processing time
6202  $a_xml_writer->xmlStartTag("qtimetadatafield");
6203  $a_xml_writer->xmlElement("fieldlabel", NULL, "processing_time");
6204  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getProcessingTime());
6205  $a_xml_writer->xmlEndTag("qtimetadatafield");
6206 
6207  // starting time
6208  if ($this->getStartingTime())
6209  {
6210  $a_xml_writer->xmlStartTag("qtimetadatafield");
6211  $a_xml_writer->xmlElement("fieldlabel", NULL, "starting_time");
6212  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->starting_time, $matches);
6213  $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]));
6214  $a_xml_writer->xmlEndTag("qtimetadatafield");
6215  }
6216  // ending time
6217  if ($this->getEndingTime())
6218  {
6219  $a_xml_writer->xmlStartTag("qtimetadatafield");
6220  $a_xml_writer->xmlElement("fieldlabel", NULL, "ending_time");
6221  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->ending_time, $matches);
6222  $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]));
6223  $a_xml_writer->xmlEndTag("qtimetadatafield");
6224  }
6225  foreach ($this->mark_schema->mark_steps as $index => $mark)
6226  {
6227  // mark steps
6228  $a_xml_writer->xmlStartTag("qtimetadatafield");
6229  $a_xml_writer->xmlElement("fieldlabel", NULL, "mark_step_$index");
6230  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("<short>%s</short><official>%s</official><percentage>%.2f</percentage><passed>%d</passed>", $mark->getShortName(), $mark->getOfficialName(), $mark->getMinimumLevel(), $mark->getPassed()));
6231  $a_xml_writer->xmlEndTag("qtimetadatafield");
6232  }
6233  $a_xml_writer->xmlEndTag("qtimetadata");
6234 
6235  // add qti objectives
6236  $a_xml_writer->xmlStartTag("objectives");
6237  $this->addQTIMaterial($a_xml_writer, $this->getIntroduction());
6238  $a_xml_writer->xmlEndTag("objectives");
6239 
6240  // add qti assessmentcontrol
6241  if ($this->getInstantFeedbackSolution() == 1)
6242  {
6243  $attrs = array(
6244  "solutionswitch" => "Yes"
6245  );
6246  }
6247  else
6248  {
6249  $attrs = NULL;
6250  }
6251  $a_xml_writer->xmlElement("assessmentcontrol", $attrs, NULL);
6252 
6253  if (strlen($this->getFinalStatement()))
6254  {
6255  // add qti presentation_material
6256  $a_xml_writer->xmlStartTag("presentation_material");
6257  $a_xml_writer->xmlStartTag("flow_mat");
6258  $this->addQTIMaterial($a_xml_writer, $this->getFinalStatement());
6259  $a_xml_writer->xmlEndTag("flow_mat");
6260  $a_xml_writer->xmlEndTag("presentation_material");
6261  }
6262 
6263  $attrs = array(
6264  "ident" => "1"
6265  );
6266  $a_xml_writer->xmlElement("section", $attrs, NULL);
6267  $a_xml_writer->xmlEndTag("assessment");
6268  $a_xml_writer->xmlEndTag("questestinterop");
6269 
6270  $xml = $a_xml_writer->xmlDumpMem(FALSE);
6271 
6272  foreach ($this->questions as $question_id)
6273  {
6274  $question =& ilObjTest::_instanciateQuestion($question_id);
6275  $qti_question = $question->toXML(false);
6276  $qti_question = preg_replace("/<questestinterop>/", "", $qti_question);
6277  $qti_question = preg_replace("/<\/questestinterop>/", "", $qti_question);
6278  if (strpos($xml, "</section>") !== false)
6279  {
6280  $xml = str_replace("</section>", "$qti_question</section>", $xml);
6281  }
6282  else
6283  {
6284  $xml = str_replace("<section ident=\"1\"/>", "<section ident=\"1\">\n$qti_question</section>", $xml);
6285  }
6286  }
6287  return $xml;
6288  }
6289 
6296  function exportPagesXML(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
6297  {
6298  global $ilBench;
6299 
6300  $this->mob_ids = array();
6301  $this->file_ids = array();
6302 
6303  $attrs = array();
6304  $attrs["Type"] = "Test";
6305  $a_xml_writer->xmlStartTag("ContentObject", $attrs);
6306 
6307  // MetaData
6308  $this->exportXMLMetaData($a_xml_writer);
6309 
6310  // PageObjects
6311  $expLog->write(date("[y-m-d H:i:s] ")."Start Export Page Objects");
6312  $ilBench->start("ContentObjectExport", "exportPageObjects");
6313  $this->exportXMLPageObjects($a_xml_writer, $a_inst, $expLog);
6314  $ilBench->stop("ContentObjectExport", "exportPageObjects");
6315  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export Page Objects");
6316 
6317  // MediaObjects
6318  $expLog->write(date("[y-m-d H:i:s] ")."Start Export Media Objects");
6319  $ilBench->start("ContentObjectExport", "exportMediaObjects");
6320  $this->exportXMLMediaObjects($a_xml_writer, $a_inst, $a_target_dir, $expLog);
6321  $ilBench->stop("ContentObjectExport", "exportMediaObjects");
6322  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export Media Objects");
6323 
6324  // FileItems
6325  $expLog->write(date("[y-m-d H:i:s] ")."Start Export File Items");
6326  $ilBench->start("ContentObjectExport", "exportFileItems");
6327  $this->exportFileItems($a_target_dir, $expLog);
6328  $ilBench->stop("ContentObjectExport", "exportFileItems");
6329  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export File Items");
6330 
6331  $a_xml_writer->xmlEndTag("ContentObject");
6332  }
6333 
6340  function exportXMLMetaData(&$a_xml_writer)
6341  {
6342  include_once "./Services/MetaData/classes/class.ilMD2XML.php";
6343  $md2xml = new ilMD2XML($this->getId(), 0, $this->getType());
6344  $md2xml->setExportMode(true);
6345  $md2xml->startExport();
6346  $a_xml_writer->appendXML($md2xml->getXML());
6347  }
6348 
6354  function modifyExportIdentifier($a_tag, $a_param, $a_value)
6355  {
6356  if ($a_tag == "Identifier" && $a_param == "Entry")
6357  {
6358  include_once "./Services/Utilities/classes/class.ilUtil.php";
6359  $a_value = ilUtil::insertInstIntoID($a_value);
6360  }
6361 
6362  return $a_value;
6363  }
6364 
6365 
6372  function exportXMLPageObjects(&$a_xml_writer, $a_inst, &$expLog)
6373  {
6374  global $ilBench;
6375 
6376  include_once "./Modules/LearningModule/classes/class.ilLMPageObject.php";
6377 
6378  foreach ($this->questions as $question_id)
6379  {
6380  $ilBench->start("ContentObjectExport", "exportPageObject");
6381  $expLog->write(date("[y-m-d H:i:s] ")."Page Object ".$question_id);
6382 
6383  $attrs = array();
6384  $a_xml_writer->xmlStartTag("PageObject", $attrs);
6385 
6386 
6387  // export xml to writer object
6388  $ilBench->start("ContentObjectExport", "exportPageObject_XML");
6389  $page_object = new ilPageObject("qpl", $question_id);
6390  $page_object->buildDom();
6391  $page_object->insertInstIntoIDs($a_inst);
6392  $mob_ids = $page_object->collectMediaObjects(false);
6393  $file_ids = $page_object->collectFileItems();
6394  $xml = $page_object->getXMLFromDom(false, false, false, "", true);
6395  $xml = str_replace("&","&amp;", $xml);
6396  $a_xml_writer->appendXML($xml);
6397  $page_object->freeDom();
6398  unset ($page_object);
6399 
6400  $ilBench->stop("ContentObjectExport", "exportPageObject_XML");
6401 
6402  // collect media objects
6403  $ilBench->start("ContentObjectExport", "exportPageObject_CollectMedia");
6404  //$mob_ids = $page_obj->getMediaObjectIDs();
6405  foreach($mob_ids as $mob_id)
6406  {
6407  $this->mob_ids[$mob_id] = $mob_id;
6408  }
6409  $ilBench->stop("ContentObjectExport", "exportPageObject_CollectMedia");
6410 
6411  // collect all file items
6412  $ilBench->start("ContentObjectExport", "exportPageObject_CollectFileItems");
6413  //$file_ids = $page_obj->getFileItemIds();
6414  foreach($file_ids as $file_id)
6415  {
6416  $this->file_ids[$file_id] = $file_id;
6417  }
6418  $ilBench->stop("ContentObjectExport", "exportPageObject_CollectFileItems");
6419 
6420  $a_xml_writer->xmlEndTag("PageObject");
6421  //unset($page_obj);
6422 
6423  $ilBench->stop("ContentObjectExport", "exportPageObject");
6424 
6425 
6426  }
6427  }
6428 
6435  function exportXMLMediaObjects(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
6436  {
6437  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
6438 
6439  foreach ($this->mob_ids as $mob_id)
6440  {
6441  $expLog->write(date("[y-m-d H:i:s] ")."Media Object ".$mob_id);
6442  if (ilObjMediaObject::_exists($mob_id))
6443  {
6444  $media_obj = new ilObjMediaObject($mob_id);
6445  $media_obj->exportXML($a_xml_writer, $a_inst);
6446  $media_obj->exportFiles($a_target_dir);
6447  unset($media_obj);
6448  }
6449  }
6450  }
6451 
6456  function exportFileItems($a_target_dir, &$expLog)
6457  {
6458  include_once "./Modules/File/classes/class.ilObjFile.php";
6459 
6460  foreach ($this->file_ids as $file_id)
6461  {
6462  $expLog->write(date("[y-m-d H:i:s] ")."File Item ".$file_id);
6463  $file_obj = new ilObjFile($file_id, false);
6464  $file_obj->export($a_target_dir);
6465  unset($file_obj);
6466  }
6467  }
6468 
6473  function getImportMapping()
6474  {
6475  if (!is_array($this->import_mapping))
6476  {
6477  return array();
6478  }
6479  else
6480  {
6481  return $this->import_mapping;
6482  }
6483  }
6484 
6494  function getECTSGrade($passed_array, $reached_points, $max_points)
6495  {
6496  return ilObjTest::_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);
6497  }
6498 
6507  function _getECTSGrade($points_passed, $reached_points, $max_points, $a, $b, $c, $d, $e, $fx)
6508  {
6509  include_once "./classes/class.ilStatistics.php";
6510  // calculate the median
6511  $passed_statistics = new ilStatistics();
6512  $passed_statistics->setData($points_passed);
6513  $ects_percentiles = array
6514  (
6515  "A" => $passed_statistics->quantile($a),
6516  "B" => $passed_statistics->quantile($b),
6517  "C" => $passed_statistics->quantile($c),
6518  "D" => $passed_statistics->quantile($d),
6519  "E" => $passed_statistics->quantile($e)
6520  );
6521  if (count($points_passed) && ($reached_points >= $ects_percentiles["A"]))
6522  {
6523  return "A";
6524  }
6525  else if (count($points_passed) && ($reached_points >= $ects_percentiles["B"]))
6526  {
6527  return "B";
6528  }
6529  else if (count($points_passed) && ($reached_points >= $ects_percentiles["C"]))
6530  {
6531  return "C";
6532  }
6533  else if (count($points_passed) && ($reached_points >= $ects_percentiles["D"]))
6534  {
6535  return "D";
6536  }
6537  else if (count($points_passed) && ($reached_points >= $ects_percentiles["E"]))
6538  {
6539  return "E";
6540  }
6541  else if (strcmp($fx, "") != 0)
6542  {
6543  if ($max_points > 0)
6544  {
6545  $percentage = ($reached_points / $max_points) * 100.0;
6546  if ($percentage < 0) $percentage = 0.0;
6547  }
6548  else
6549  {
6550  $percentage = 0.0;
6551  }
6552  if ($percentage >= $fx)
6553  {
6554  return "FX";
6555  }
6556  else
6557  {
6558  return "F";
6559  }
6560  }
6561  else
6562  {
6563  return "F";
6564  }
6565  }
6566 
6567  function checkMarks()
6568  {
6569  return $this->mark_schema->checkMarks();
6570  }
6571 
6572  function getMarkSchema()
6573  {
6574  return $this->mark_schema;
6575  }
6576 
6584  function setAuthor($author = "")
6585  {
6586  $this->author = $author;
6587  }
6588 
6598  function saveAuthorToMetadata($a_author = "")
6599  {
6600  $md =& new ilMD($this->getId(), 0, $this->getType());
6601  $md_life =& $md->getLifecycle();
6602  if (!$md_life)
6603  {
6604  if (strlen($a_author) == 0)
6605  {
6606  global $ilUser;
6607  $a_author = $ilUser->getFullname();
6608  }
6609 
6610  $md_life =& $md->addLifecycle();
6611  $md_life->save();
6612  $con =& $md_life->addContribute();
6613  $con->setRole("Author");
6614  $con->save();
6615  $ent =& $con->addEntity();
6616  $ent->setEntity($a_author);
6617  $ent->save();
6618  }
6619  }
6620 
6626  function createMetaData()
6627  {
6629  $this->saveAuthorToMetadata();
6630  }
6631 
6639  function getAuthor()
6640  {
6641  $author = array();
6642  include_once "./Services/MetaData/classes/class.ilMD.php";
6643  $md =& new ilMD($this->getId(), 0, $this->getType());
6644  $md_life =& $md->getLifecycle();
6645  if ($md_life)
6646  {
6647  $ids =& $md_life->getContributeIds();
6648  foreach ($ids as $id)
6649  {
6650  $md_cont =& $md_life->getContribute($id);
6651  if (strcmp($md_cont->getRole(), "Author") == 0)
6652  {
6653  $entids =& $md_cont->getEntityIds();
6654  foreach ($entids as $entid)
6655  {
6656  $md_ent =& $md_cont->getEntity($entid);
6657  array_push($author, $md_ent->getEntity());
6658  }
6659  }
6660  }
6661  }
6662  return join($author, ",");
6663  }
6664 
6672  function _lookupAuthor($obj_id)
6673  {
6674  $author = array();
6675  include_once "./Services/MetaData/classes/class.ilMD.php";
6676  $md =& new ilMD($obj_id, 0, "tst");
6677  $md_life =& $md->getLifecycle();
6678  if ($md_life)
6679  {
6680  $ids =& $md_life->getContributeIds();
6681  foreach ($ids as $id)
6682  {
6683  $md_cont =& $md_life->getContribute($id);
6684  if (strcmp($md_cont->getRole(), "Author") == 0)
6685  {
6686  $entids =& $md_cont->getEntityIds();
6687  foreach ($entids as $entid)
6688  {
6689  $md_ent =& $md_cont->getEntity($entid);
6690  array_push($author, $md_ent->getEntity());
6691  }
6692  }
6693  }
6694  }
6695  return join($author, ",");
6696  }
6697 
6704  function &_getAvailableTests($use_object_id = FALSE)
6705  {
6706  global $ilUser;
6707  global $ilDB;
6708 
6709  $result_array = array();
6710  $tests = ilUtil::_getObjectsByOperations("tst","write", $ilUser->getId(), -1);
6711  if (count($tests))
6712  {
6713  $titles = ilObject::_prepareCloneSelection($tests, "tst");
6714  foreach ($tests as $ref_id)
6715  {
6716  if ($use_object_id)
6717  {
6718  $obj_id = ilObject::_lookupObjId($ref_id);
6719  $result_array[$obj_id] = $titles[$ref_id];
6720  }
6721  else
6722  {
6723  $result_array[$ref_id] = $titles[$ref_id];
6724  }
6725  }
6726  }
6727  return $result_array;
6728  }
6729 
6736  function cloneRandomQuestions($new_id)
6737  {
6738  global $ilDB;
6739 
6740  if ($new_id > 0)
6741  {
6742  $result = $ilDB->queryF("SELECT * FROM tst_test_random WHERE test_fi = %s ORDER BY sequence, test_random_id",
6743  array('integer'),
6744  array($this->getTestId())
6745  );
6746  if ($result->numRows())
6747  {
6748  while ($row = $ilDB->fetchAssoc($result))
6749  {
6750  $next_id = $ilDB->nextId('tst_test_random');
6751  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_random (test_random_id, test_fi, questionpool_fi, num_of_q, tstamp, sequence) VALUES (%s, %s, %s, %s, %s, %s)",
6752  array('integer', 'integer', 'integer', 'integer', 'integer', 'integer'),
6753  array($next_id, $new_id, $row["questionpool_fi"], $row["num_of_q"], time(), $row['sequence'])
6754  );
6755  }
6756  }
6757  }
6758  }
6759 
6760 
6769  public function cloneObject($a_target_id,$a_copy_id = 0)
6770  {
6771  global $ilDB,$ilLog;
6772 
6773  $this->loadFromDb();
6774 
6775  // Copy settings
6776  $newObj = parent::cloneObject($a_target_id,$a_copy_id);
6777  $this->cloneMetaData($newObj);
6778  $newObj->setAnonymity($this->getAnonymity());
6779  $newObj->setAnswerFeedback($this->getAnswerFeedback());
6780  $newObj->setAnswerFeedbackPoints($this->getAnswerFeedbackPoints());
6781  $newObj->setAuthor($this->getAuthor());
6782  $newObj->setCountSystem($this->getCountSystem());
6783  $newObj->setECTSFX($this->getECTSFX());
6784  $newObj->setECTSGrades($this->getECTSGrades());
6785  $newObj->setECTSOutput($this->getECTSOutput());
6786  $newObj->setEnableProcessingTime($this->getEnableProcessingTime());
6787  $newObj->setEndingTime($this->getEndingTime());
6788  $newObj->setFixedParticipants($this->getFixedParticipants());
6789  $newObj->setInstantFeedbackSolution($this->getInstantFeedbackSolution());
6790  $newObj->setIntroduction($this->getIntroduction());
6791  $newObj->setFinalStatement($this->getFinalStatement());
6792  $newObj->setShowInfo($this->getShowInfo());
6793  $newObj->setForceJS($this->getForceJS());
6794  $newObj->setCustomStyle($this->getCustomStyle());
6795  $newObj->setShowFinalStatement($this->getShowFinalStatement());
6796  $newObj->setListOfQuestionsSettings($this->getListOfQuestionsSettings());
6797  $newObj->setKiosk($this->getKiosk());
6798  $newObj->setMCScoring($this->getMCScoring());
6799  $newObj->setMailNotification($this->getMailNotification());
6800  $newObj->setMailNotificationType($this->getMailNotificationType());
6801  $newObj->setNrOfTries($this->getNrOfTries());
6802  $newObj->setPassScoring($this->getPassScoring());
6803  $newObj->setPassword($this->getPassword());
6804  $newObj->setProcessingTime($this->getProcessingTime());
6805  $newObj->setRandomQuestionCount($this->getRandomQuestionCount());
6806  $newObj->setRandomTest($this->isRandomTest());
6807  $newObj->setReportingDate($this->getReportingDate());
6808  $newObj->setResetProcessingTime($this->getResetProcessingTime());
6809  $newObj->setResultsPresentation($this->getResultsPresentation());
6810  $newObj->setScoreCutting($this->getScoreCutting());
6811  $newObj->setScoreReporting($this->getScoreReporting());
6812  $newObj->setSequenceSettings($this->getSequenceSettings());
6813  $newObj->setShowCancel($this->getShowCancel());
6814  $newObj->setShowMarker($this->getShowMarker());
6815  $newObj->setShuffleQuestions($this->getShuffleQuestions());
6816  $newObj->setStartingTime($this->getStartingTime());
6817  $newObj->setTitleOutput($this->getTitleOutput());
6818  $newObj->setUsePreviousAnswers($this->getUsePreviousAnswers());
6819  $newObj->setCertificateVisibility($this->getCertificateVisibility());
6820  $newObj->mark_schema = clone $this->mark_schema;
6821  $newObj->saveToDb();
6822 
6823  // clone certificate
6824  include_once "./Services/Certificate/classes/class.ilCertificate.php";
6825  include_once "./Modules/Test/classes/class.ilTestCertificateAdapter.php";
6826  $cert = new ilCertificate(new ilTestCertificateAdapter($this));
6827  $newcert = new ilCertificate(new ilTestCertificateAdapter($newObj));
6828  $cert->cloneCertificate($newcert);
6829 
6830  if ($this->isRandomTest())
6831  {
6832  $newObj->saveRandomQuestionCount($newObj->getRandomQuestionCount());
6833  $this->cloneRandomQuestions($newObj->getTestId());
6834  }
6835  else
6836  {
6837  include_once("./Services/CopyWizard/classes/class.ilCopyWizardOptions.php");
6838  $cwo = ilCopyWizardOptions::_getInstance($a_copy_id);
6839 
6840  // clone the questions
6841  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
6842  foreach ($this->questions as $key => $question_id)
6843  {
6844  $question = ilObjTest::_instanciateQuestion($question_id);
6845  $newObj->questions[$key] = $question->duplicate();
6846  $original_id = assQuestion::_getOriginalId($question_id);
6847  $question = ilObjTest::_instanciateQuestion($newObj->questions[$key]);
6848  $question->saveToDb($original_id);
6849 
6850  // Save the mapping of old question id <-> new question id
6851  // This will be used in class.ilObjCourse::cloneDependencies to copy learning objectives
6852  $cwo->appendMapping($this->getRefId().'_'.$question_id,$newObj->getRefId().'_'.$newObj->questions[$key]);
6853  $ilLog->write(__METHOD__.': Added mapping '.$this->getRefId().'_'.$question_id.' <-> ' .
6854  $newObj->getRefId().'_'.$newObj->questions[$key]);
6855  }
6856  }
6857  $newObj->saveToDb();
6858  return $newObj;
6859  }
6860 
6867  function getQuestionCount()
6868  {
6869  $num = 0;
6870 
6871  if ($this->isRandomTest())
6872  {
6873  if ($this->getRandomQuestionCount())
6874  {
6875  $num = $this->getRandomQuestionCount();
6876  $qpls =& $this->getRandomQuestionpools();
6877  $maxcount = 0;
6878  foreach ($qpls as $data)
6879  {
6880  $maxcount += $data["contains"];
6881  }
6882  if ($num > $maxcount) $num = $maxcount;
6883  }
6884  else
6885  {
6886  $qpls =& $this->getRandomQuestionpools();
6887  foreach ($qpls as $data)
6888  {
6889  $add = ($data["count"] <= $data["contains"]) ? $data["count"] : $data["contains"];
6890  $num += $add;
6891  }
6892  }
6893  }
6894  else
6895  {
6896  $num = count($this->questions);
6897  }
6898  return $num;
6899  }
6900 
6908  {
6909  global $ilDB;
6910 
6911  $num = 0;
6912 
6913  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
6914  array('integer'),
6915  array($test_id)
6916  );
6917  if (!$result->numRows())
6918  {
6919  return 0;
6920  }
6921  $test = $ilDB->fetchAssoc($result);
6922 
6923  if ($test["random_test"] == 1)
6924  {
6925  $qpls = array();
6926  $counter = 0;
6927  $result = $ilDB->queryF("SELECT * FROM tst_test_random WHERE test_fi = %s ORDER BY sequence, test_random_id",
6928  array('integer'),
6929  array($test_id)
6930  );
6931  if ($result->numRows())
6932  {
6933  while ($row = $ilDB->fetchAssoc($result))
6934  {
6935  $countresult = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE obj_fi = %s AND qpl_questions.tstamp > 0 AND original_id IS NULL",
6936  array('integer'),
6937  $row["questionpool_fi"]
6938  );
6939  $contains = $countresult->numRows();
6940  $qpls[$counter] = array(
6941  "index" => $counter,
6942  "count" => $row["num_of_q"],
6943  "qpl" => $row["questionpool_fi"],
6944  "contains" => $contains
6945  );
6946  $counter++;
6947  }
6948  }
6949  if ($test["random_question_count"] > 0)
6950  {
6951  $num = $test["random_question_count"];
6952  $maxcount = 0;
6953  foreach ($qpls as $data)
6954  {
6955  $maxcount += $data["contains"];
6956  }
6957  if ($num > $maxcount) $num = $maxcount;
6958  }
6959  else
6960  {
6961  $num = 0;
6962  foreach ($qpls as $data)
6963  {
6964  $add = ($data["count"] <= $data["contains"]) ? $data["count"] : $data["contains"];
6965  $num += $add;
6966  }
6967  }
6968  }
6969  else
6970  {
6971  $result = $ilDB->queryF("SELECT test_question_id FROM tst_test_question WHERE test_fi = %s",
6972  array('integer'),
6973  array($test_id)
6974  );
6975  $num = $result->numRows();
6976  }
6977  return $num;
6978  }
6979 
6986  {
6987  global $ilDB;
6988 
6989  // delete eventually set questions of a previous non-random test
6990  $this->removeAllTestEditings();
6991  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
6992  array('integer'),
6993  array($this->getTestId())
6994  );
6995  $this->questions = array();
6996  $this->saveCompleteStatus();
6997  }
6998 
7005  {
7006  global $ilDB;
7007  // delete eventually set random question pools of a previous random test
7008  $this->removeAllTestEditings();
7009  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
7010  array('integer'),
7011  array($this->getTestId())
7012  );
7013  $this->questions = array();
7014  $this->saveCompleteStatus();
7015  }
7016 
7024  function logAction($logtext = "", $question_id = "")
7025  {
7026  global $ilUser;
7027 
7028  $original_id = "";
7029  if (strcmp($question_id, "") != 0)
7030  {
7031  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7032  $original_id = assQuestion::_getOriginalId($question_id);
7033  }
7034  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
7035  ilObjAssessmentFolder::_addLog($ilUser->getId(), $this->getId(), $logtext, $question_id, $original_id, TRUE, $this->getRefId());
7036  }
7037 
7046  {
7047  global $ilDB;
7048  $object_id = FALSE;
7049  $result = $ilDB->queryF("SELECT obj_fi FROM tst_tests WHERE test_id = %s",
7050  array('integer'),
7051  array($test_id)
7052  );
7053  if ($result->numRows())
7054  {
7055  $row = $ilDB->fetchAssoc($result);
7056  $object_id = $row["obj_fi"];
7057  }
7058  return $object_id;
7059  }
7060 
7068  function _getObjectIDFromActiveID($active_id)
7069  {
7070  global $ilDB;
7071  $object_id = FALSE;
7072  $result = $ilDB->queryF("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",
7073  array('integer'),
7074  array($active_id)
7075  );
7076  if ($result->numRows())
7077  {
7078  $row = $ilDB->fetchAssoc($result);
7079  $object_id = $row["obj_fi"];
7080  }
7081  return $object_id;
7082  }
7083 
7091  function _getTestIDFromObjectID($object_id)
7092  {
7093  global $ilDB;
7094  $test_id = FALSE;
7095  $result = $ilDB->queryF("SELECT test_id FROM tst_tests WHERE obj_fi = %s",
7096  array('integer'),
7097  array($object_id)
7098  );
7099  if ($result->numRows())
7100  {
7101  $row = $ilDB->fetchAssoc($result);
7102  $test_id = $row["test_id"];
7103  }
7104  return $test_id;
7105  }
7106 
7115  function getTextAnswer($active_id, $question_id, $pass = NULL)
7116  {
7117  global $ilDB;
7118 
7119  $res = "";
7120  if (($active_id) && ($question_id))
7121  {
7122  if (is_null($pass))
7123  {
7124  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7125  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
7126  }
7127  $result = $ilDB->queryF("SELECT value1 FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
7128  array('integer', 'integer', 'integer'),
7129  array($active_id, $question_id, $pass)
7130  );
7131  if ($result->numRows() == 1)
7132  {
7133  $row = $ilDB->fetchAssoc($result);
7134  $res = $row["value1"];
7135  }
7136  }
7137  return $res;
7138  }
7139 
7147  function getQuestiontext($question_id)
7148  {
7149  global $ilDB;
7150 
7151  $res = "";
7152  if ($question_id)
7153  {
7154  $result = $ilDB->queryF("SELECT question_text FROM qpl_questions WHERE question_id = %s",
7155  array('integer'),
7156  array($question_id)
7157  );
7158  if ($result->numRows() == 1)
7159  {
7160  $row = $ilDB->fetchAssoc($result);
7161  $res = $row["question_text"];
7162  }
7163  }
7164  return $res;
7165  }
7166 
7173  function &getInvitedUsers($user_id="", $order="login, lastname, firstname")
7174  {
7175  global $ilDB;
7176 
7177  $result_array = array();
7178 
7179  if ($this->getAnonymity())
7180  {
7181  if (is_numeric($user_id))
7182  {
7183  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " .
7184  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7185  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7186  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
7187  "ORDER BY $order",
7188  array('text', 'text', 'text', 'integer', 'integer'),
7189  array("", $this->lng->txt("unknown"), "", $this->getTestId(), $user_id)
7190  );
7191  }
7192  else
7193  {
7194  $result = $ilDB->queryF("SELECT tst_active.active_id, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " .
7195  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7196  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7197  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
7198  "ORDER BY $order",
7199  array('text', 'text', 'text', 'integer'),
7200  array("", $this->lng->txt("unknown"), "", $this->getTestId())
7201  );
7202  }
7203  }
7204  else
7205  {
7206  if (is_numeric($user_id))
7207  {
7208  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " .
7209  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7210  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7211  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
7212  "ORDER BY $order",
7213  array('integer', 'integer'),
7214  array($this->getTestId(), $user_id)
7215  );
7216  }
7217  else
7218  {
7219  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " .
7220  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7221  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7222  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
7223  "ORDER BY $order",
7224  array('integer'),
7225  array($this->getTestId())
7226  );
7227  }
7228  }
7229  $result_array = array();
7230  while ($row = $ilDB->fetchAssoc($result))
7231  {
7232  $result_array[$row['usr_id']]= $row;
7233  }
7234  return $result_array;
7235  }
7236 
7244  {
7245  global $ilDB;
7246 
7247  if ($this->getAnonymity())
7248  {
7249  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, tst_active.user_fi usr_id, %s login, %s lastname, %s firstname, tst_active.submitted test_finished, usr_data.matriculation, usr_data.active ".
7250  "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),
7251  array('text', 'text', 'text', 'integer'),
7252  array("", $this->lng->txt("unknown"), "", $this->getTestId())
7253  );
7254  }
7255  else
7256  {
7257  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, tst_active.user_fi usr_id, usr_data.login, usr_data.lastname, usr_data.firstname, tst_active.submitted test_finished, usr_data.matriculation, usr_data.active ".
7258  "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),
7259  array('integer'),
7260  array($this->getTestId())
7261  );
7262  }
7263  $data = array();
7264  while ($row = $ilDB->fetchAssoc($result))
7265  {
7266  $data[$row['active_id']] = $row;
7267  }
7268  foreach ($data as $index => $participant)
7269  {
7270  if (strlen(trim($participant["firstname"].$participant["lastname"])) == 0)
7271  {
7272  $data[$index]["lastname"] = $this->lng->txt("deleted_user");
7273  }
7274  }
7275  return $data;
7276  }
7277 
7278  public function getTestParticipantsForManualScoring($filter = NULL)
7279  {
7280  global $ilDB;
7281 
7282  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
7284  if (count($scoring) == 0) return array();
7285 
7286  $participants =& $this->getTestParticipants();
7287  $filtered_participants = array();
7288  foreach ($participants as $active_id => $participant)
7289  {
7290  $result = $ilDB->queryF("SELECT tst_test_result.manual FROM tst_test_result,qpl_questions WHERE tst_test_result.question_fi = qpl_questions.question_id AND " . $ilDB->in('qpl_questions.question_type_fi', $scoring, false, 'integer') . " AND tst_test_result.active_fi = %s",
7291  array("integer"),
7292  array($active_id)
7293  );
7294  $count = $result->numRows();
7295  if ($count > 0)
7296  {
7297  switch ($filter)
7298  {
7299  case 1: // only active users
7300  if ($participant->active) $filtered_participants[$active_id] = $participant;
7301  break;
7302  case 2: // only inactive users
7303  if (!$participant->active) $filtered_participants[$active_id] = $participant;
7304  break;
7305  case 3: // all users
7306  $filtered_participants[$active_id] = $participant;
7307  break;
7308  case 4:
7309  // already scored participants
7310  //$found = 0;
7311  //while ($row = $ilDB->fetchAssoc($result))
7312  //{
7313  // if ($row["manual"]) $found++;
7314  //}
7315  //if ($found == $count)
7316  //{
7317  //$filtered_participants[$active_id] = $participant;
7318  //}
7319  //else
7320  //{
7321  $assessmentSetting = new ilSetting("assessment");
7322  $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id);
7323  if ($manscoring_done) $filtered_participants[$active_id] = $participant;
7324  //}
7325  break;
7326  case 5:
7327  // unscored participants
7328  //$found = 0;
7329  //while ($row = $ilDB->fetchAssoc($result))
7330  //{
7331  // if ($row["manual"]) $found++;
7332  //}
7333  //if ($found == 0)
7334  //{
7335  $assessmentSetting = new ilSetting("assessment");
7336  $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id);
7337  if (!$manscoring_done) $filtered_participants[$active_id] = $participant;
7338  //}
7339  break;
7340  case 6:
7341  // partially scored participants
7342  $found = 0;
7343  while ($row = $ilDB->fetchAssoc($result))
7344  {
7345  if ($row["manual"]) $found++;
7346  }
7347  if (($found > 0) && ($found < $count)) $filtered_participants[$active_id] = $participant;
7348  break;
7349  default:
7350  $filtered_participants[$active_id] = $participant;
7351  break;
7352  }
7353  }
7354  }
7355  return $filtered_participants;
7356  }
7357 
7365  function &getUserData($ids)
7366  {
7367  global $ilDB;
7368 
7369  if (!is_array($ids) || count($ids) ==0) return array();
7370 
7371  if ($this->getAnonymity())
7372  {
7373  $result = $ilDB->queryF("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",
7374  array('text', 'text', 'text'),
7375  array("", $this->lng->txt("unknown"), "")
7376  );
7377  }
7378  else
7379  {
7380  $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");
7381  }
7382 
7383  $result_array = array();
7384  while ($row = $ilDB->fetchAssoc($result))
7385  {
7386  $result_array[$row["usr_id"]]= $row;
7387  }
7388  return $result_array;
7389  }
7390 
7391  function &getGroupData($ids)
7392  {
7393  if (!is_array($ids) || count($ids) ==0) return array();
7394  $result = array();
7395  foreach ($ids as $ref_id)
7396  {
7397  $obj_id = ilObject::_lookupObjId($ref_id);
7398  $result[$ref_id] = array("ref_id" => $ref_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id));
7399  }
7400  return $result;
7401  }
7402 
7403  function &getRoleData($ids)
7404  {
7405  if (!is_array($ids) || count($ids) ==0) return array();
7406  $result = array();
7407  foreach ($ids as $obj_id)
7408  {
7409  $result[$obj_id] = array("obj_id" => $obj_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id));
7410  }
7411  return $result;
7412  }
7413 
7414 
7421  function inviteGroup($group_id)
7422  {
7423  include_once "./Modules/Group/classes/class.ilObjGroup.php";
7424  $group = new ilObjGroup($group_id);
7425  $members = $group->getGroupMemberIds();
7426  include_once './Services/User/classes/class.ilObjUser.php';
7427  foreach ($members as $user_id)
7428  {
7429  $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id));
7430  }
7431  }
7432 
7439  function inviteRole($role_id)
7440  {
7441  global $rbacreview;
7442  $members = $rbacreview->assignedUsers($role_id,"usr_id");
7443  include_once './Services/User/classes/class.ilObjUser.php';
7444  foreach ($members as $user_id)
7445  {
7446  $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id));
7447  }
7448  }
7449 
7450 
7451 
7458  function disinviteUser($user_id)
7459  {
7460  global $ilDB;
7461 
7462  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
7463  array('integer', 'integer'),
7464  array($this->getTestId(), $user_id)
7465  );
7466  }
7467 
7474  function inviteUser($user_id, $client_ip="")
7475  {
7476  global $ilDB;
7477 
7478  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
7479  array('integer', 'integer'),
7480  array($this->getTestId(), $user_id)
7481  );
7482  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_invited_user (test_fi, user_fi, clientip, tstamp) VALUES (%s, %s, %s, %s)",
7483  array('integer', 'integer', 'text', 'integer'),
7484  array($this->getTestId(), $user_id, (strlen($client_ip)) ? $client_ip : NULL, time())
7485  );
7486  }
7487 
7488 
7489  function setClientIP($user_id, $client_ip)
7490  {
7491  global $ilDB;
7492 
7493  $affectedRows = $ilDB->manipulateF("UPDATE tst_invited_user SET clientip = %s, tstamp = %s WHERE test_fi=%s and user_fi=%s",
7494  array('text', 'integer', 'integer', 'integer'),
7495  array((strlen($client_ip)) ? $client_ip : NULL, time(), $this->getTestId(), $user_id)
7496  );
7497  }
7498 
7504  function _getSolvedQuestions($active_id, $question_fi = null)
7505  {
7506  global $ilDB;
7507  if (is_numeric($question_fi))
7508  {
7509  $result = $ilDB->queryF("SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s AND question_fi=%s",
7510  array('integer', 'integer'),
7511  array($active_id, $question_fi)
7512  );
7513  }
7514  else
7515  {
7516  $result = $ilDB->queryF("SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s",
7517  array('integer'),
7518  array($active_id)
7519  );
7520  }
7521  $result_array = array();
7522  while ($row = $ilDB->fetchAssoc($result))
7523  {
7524  $result_array[$row["question_fi"]]= $row;
7525  }
7526  return $result_array;
7527  }
7528 
7529 
7533  function setQuestionSetSolved($value, $question_id, $user_id)
7534  {
7535  global $ilDB;
7536 
7537  $active_id = $this->getActiveIdOfUser($user_id);
7538  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s AND question_fi = %s",
7539  array('integer', 'integer'),
7540  array($active_id, $question_id)
7541  );
7542  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_qst_solved (solved, question_fi, active_fi) VALUES (%s, %s, %s)",
7543  array('integer', 'integer', 'integer'),
7544  array($value, $question_id, $active_id)
7545  );
7546  }
7547 
7548 
7552  function setActiveTestSubmitted($user_id)
7553  {
7554  global $ilDB, $ilLog;
7555 
7556  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s, tstamp = %s WHERE test_fi = %s AND user_fi = %s",
7557  array('integer', 'timestamp', 'integer', 'integer', 'integer'),
7558  array(1, date('Y-m-d H:i:s'), time(), $this->getTestId(), $user_id)
7559  );
7560  $this->testSession = NULL;
7561  }
7562 
7566  function isTestFinished($active_id)
7567  {
7568  global $ilDB;
7569 
7570  $result = $ilDB->queryF("SELECT submitted FROM tst_active WHERE active_id=%s AND submitted=%s",
7571  array('integer', 'integer'),
7572  array($active_id, 1)
7573  );
7574  return $result->numRows() == 1;
7575  }
7576 
7580  function isActiveTestSubmitted($user_id = null)
7581  {
7582  global $ilUser;
7583  global $ilDB;
7584 
7585  if (!is_numeric($user_id))
7586  $user_id = $ilUser->getId();
7587 
7588  $result = $ilDB->queryF("SELECT submitted FROM tst_active WHERE test_fi=%s AND user_fi=%s AND submitted=%s",
7589  array('integer', 'integer', 'integer'),
7590  array($this->getTestId(), $user_id, 1)
7591  );
7592  return $result->numRows() == 1;
7593  }
7594 
7599  {
7600  return $this->getNrOfTries() != 0;
7601  }
7602 
7603 
7608  function isNrOfTriesReached($tries)
7609  {
7610  return $tries >= (int) $this->getNrOfTries();
7611  }
7612 
7613 
7622  function getAllTestResults($participants, $prepareForCSV = true)
7623  {
7624  $results = array();
7625  $row = array(
7626  "user_id" => $this->lng->txt("user_id"),
7627  "matriculation" => $this->lng->txt("matriculation"),
7628  "lastname" => $this->lng->txt("lastname"),
7629  "firstname" => $this->lng->txt("firstname"),
7630  "login" =>$this->lng->txt("login"),
7631  "reached_points" => $this->lng->txt("tst_reached_points"),
7632  "max_points" => $this->lng->txt("tst_maximum_points"),
7633  "percent_value" => $this->lng->txt("tst_percent_solved"),
7634  "mark" => $this->lng->txt("tst_mark"),
7635  "ects" => $this->lng->txt("ects_grade")
7636  );
7637  $results[] = $row;
7638  if (count($participants))
7639  {
7640  if ($this->ects_output)
7641  {
7642  $passed_array =& $this->getTotalPointsPassedArray();
7643  }
7644  foreach ($participants as $active_id => $user_rec)
7645  {
7646  $row = array();
7647  $reached_points = 0;
7648  $max_points = 0;
7649  foreach ($this->questions as $value)
7650  {
7651  $question =& ilObjTest::_instanciateQuestion($value);
7652  if (is_object($question))
7653  {
7654  $max_points += $question->getMaximumPoints();
7655  $reached_points += $question->getReachedPoints($active_id);
7656  }
7657  }
7658  if ($max_points > 0)
7659  {
7660  $percentvalue = $reached_points / $max_points;
7661  if ($percentvalue < 0) $percentvalue = 0.0;
7662  }
7663  else
7664  {
7665  $percentvalue = 0;
7666  }
7667  $mark_obj = $this->mark_schema->getMatchingMark($percentvalue * 100);
7668  $passed = "";
7669  if ($mark_obj)
7670  {
7671  $mark = $mark_obj->getOfficialName();
7672  $ects_mark = $this->getECTSGrade($passed_array, $reached_points, $max_points);
7673  }
7674  if ($this->getAnonymity())
7675  {
7676  $user_rec['firstname'] = "";
7677  $user_rec['lastname'] = $this->lng->txt("unknown");
7678  }
7679  $row = array(
7680  "user_id"=>$user_rec['usr_id'],
7681  "matriculation" => $user_rec['matriculation'],
7682  "lastname" => $user_rec['lastname'],
7683  "firstname" => $user_rec['firstname'],
7684  "login"=>$user_rec['login'],
7685  "reached_points" => $reached_points,
7686  "max_points" => $max_points,
7687  "percent_value" => $percentvalue,
7688  "mark" => $mark,
7689  "ects" => $ects_mark
7690  );
7691  $results[] = $prepareForCSV ? $this->processCSVRow ($row, true) : $row;
7692  }
7693  }
7694  return $results;
7695  }
7696 
7707  function &processCSVRow($row, $quoteAll = FALSE, $separator = ";")
7708  {
7709  $resultarray = array();
7710  foreach ($row as $rowindex => $entry)
7711  {
7712  $surround = FALSE;
7713  if ($quoteAll)
7714  {
7715  $surround = TRUE;
7716  }
7717  if (strpos($entry, "\"") !== FALSE)
7718  {
7719  $entry = str_replace("\"", "\"\"", $entry);
7720  $surround = TRUE;
7721  }
7722  if (strpos($entry, $separator) !== FALSE)
7723  {
7724  $surround = TRUE;
7725  }
7726  // replace all CR LF with LF (for Excel for Windows compatibility
7727  $entry = str_replace(chr(13).chr(10), chr(10), $entry);
7728  if ($surround)
7729  {
7730  $resultarray[$rowindex] = utf8_decode("\"" . $entry . "\"");
7731  }
7732  else
7733  {
7734  $resultarray[$rowindex] = utf8_decode($entry);
7735  }
7736  }
7737  return $resultarray;
7738  }
7739 
7748  function _getPass($active_id)
7749  {
7750  global $ilDB;
7751  $result = $ilDB->queryF("SELECT tries FROM tst_active WHERE active_id = %s",
7752  array('integer'),
7753  array($active_id)
7754  );
7755  if ($result->numRows())
7756  {
7757  $row = $ilDB->fetchAssoc($result);
7758  return $row["tries"];
7759  }
7760  else
7761  {
7762  return 0;
7763  }
7764  }
7765 
7775  function _getMaxPass($active_id)
7776  {
7777  global $ilDB;
7778  $result = $ilDB->queryF("SELECT MAX(pass) maxpass FROM tst_test_result WHERE active_fi = %s",
7779  array('integer'),
7780  array($active_id)
7781  );
7782  if ($result->numRows())
7783  {
7784  $row = $ilDB->fetchAssoc($result);
7785  $max = $row["maxpass"];
7786  }
7787  else
7788  {
7789  $max = NULL;
7790  }
7791  return $max;
7792  }
7793 
7802  function _getBestPass($active_id)
7803  {
7804  global $ilDB;
7805 
7806  $result = $ilDB->queryF("SELECT * FROM tst_pass_result WHERE active_fi = %s",
7807  array('integer'),
7808  array($active_id)
7809  );
7810  if ($result->numRows())
7811  {
7812  $bestrow = null;
7813  $bestfactor = 0;
7814  while ($row = $ilDB->fetchAssoc($result))
7815  {
7816  $factor = $row["points"] / $row["maxpoints"];
7817 
7818  if($factor > $bestfactor)
7819  {
7820  $bestrow = $row;
7821  $bestfactor = $factor;
7822  }
7823  }
7824  if (is_array($bestrow))
7825  {
7826  return $bestrow["pass"];
7827  }
7828  else
7829  {
7830  return 0;
7831  }
7832  }
7833  else
7834  {
7835  return 0;
7836  }
7837  }
7838 
7847  function _getResultPass($active_id)
7848  {
7849  $counted_pass = NULL;
7850  if (ilObjTest::_getPassScoring($active_id) == SCORE_BEST_PASS)
7851  {
7852  $counted_pass = ilObjTest::_getBestPass($active_id);
7853  }
7854  else
7855  {
7856  $counted_pass = ilObjTest::_getMaxPass($active_id);
7857  }
7858  return $counted_pass;
7859  }
7860 
7870  function getAnsweredQuestionCount($active_id, $pass = NULL)
7871  {
7872  if ($this->isRandomTest())
7873  {
7874  $this->loadQuestions($active_id, $pass);
7875  }
7876  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7877  $workedthrough = 0;
7878  foreach ($this->questions as $value)
7879  {
7880  if (assQuestion::_isWorkedThrough($active_id, $value, $pass))
7881  {
7882  $workedthrough += 1;
7883  }
7884  }
7885  return $workedthrough;
7886  }
7887 
7897  function getPassFinishDate($active_id, $pass)
7898  {
7899  global $ilDB;
7900  if (is_null($pass)) $pass = 0;
7901  $result = $ilDB->queryF("SELECT tst_test_result.tstamp FROM tst_test_result WHERE active_fi = %s AND pass = %s ORDER BY tst_test_result.tstamp DESC",
7902  array('integer', 'integer'),
7903  array($active_id, $pass)
7904  );
7905  if ($result->numRows())
7906  {
7907  $row = $ilDB->fetchAssoc($result);
7908  return $row["tstamp"];
7909  }
7910  else
7911  {
7912  return 0;
7913  }
7914  }
7915 
7923  function isExecutable($user_id, $allowPassIncrease = FALSE)
7924  {
7925  $result = array(
7926  "executable" => true,
7927  "errormessage" => ""
7928  );
7929  if (!$this->startingTimeReached())
7930  {
7931  $result["executable"] = false;
7932  $result["errormessage"] = sprintf($this->lng->txt("detail_starting_time_not_reached"), ilFormat::ftimestamp2datetimeDB($this->getStartingTime()));
7933  return $result;
7934  }
7935  if ($this->endingTimeReached())
7936  {
7937  $result["executable"] = false;
7938  $result["errormessage"] = sprintf($this->lng->txt("detail_ending_time_reached"), ilFormat::ftimestamp2datetimeDB($this->getEndingTime()));
7939  return $result;
7940  }
7941 
7942  $active_id = $this->getActiveIdOfUser($user_id);
7943 
7944  if ($this->getEnableProcessingTime())
7945  {
7946  if ($active_id > 0)
7947  {
7948  $starting_time = $this->getStartingTimeOfUser($active_id);
7949  if ($starting_time !== FALSE)
7950  {
7952  {
7953  if ($allowPassIncrease && $this->getResetProcessingTime() && (($this->getNrOfTries() == 0) || ($this->getNrOfTries() > ($this->_getPass($active_id)+1))))
7954  {
7955  // a test pass was quitted because the maximum processing time was reached, but the time
7956  // will be resetted for future passes, so if there are more passes allowed, the participant may
7957  // start the test again.
7958  // This code block is only called when $allowPassIncrease is TRUE which only happens when
7959  // the test info page is opened. Otherwise this will lead to unexpected results!
7960  $this->getTestSession()->increasePass();
7961  $this->getTestSession()->setLastSequence(0);
7962  $this->getTestSession()->saveToDb();
7963  }
7964  else
7965  {
7966  $result["executable"] = false;
7967  $result["errormessage"] = $this->lng->txt("detail_max_processing_time_reached");
7968  }
7969  return $result;
7970  }
7971  }
7972  }
7973  }
7974 
7975  if ($this->hasNrOfTriesRestriction() && ($active_id > 0) && $this->isNrOfTriesReached($this->getTestSession($active_id)->getPass()))
7976  {
7977  $result["executable"] = false;
7978  $result["errormessage"] = $this->lng->txt("maximum_nr_of_tries_reached");
7979  return $result;
7980  }
7981 
7982  if ($this->getTestSession($active_id)->isSubmitted())
7983  {
7984  $result["executable"] = FALSE;
7985  $result["errormessage"] = $this->lng->txt("maximum_nr_of_tries_reached");
7986  return $result;
7987  }
7988 
7989  // TODO: max. processing time
7990 
7991  return $result;
7992  }
7993 
8000  function canViewResults()
8001  {
8002  $result = true;
8003  if ($this->getScoreReporting() == 4) return false;
8004  if ($this->getReportingDate())
8005  {
8006  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches))
8007  {
8008  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
8009  $now = mktime();
8010  if ($now < $epoch_time)
8011  {
8012  $result = false;
8013  }
8014  }
8015  }
8016  return $result;
8017  }
8018 
8019  function canShowTestResults($user_id)
8020  {
8021  $active_id = $this->getActiveIdOfUser($user_id);
8022  if ($active_id > 0)
8023  {
8024  $starting_time = $this->getStartingTimeOfUser($active_id);
8025  }
8026  $notimeleft = FALSE;
8027  if ($starting_time !== FALSE)
8028  {
8030  {
8031  $notimeleft = TRUE;
8032  }
8033  }
8034  $result = TRUE;
8035  if (!$this->isTestFinishedToViewResults($active_id, $this->getTestSession($active_id)->getPass()) && ($this->getScoreReporting() == REPORT_AFTER_TEST))
8036  {
8037  $result = FALSE;
8038  }
8039  if (($this->endingTimeReached()) || $notimeleft) $result = TRUE;
8040  $result = $result & $this->canViewResults();
8041  return $result;
8042  }
8043 
8044  function canEditMarks()
8045  {
8046  $total = $this->evalTotalPersons();
8047  if ($total > 0)
8048  {
8049  if ($this->getReportingDate())
8050  {
8051  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches))
8052  {
8053  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
8054  $now = mktime();
8055  if ($now < $epoch_time)
8056  {
8057  return true;
8058  }
8059  }
8060  }
8061  return false;
8062  }
8063  else
8064  {
8065  return true;
8066  }
8067  }
8068 
8076  function getStartingTimeOfUser($active_id)
8077  {
8078  global $ilDB;
8079 
8080  if ($active_id < 1) return FALSE;
8081  $pass = ($this->getResetProcessingTime()) ? $this->_getPass($active_id) : 0;
8082  $result = $ilDB->queryF("SELECT tst_times.started FROM tst_times WHERE tst_times.active_fi = %s AND tst_times.pass = %s ORDER BY tst_times.started",
8083  array('integer', 'integer'),
8084  array($active_id, $pass)
8085  );
8086  if ($result->numRows())
8087  {
8088  $row = $ilDB->fetchAssoc($result);
8089  if (preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches))
8090  {
8091  return mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
8092  }
8093  else
8094  {
8095  return mktime();
8096  }
8097  }
8098  else
8099  {
8100  return mktime();
8101  }
8102  }
8103 
8113  {
8114  if ($this->getEnableProcessingTime())
8115  {
8117  $now = mktime();
8118  if ($now > ($starting_time + $processing_time))
8119  {
8120  return TRUE;
8121  }
8122  else
8123  {
8124  return FALSE;
8125  }
8126  }
8127  else
8128  {
8129  return FALSE;
8130  }
8131  }
8132 
8133  function &getTestQuestions()
8134  {
8135  global $ilDB;
8136  $query_result = $ilDB->queryF("SELECT qpl_questions.*, qpl_qst_type.type_tag, tst_test_question.sequence FROM qpl_questions, qpl_qst_type, tst_test_question WHERE qpl_questions.question_type_fi = qpl_qst_type.question_type_id AND tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id ORDER BY tst_test_question.sequence",
8137  array('integer'),
8138  array($this->getTestId())
8139  );
8140  $removableQuestions = array();
8141  while ($row = $ilDB->fetchAssoc($query_result))
8142  {
8143  array_push($removableQuestions, $row);
8144  }
8145  return $removableQuestions;
8146  }
8147 
8155  {
8156  return ($this->shuffle_questions) ? 1 : 0;
8157  }
8158 
8165  function setShuffleQuestions($a_shuffle)
8166  {
8167  $this->shuffle_questions = ($a_shuffle) ? 1 : 0;
8168  }
8169 
8183  {
8184  return ($this->show_summary) ? $this->show_summary : 0;
8185  }
8186 
8199  function setListOfQuestionsSettings($a_value = 0)
8200  {
8201  $this->show_summary = $a_value;
8202  }
8203 
8211  {
8212  if (($this->show_summary & 1) > 0)
8213  {
8214  return TRUE;
8215  }
8216  else
8217  {
8218  return FALSE;
8219  }
8220  }
8221 
8228  function setListOfQuestions($a_value = TRUE)
8229  {
8230  if ($a_value)
8231  {
8232  $this->show_summary = 1;
8233  }
8234  else
8235  {
8236  $this->show_summary = 0;
8237  }
8238  }
8239 
8247  {
8248  if (($this->show_summary & 2) > 0)
8249  {
8250  return TRUE;
8251  }
8252  else
8253  {
8254  return FALSE;
8255  }
8256  }
8257 
8264  function setListOfQuestionsStart($a_value = TRUE)
8265  {
8266  if ($a_value && $this->getListOfQuestions())
8267  {
8268  $this->show_summary = $this->show_summary | 2;
8269  }
8270  if (!$a_value && $this->getListOfQuestions())
8271  {
8272  if ($this->getListOfQuestionsStart())
8273  {
8274  $this->show_summary = $this->show_summary ^ 2;
8275  }
8276  }
8277  }
8278 
8286  {
8287  if (($this->show_summary & 4) > 0)
8288  {
8289  return TRUE;
8290  }
8291  else
8292  {
8293  return FALSE;
8294  }
8295  }
8296 
8303  function setListOfQuestionsEnd($a_value = TRUE)
8304  {
8305  if ($a_value && $this->getListOfQuestions())
8306  {
8307  $this->show_summary = $this->show_summary | 4;
8308  }
8309  if (!$a_value && $this->getListOfQuestions())
8310  {
8311  if ($this->getListOfQuestionsEnd())
8312  {
8313  $this->show_summary = $this->show_summary ^ 4;
8314  }
8315  }
8316  }
8317 
8325  {
8326  if (($this->show_summary & 8) > 0)
8327  {
8328  return TRUE;
8329  }
8330  else
8331  {
8332  return FALSE;
8333  }
8334  }
8335 
8342  function setListOfQuestionsDescription($a_value = TRUE)
8343  {
8344  if ($a_value && $this->getListOfQuestions())
8345  {
8346  $this->show_summary = $this->show_summary | 8;
8347  }
8348  if (!$a_value && $this->getListOfQuestions())
8349  {
8350  if ($this->getListOfQuestionsDescription())
8351  {
8352  $this->show_summary = $this->show_summary ^ 8;
8353  }
8354  }
8355  }
8356 
8364  {
8365  return ($this->results_presentation) ? $this->results_presentation : 0;
8366  }
8367 
8375  {
8376  if (($this->results_presentation & 1) > 0)
8377  {
8378  return TRUE;
8379  }
8380  else
8381  {
8382  return FALSE;
8383  }
8384  }
8385 
8393  {
8394  if (($this->results_presentation & 2) > 0)
8395  {
8396  return TRUE;
8397  }
8398  else
8399  {
8400  return FALSE;
8401  }
8402  }
8403 
8411  {
8412  if (($this->results_presentation & 4) > 0)
8413  {
8414  return TRUE;
8415  }
8416  else
8417  {
8418  return FALSE;
8419  }
8420  }
8421 
8429  {
8430  if (($this->results_presentation & 8) > 0)
8431  {
8432  return TRUE;
8433  }
8434  else
8435  {
8436  return FALSE;
8437  }
8438  }
8439 
8447  {
8448  if (($this->results_presentation & 16) > 0)
8449  {
8450  return TRUE;
8451  }
8452  else
8453  {
8454  return FALSE;
8455  }
8456  }
8457 
8465  {
8466  if (($this->results_presentation & 32) > 0)
8467  {
8468  return TRUE;
8469  }
8470  else
8471  {
8472  return FALSE;
8473  }
8474  }
8475 
8481  {
8482  if (($this->results_presentation & 64) > 0)
8483  {
8484  return TRUE;
8485  }
8486  else
8487  {
8488  return FALSE;
8489  }
8490  }
8491 
8498  function setResultsPresentation($a_results_presentation = 3)
8499  {
8500  $this->results_presentation = $a_results_presentation;
8501  }
8502 
8511  function setShowPassDetails($a_details = 1)
8512  {
8513  if ($a_details)
8514  {
8515  $this->results_presentation = $this->results_presentation | 1;
8516  }
8517  else
8518  {
8519  if ($this->getShowPassDetails())
8520  {
8521  $this->results_presentation = $this->results_presentation ^ 1;
8522  }
8523  }
8524  }
8525 
8532  function setShowSolutionDetails($a_details = 1)
8533  {
8534  if ($a_details)
8535  {
8536  $this->results_presentation = $this->results_presentation | 2;
8537  }
8538  else
8539  {
8540  if ($this->getShowSolutionDetails())
8541  {
8542  $this->results_presentation = $this->results_presentation ^ 2;
8543  }
8544  }
8545  }
8546 
8553  function canShowSolutionPrintview($user_id = NULL)
8554  {
8555  return $this->getShowSolutionPrintview();
8556  }
8557 
8564  function setShowSolutionPrintview($a_printview = 1)
8565  {
8566  if ($a_printview)
8567  {
8568  $this->results_presentation = $this->results_presentation | 4;
8569  }
8570  else
8571  {
8572  if ($this->getShowSolutionPrintview())
8573  {
8574  $this->results_presentation = $this->results_presentation ^ 4;
8575  }
8576  }
8577  }
8578 
8585  function setShowSolutionFeedback($a_feedback = TRUE)
8586  {
8587  if ($a_feedback)
8588  {
8589  $this->results_presentation = $this->results_presentation | 8;
8590  }
8591  else
8592  {
8593  if ($this->getShowSolutionFeedback())
8594  {
8595  $this->results_presentation = $this->results_presentation ^ 8;
8596  }
8597  }
8598  }
8599 
8606  function setShowSolutionAnswersOnly($a_full = TRUE)
8607  {
8608  if ($a_full)
8609  {
8610  $this->results_presentation = $this->results_presentation | 16;
8611  }
8612  else
8613  {
8614  if ($this->getShowSolutionAnswersOnly())
8615  {
8616  $this->results_presentation = $this->results_presentation ^ 16;
8617  }
8618  }
8619  }
8620 
8627  function setShowSolutionSignature($a_signature = FALSE)
8628  {
8629  if ($a_signature)
8630  {
8631  $this->results_presentation = $this->results_presentation | 32;
8632  }
8633  else
8634  {
8635  if ($this->getShowSolutionSignature())
8636  {
8637  $this->results_presentation = $this->results_presentation ^ 32;
8638  }
8639  }
8640  }
8641 
8648  function setShowSolutionSuggested($a_solution = FALSE)
8649  {
8650  if ($a_solution)
8651  {
8652  $this->results_presentation = $this->results_presentation | 64;
8653  }
8654  else
8655  {
8656  if ($this->getShowSolutionSuggested())
8657  {
8658  $this->results_presentation = $this->results_presentation ^ 64;
8659  }
8660  }
8661  }
8662 
8669  {
8670  // create a 5 character code
8671  $codestring = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
8672  mt_srand();
8673  $code = "";
8674  for ($i = 1; $i <=5; $i++)
8675  {
8676  $index = mt_rand(0, strlen($codestring)-1);
8677  $code .= substr($codestring, $index, 1);
8678  }
8679  // verify it against the database
8680  while ($this->isAccessCodeUsed($code))
8681  {
8682  $code = $this->createNewAccessCode();
8683  }
8684  return $code;
8685  }
8686 
8687  function isAccessCodeUsed($code)
8688  {
8689  global $ilDB;
8690 
8691  $result = $ilDB->queryF("SELECT anonymous_id FROM tst_active WHERE test_fi = %s AND anonymous_id = %s",
8692  array('integer', 'text'),
8693  array($this->getTestId(), $code)
8694  );
8695  return ($result->numRows() > 0) ? true : false;
8696  }
8697 
8698  public static function _getUserIdFromActiveId($active_id)
8699  {
8700  global $ilDB;
8701  $result = $ilDB->queryF("SELECT user_fi FROM tst_active WHERE active_id = %s",
8702  array('integer'),
8703  array($active_id)
8704  );
8705  if ($result->numRows())
8706  {
8707  $row = $ilDB->fetchAssoc($result);
8708  return $row["user_fi"];
8709  }
8710  else
8711  {
8712  return -1;
8713  }
8714  }
8715 
8717  {
8718  $id = $this->getTestId();
8719  if (!is_array($_SESSION["tst_access_code"]))
8720  {
8721  return "";
8722  }
8723  else
8724  {
8725  return $_SESSION["tst_access_code"]["$id"];
8726  }
8727  }
8728 
8729  function setAccessCodeSession($access_code)
8730  {
8731  $id = $this->getTestId();
8732  if (!is_array($_SESSION["tst_access_code"]))
8733  {
8734  $_SESSION["tst_access_code"] = array();
8735  }
8736  $_SESSION["tst_access_code"]["$id"] = $access_code;
8737  }
8738 
8740  {
8741  $id = $this->getTestId();
8742  unset($_SESSION["tst_access_code"]["$id"]);
8743  }
8744 
8745  function getAllowedUsers()
8746  {
8747  return ($this->allowedUsers) ? $this->allowedUsers : 0;
8748  }
8749 
8750  function setAllowedUsers($a_allowed_users)
8751  {
8752  $this->allowedUsers = $a_allowed_users;
8753  }
8754 
8756  {
8757  return ($this->allowedUsersTimeGap) ? $this->allowedUsersTimeGap : 0;
8758  }
8759 
8760  function setAllowedUsersTimeGap($a_allowed_users_time_gap)
8761  {
8762  $this->allowedUsersTimeGap = $a_allowed_users_time_gap;
8763  }
8764 
8766  {
8767  global $ilDB;
8768 
8769  $nr_of_users = $this->getAllowedUsers();
8770  $time_gap = ($this->getAllowedUsersTimeGap()) ? $this->getAllowedUsersTimeGap() : 60;
8771  if (($nr_of_users > 0) && ($time_gap > 0))
8772  {
8773  $now = mktime();
8774  $time_border = $now - $time_gap;
8775  $str_time_border = strftime("%Y%m%d%H%M%S", $time_border);
8776  $result = $ilDB->queryF("SELECT DISTINCT tst_times.active_fi FROM tst_times, tst_active WHERE tst_times.tstamp > %s AND tst_times.active_fi = tst_active.active_id AND tst_active.test_fi = %s",
8777  array('integer', 'integer'),
8778  array($time_border, $this->getTestId())
8779  );
8780  if ($result->numRows() >= $nr_of_users)
8781  {
8782  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
8784  {
8785  $this->logAction($this->lng->txtlng("assessment", "log_could_not_enter_test_due_to_simultaneous_users", ilObjAssessmentFolder::_getLogLanguage()));
8786  }
8787  return FALSE;
8788  }
8789  else
8790  {
8791  return TRUE;
8792  }
8793  }
8794  return TRUE;
8795  }
8796 
8797  function _getLastAccess($active_id)
8798  {
8799  global $ilDB;
8800 
8801  $result = $ilDB->queryF("SELECT finished FROM tst_times WHERE active_fi = %s ORDER BY finished DESC",
8802  array('integer'),
8803  array($active_id)
8804  );
8805  if ($result->numRows())
8806  {
8807  $row = $ilDB->fetchAssoc($result);
8808  return $row["finished"];
8809  }
8810  return "";
8811  }
8812 
8820  function isHTML($a_text)
8821  {
8822  if (preg_match("/<[^>]*?>/", $a_text))
8823  {
8824  return TRUE;
8825  }
8826  else
8827  {
8828  return FALSE;
8829  }
8830  }
8831 
8839  function QTIMaterialToString($a_material)
8840  {
8841  $result = "";
8842  for ($i = 0; $i < $a_material->getMaterialCount(); $i++)
8843  {
8844  $material = $a_material->getMaterial($i);
8845  if (strcmp($material["type"], "mattext") == 0)
8846  {
8847  $result .= $material["material"]->getContent();
8848  }
8849  if (strcmp($material["type"], "matimage") == 0)
8850  {
8851  $matimage = $material["material"];
8852  if (preg_match("/(il_([0-9]+)_mob_([0-9]+))/", $matimage->getLabel(), $matches))
8853  {
8854  // import an mediaobject which was inserted using tiny mce
8855  if (!is_array($_SESSION["import_mob_xhtml"])) $_SESSION["import_mob_xhtml"] = array();
8856  array_push($_SESSION["import_mob_xhtml"], array("mob" => $matimage->getLabel(), "uri" => $matimage->getUri()));
8857  }
8858  }
8859  }
8860  global $ilLog;
8861  $ilLog->write(print_r($_SESSION["import_mob_xhtml"], true));
8862  return $result;
8863  }
8864 
8873  function addQTIMaterial(&$a_xml_writer, $a_material)
8874  {
8875  include_once "./Services/RTE/classes/class.ilRTE.php";
8876  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
8877 
8878  $a_xml_writer->xmlStartTag("material");
8879  $attrs = array(
8880  "texttype" => "text/plain"
8881  );
8882  if ($this->isHTML($a_material))
8883  {
8884  $attrs["texttype"] = "text/xhtml";
8885  }
8886  $a_xml_writer->xmlElement("mattext", $attrs, ilRTE::_replaceMediaObjectImageSrc($a_material, 0));
8887 
8888  $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId());
8889  foreach ($mobs as $mob)
8890  {
8891  $moblabel = "il_" . IL_INST_ID . "_mob_" . $mob;
8892  if (strpos($a_material, "mm_$mob") !== FALSE)
8893  {
8894  if (ilObjMediaObject::_exists($mob))
8895  {
8896  $mob_obj =& new ilObjMediaObject($mob);
8897  $imgattrs = array(
8898  "label" => $moblabel,
8899  "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle()
8900  );
8901  }
8902  $a_xml_writer->xmlElement("matimage", $imgattrs, NULL);
8903  }
8904  }
8905  $a_xml_writer->xmlEndTag("material");
8906  }
8907 
8914  function prepareTextareaOutput($txt_output, $prepare_for_latex_output = FALSE)
8915  {
8916  include_once "./Services/Utilities/classes/class.ilUtil.php";
8917  return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output);
8918  }
8919 
8926  function saveCertificateVisibility($a_value)
8927  {
8928  global $ilDB;
8929 
8930  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET certificate_visibility = %s, tstamp = %s WHERE test_id = %s",
8931  array('text', 'integer', 'integer'),
8932  array($a_value, time(), $this->getTestId())
8933  );
8934  }
8935 
8943  {
8944  return (strlen($this->certificate_visibility)) ? $this->certificate_visibility : 0;
8945  }
8946 
8953  function setCertificateVisibility($a_value)
8954  {
8955  $this->certificate_visibility = $a_value;
8956  }
8957 
8964  function getAnonymity()
8965  {
8966  return ($this->anonymity) ? 1 : 0;
8967  }
8968 
8975  function setAnonymity($a_value = 0)
8976  {
8977  switch ($a_value)
8978  {
8979  case 1:
8980  $this->anonymity = 1;
8981  break;
8982  default:
8983  $this->anonymity = 0;
8984  break;
8985  }
8986  }
8987 
8994  function getShowCancel()
8995  {
8996  return ($this->show_cancel) ? 1 : 0;
8997  }
8998 
9005  function setShowCancel($a_value = 1)
9006  {
9007  switch ($a_value)
9008  {
9009  case 1:
9010  $this->show_cancel = 1;
9011  break;
9012  default:
9013  $this->show_cancel = 0;
9014  break;
9015  }
9016  }
9017 
9024  function getShowMarker()
9025  {
9026  return ($this->show_marker) ? 1 : 0;
9027  }
9028 
9035  function setShowMarker($a_value = 1)
9036  {
9037  switch ($a_value)
9038  {
9039  case 1:
9040  $this->show_marker = 1;
9041  break;
9042  default:
9043  $this->show_marker = 0;
9044  break;
9045  }
9046  }
9047 
9055  {
9056  return ($this->fixed_participants) ? 1 : 0;
9057  }
9058 
9065  function setFixedParticipants($a_value = 1)
9066  {
9067  switch ($a_value)
9068  {
9069  case 1:
9070  $this->fixed_participants = 1;
9071  break;
9072  default:
9073  $this->fixed_participants = 0;
9074  break;
9075  }
9076  }
9077 
9085  function _lookupAnonymity($a_obj_id)
9086  {
9087  global $ilDB;
9088 
9089  $result = $ilDB->queryF("SELECT anonymity FROM tst_tests WHERE obj_fi = %s",
9090  array('integer'),
9091  array($a_obj_id)
9092  );
9093  while($row = $ilDB->fetchAssoc($result))
9094  {
9095  return $row['anonymity'];
9096  }
9097  return 0;
9098  }
9099 
9107  function _lookupRandomTestFromActiveId($active_id)
9108  {
9109  global $ilDB;
9110 
9111  $result = $ilDB->queryF("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",
9112  array('integer'),
9113  array($active_id)
9114  );
9115  while($row = $ilDB->fetchAssoc($result))
9116  {
9117  return $row['random_test'];
9118  }
9119  return 0;
9120  }
9121 
9130  function userLookupFullName($user_id, $overwrite_anonymity = FALSE, $sorted_order = FALSE, $suffix = "")
9131  {
9132  if ($this->getAnonymity() && !$overwrite_anonymity)
9133  {
9134  return $this->lng->txt("unknown") . $suffix;
9135  }
9136  else
9137  {
9138  include_once './Services/User/classes/class.ilObjUser.php';
9139  $uname = ilObjUser::_lookupName($user_id);
9140  if (strlen($uname["firstname"].$uname["lastname"]) == 0) $uname["firstname"] = $this->lng->txt("deleted_user");
9141  if ($sorted_order)
9142  {
9143  return trim($uname["lastname"] . ", " . $uname["firstname"]) . $suffix;
9144  }
9145  else
9146  {
9147  return trim($uname["firstname"] . " " . $uname["lastname"]) . $suffix;
9148  }
9149  }
9150  }
9151 
9159  function getStartTestLabel($active_id)
9160  {
9161  if ($this->getNrOfTries() == 1)
9162  {
9163  return $this->lng->txt("tst_start_test");
9164  }
9165  $active_pass = $this->_getPass($active_id);
9166  $res = $this->getNrOfResultsForPass($active_id, $active_pass);
9167  if ($res == 0)
9168  {
9169  if ($active_pass == 0)
9170  {
9171  return $this->lng->txt("tst_start_test");
9172  }
9173  else
9174  {
9175  return $this->lng->txt("tst_start_new_test_pass");
9176  }
9177  }
9178  else
9179  {
9180  return $this->lng->txt("tst_resume_test");
9181  }
9182  }
9183 
9192  function &getAvailableDefaults($sortby = "name", $sortorder = "asc")
9193  {
9194  global $ilDB;
9195  global $ilUser;
9196 
9197  $result = $ilDB->queryF("SELECT * FROM tst_test_defaults WHERE user_fi = %s ORDER BY $sortby $sortorder",
9198  array('integer'),
9199  array($ilUser->getId())
9200  );
9201  $defaults = array();
9202  while ($row = $ilDB->fetchAssoc($result))
9203  {
9204  $defaults[$row["test_defaults_id"]] = $row;
9205  }
9206  return $defaults;
9207  }
9208 
9216  function &getTestDefaults($test_defaults_id)
9217  {
9218  global $ilDB;
9219 
9220  $result = $ilDB->queryF("SELECT * FROM tst_test_defaults WHERE test_defaults_id = %s",
9221  array('integer'),
9222  array($test_defaults_id)
9223  );
9224  if ($result->numRows() == 1)
9225  {
9226  $row = $ilDB->fetchAssoc($result);
9227  return $row;
9228  }
9229  else
9230  {
9231  return NULL;
9232  }
9233  }
9234 
9241  function deleteDefaults($test_default_id)
9242  {
9243  global $ilDB;
9244  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_defaults WHERE test_defaults_id = %s",
9245  array('integer'),
9246  array($test_default_id)
9247  );
9248  }
9249 
9256  function addDefaults($a_name)
9257  {
9258  global $ilDB;
9259  global $ilUser;
9260  $testsettings = array(
9261  "TitleOutput" => $this->getTitleOutput(),
9262  "PassScoring" => $this->getPassScoring(),
9263  "Introduction" => $this->getIntroduction(),
9264  "FinalStatement" => $this->getFinalStatement(),
9265  "ShowInfo" => $this->getShowInfo(),
9266  "ForceJS" => $this->getForceJS(),
9267  "CustomStyle" => $this->getCustomStyle(),
9268  "ShowFinalStatement" => $this->getShowFinalStatement(),
9269  "SequenceSettings" => $this->getSequenceSettings(),
9270  "ScoreReporting" => $this->getScoreReporting(),
9271  "InstantFeedbackSolution" => $this->getInstantFeedbackSolution(),
9272  "AnswerFeedback" => $this->getAnswerFeedback(),
9273  "AnswerFeedbackPoints" => $this->getAnswerFeedbackPoints(),
9274  "ResultsPresentation" => $this->getResultsPresentation(),
9275  "Anonymity" => $this->getAnonymity(),
9276  "ShowCancel" => $this->getShowCancel(),
9277  "ShowMarker" => $this->getShowMarker(),
9278  "ReportingDate" => $this->getReportingDate(),
9279  "NrOfTries" => $this->getNrOfTries(),
9280  "Shuffle" => $this->getShuffleQuestions(),
9281  "Kiosk" => $this->getKiosk(),
9282  "UsePreviousAnswers" => $this->getUsePreviousAnswers(),
9283  "ProcessingTime" => $this->getProcessingTime(),
9284  "EnableProcessingTime" => $this->getEnableProcessingTime(),
9285  "ResetProcessingTime" => $this->getResetProcessingTime(),
9286  "StartingTime" => $this->getStartingTime(),
9287  "EndingTime" => $this->getEndingTime(),
9288  "ECTSOutput" => $this->getECTSOutput(),
9289  "ECTSFX" => $this->getECTSFX(),
9290  "ECTSGrades" => $this->getECTSGrades(),
9291  "isRandomTest" => $this->isRandomTest(),
9292  "RandomQuestionCount" => $this->getRandomQuestionCount(),
9293  "CountSystem" => $this->getCountSystem(),
9294  "MCScoring" => $this->getMCScoring(),
9295  "mailnotification" => $this->getMailNotification(),
9296  "mailnottype" => $this->getMailNotificationType(),
9297  "exportsettings" => $this->getExportSettings(),
9298  "ListOfQuestionsSettings" => $this->getListOfQuestionsSettings()
9299  );
9300  $next_id = $ilDB->nextId('tst_test_defaults');
9301  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_defaults (test_defaults_id, name, user_fi, defaults, marks, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
9302  array('integer', 'text', 'integer', 'text', 'text', 'integer'),
9303  array($next_id, $a_name, $ilUser->getId(), serialize($testsettings), serialize($this->mark_schema), time())
9304  );
9305  }
9306 
9314  function applyDefaults($test_defaults_id)
9315  {
9316  $total = $this->evalTotalPersons();
9317  $result = FALSE;
9318  if (($this->getQuestionCount() == 0) && ($total == 0))
9319  {
9320  // only apply if there are no questions added and not user datasets exist
9321  $defaults =& $this->getTestDefaults($test_defaults_id);
9322  $testsettings = unserialize($defaults["defaults"]);
9323  include_once "./Modules/Test/classes/class.assMarkSchema.php";
9324  $this->mark_schema = unserialize($defaults["marks"]);
9325  $this->setTitleOutput($testsettings["TitleOutput"]);
9326  $this->setPassScoring($testsettings["PassScoring"]);
9327  $this->setIntroduction($testsettings["Introduction"]);
9328  $this->setFinalStatement($testsettings["FinalStatement"]);
9329  $this->setShowInfo($testsettings["ShowInfo"]);
9330  $this->setForceJS($testsettings["ForceJS"]);
9331  $this->setCustomStyle($testsettings["CustomStyle"]);
9332  $this->setShowFinalStatement($testsettings["ShowFinalStatement"]);
9333  $this->setSequenceSettings($testsettings["SequenceSettings"]);
9334  $this->setScoreReporting($testsettings["ScoreReporting"]);
9335  $this->setInstantFeedbackSolution($testsettings["InstantFeedbackSolution"]);
9336  $this->setAnswerFeedback($testsettings["AnswerFeedback"]);
9337  $this->setAnswerFeedbackPoints($testsettings["AnswerFeedbackPoints"]);
9338  $this->setResultsPresentation($testsettings["ResultsPresentation"]);
9339  $this->setAnonymity($testsettings["Anonymity"]);
9340  $this->setShowCancel($testsettings["ShowCancel"]);
9341  $this->setShowMarker($testsettings["ShowMarker"]);
9342  $this->setReportingDate($testsettings["ReportingDate"]);
9343  $this->setShuffleQuestions($testsettings["this"]);
9344  $this->setNrOfTries($testsettings["NrOfTries"]);
9345  $this->setUsePreviousAnswers($testsettings["UsePreviousAnswers"]);
9346  $this->setProcessingTime($testsettings["ProcessingTime"]);
9347  $this->setResetProcessingTime($testsettings["ResetProcessingTime"]);
9348  $this->setEnableProcessingTime($testsettings["EnableProcessingTime"]);
9349  $this->setStartingTime($testsettings["StartingTime"]);
9350  $this->setKiosk($testsettings["Kiosk"]);
9351  $this->setEndingTime($testsettings["EndingTime"]);
9352  $this->setECTSOutput($testsettings["ECTSOutput"]);
9353  $this->setECTSFX($testsettings["ECTSFX"]);
9354  $this->setECTSGrades($testsettings["ECTSGrades"]);
9355  $this->setRandomTest($testsettings["isRandomTest"]);
9356  $this->setRandomQuestionCount($testsettings["RandomQuestionCount"]);
9357  $this->setCountSystem($testsettings["CountSystem"]);
9358  $this->setMCScoring($testsettings["MCScoring"]);
9359  $this->setMailNotification($testsettings["mailnotification"]);
9360  $this->setMailNotificationType($testsettings["mailnottype"]);
9361  $this->setExportSettings($testsettings['exportsettings']);
9362  $this->setListOfQuestionsSettings($testsettings["ListOfQuestionsSettings"]);
9363  $this->saveToDb();
9364  $result = TRUE;
9365  }
9366  return $result;
9367  }
9368 
9376  function processPrintoutput2FO($print_output)
9377  {
9378  if (extension_loaded("tidy"))
9379  {
9380  $config = array(
9381  "indent" => false,
9382  "output-xml" => true,
9383  "numeric-entities" => true
9384  );
9385  $tidy = new tidy();
9386  $tidy->parseString($print_output, $config, 'utf8');
9387  $tidy->cleanRepair();
9388  $print_output = tidy_get_output($tidy);
9389  $print_output = preg_replace("/^.*?(<html)/", "\\1", $print_output);
9390  }
9391  else
9392  {
9393  $print_output = str_replace("&nbsp;", "&#160;", $print_output);
9394  $print_output = str_replace("&otimes;", "X", $print_output);
9395  }
9396  $xsl = file_get_contents("./Modules/Test/xml/question2fo.xsl");
9397  $args = array( '/_xml' => $print_output, '/_xsl' => $xsl );
9398  $xh = xslt_create();
9399  $params = array();
9400  $output = xslt_process($xh, "arg:/_xml", "arg:/_xsl", NULL, $args, $params);
9401  xslt_error($xh);
9402  xslt_free($xh);
9403  return $output;
9404  }
9405 
9412  public function deliverPDFfromHTML($content, $title = NULL)
9413  {
9414  $content = preg_replace("/href=\".*?\"/", "", $content);
9415  $printbody = new ilTemplate("tpl.il_as_tst_print_body.html", TRUE, TRUE, "Modules/Test");
9416  $printbody->setVariable("TITLE", ilUtil::prepareFormOutput($this->getTitle()));
9417  $printbody->setVariable("ADM_CONTENT", $content);
9418  $printbody->setCurrentBlock("css_file");
9419  $printbody->setVariable("CSS_FILE", $this->getTestStyleLocation("filesystem"));
9420  $printbody->parseCurrentBlock();
9421  $printbody->setCurrentBlock("css_file");
9422  $printbody->setVariable("CSS_FILE", ilUtil::getStyleSheetLocation("filesystem", "delos.css"));
9423  $printbody->parseCurrentBlock();
9424  $printoutput = $printbody->get();
9425  $html = str_replace("href=\"./", "href=\"" . ILIAS_HTTP_PATH . "/", $printoutput);
9426  $html = preg_replace("/<div id=\"dontprint\">.*?<\\/div>/ims", "", $html);
9427  if (extension_loaded("tidy"))
9428  {
9429  $config = array(
9430  "indent" => false,
9431  "output-xml" => true,
9432  "numeric-entities" => true
9433  );
9434  $tidy = new tidy();
9435  $tidy->parseString($html, $config, 'utf8');
9436  $tidy->cleanRepair();
9437  $html = tidy_get_output($tidy);
9438  $html = preg_replace("/^.*?(<html)/", "\\1", $html);
9439  }
9440  else
9441  {
9442  $html = str_replace("&nbsp;", "&#160;", $html);
9443  $html = str_replace("&otimes;", "X", $html);
9444  }
9445 // include_once "./Services/Utilities/classes/class.ilUtil.php";
9446 // $html_file = ilUtil::ilTempnam() . "_debug_html.html";
9447 // $fp = fopen($html_file, "w"); fwrite($fp, $html); fclose($fp);
9448  $this->deliverPDFfromFO($this->processPrintoutput2FO($html), $title);
9449  }
9450 
9457  public function deliverPDFfromFO($fo, $title = null)
9458  {
9459  global $ilLog;
9460 
9461  include_once "./Services/Utilities/classes/class.ilUtil.php";
9462  include_once './Services/WebServices/RPC/classes/class.ilRpcClientFactory.php';
9463  try
9464  {
9465  $pdf_base64 = ilRpcClientFactory::factory('RPCTransformationHandler')->ilFO2PDF($fo);
9466  $filename = (strlen($title)) ? $title : $this->getTitle();
9467  ilUtil::deliverData($pdf_base64->scalar, ilUtil::getASCIIFilename($filename) . ".pdf", "application/pdf", false, true);
9468  return true;
9469  }
9470  catch(XML_RPC2_FaultException $e)
9471  {
9472  $ilLog->write(__METHOD__.': '.$e->getMessage());
9473  return false;
9474  }
9475  catch(Exception $e)
9476  {
9477  $ilLog->write(__METHOD__.': '.$e->getMessage());
9478  return false;
9479  }
9480 
9481  /*
9482  include_once "./Services/Transformation/classes/class.ilFO2PDF.php";
9483  $fo2pdf = new ilFO2PDF();
9484  $fo2pdf->setFOString($fo);
9485  $result = $fo2pdf->send();
9486  $filename = (strlen($title)) ? $title : $this->getTitle();
9487  ilUtil::deliverData($result, ilUtil::getASCIIFilename($filename) . ".pdf", "application/pdf", false, true);
9488  */
9489  }
9490 
9500  static function getManualFeedback($active_id, $question_id, $pass)
9501  {
9502  global $ilDB;
9503  $feedback = "";
9504  $result = $ilDB->queryF("SELECT feedback FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
9505  array('integer', 'integer', 'integer'),
9506  array($active_id, $question_id, $pass)
9507  );
9508  if ($result->numRows())
9509  {
9510  $row = $ilDB->fetchAssoc($result);
9511  include_once("./Services/RTE/classes/class.ilRTE.php");
9512  $feedback = ilRTE::_replaceMediaObjectImageSrc($row["feedback"], 1);
9513  }
9514  return $feedback;
9515  }
9516 
9527  function saveManualFeedback($active_id, $question_id, $pass, $feedback)
9528  {
9529  global $ilDB;
9530 
9531  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
9532  array('integer', 'integer', 'integer'),
9533  array($active_id, $question_id, $pass)
9534  );
9535 
9536  if (strlen($feedback))
9537  {
9538  $next_id = $ilDB->nextId('tst_manual_fb');
9539  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_manual_fb (manual_feedback_id, active_fi, question_fi, pass, feedback, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
9540  array('integer', 'integer', 'integer', 'integer', 'text', 'integer'),
9541  array($next_id, $active_id, $question_id, $pass, ilRTE::_replaceMediaObjectImageSrc($feedback, 0), time())
9542  );
9543  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
9545  {
9546  global $lng, $ilUser;
9547  include_once "./Modules/Test/classes/class.ilObjTestAccess.php";
9548  $username = ilObjTestAccess::_getParticipantData($active_id);
9549  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
9550  $this->logAction(sprintf($lng->txtlng("assessment", "log_manual_feedback", ilObjAssessmentFolder::_getLogLanguage()), $ilUser->getFullname() . " (" . $ilUser->getLogin() . ")", $username, assQuestion::_getQuestionTitle($question_id), $feedback));
9551  }
9552  }
9553  if (PEAR::isError($result))
9554  {
9555  global $ilias;
9556  $ilias->raiseError($result->getMessage());
9557  }
9558  else
9559  {
9560  return TRUE;
9561  }
9562  }
9563 
9572  {
9573  global $ilUser;
9574  if (strcmp($_GET["tst_javascript"], "0") == 0) return FALSE;
9575  if ($this->getForceJS()) return TRUE;
9576  $assessmentSetting = new ilSetting("assessment");
9577  return ($ilUser->getPref("tst_javascript") === FALSE) ? $assessmentSetting->get("use_javascript") : $ilUser->getPref("tst_javascript");
9578  }
9579 
9586  function &createTestSession()
9587  {
9588  global $ilUser;
9589 
9590  include_once "./Modules/Test/classes/class.ilTestSession.php";
9591  $testSession = FALSE;
9592  $testSession = new ilTestSession();
9593  $testSession->setRefId($this->getRefId());
9594  $testSession->setTestId($this->getTestId());
9595  $testSession->setUserId($ilUser->getId());
9596  $testSession->setAnonymousId($_SESSION["tst_access_code"][$this->getTestId()]);
9597  $testSession->saveToDb();
9598  $this->testSession =& $testSession;
9599  return $this->testSession;
9600  }
9601 
9607  public function setTestId($a_id)
9608  {
9609  $this->test_id = $a_id;
9610  }
9611 
9619  function &setTestSession($active_id = "")
9620  {
9621  if (is_object($this->testSession) && ($this->testSession->getActiveId() > 0)) return $this->testSession;
9622 
9623  global $ilUser;
9624 
9625  include_once "./Modules/Test/classes/class.ilTestSession.php";
9626  $testSession = FALSE;
9627  if ($active_id > 0)
9628  {
9629  $testSession = new ilTestSession($active_id);
9630  $testSession->setRefId($this->getRefId());
9631  }
9632  else
9633  {
9634  $testSession = new ilTestSession();
9635  $testSession->setRefId($this->getRefId());
9636  $testSession->loadTestSession($this->getTestId(), $ilUser->getId(), $_SESSION["tst_access_code"][$this->getTestId()]);
9637  }
9638  $this->testSession =& $testSession;
9639  return $this->testSession;
9640  }
9641 
9648  function &getTestSession($active_id = "")
9649  {
9650  if (is_object($this->testSession) && ($this->testSession->getActiveId() > 0)) return $this->testSession;
9651  return $this->setTestSession($active_id);
9652  }
9653 
9654  function &createTestSequence($active_id, $pass, $shuffle)
9655  {
9656  include_once "./Modules/Test/classes/class.ilTestSequence.php";
9657  $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
9658  if (!$this->testSequence->hasSequence())
9659  {
9660  $this->testSequence->createNewSequence($this->getQuestionCount(), $shuffle);
9661  $this->testSequence->saveToDb();
9662  }
9663  }
9664 
9665  function &getTestSequence($active_id = "", $pass = "")
9666  {
9667  if (is_object($this->testSequence) && ($this->testSequence->getActiveId() > 0)) return $this->testSequence;
9668 
9669  include_once "./Modules/Test/classes/class.ilTestSequence.php";
9670  if (($active_id > 0) && (strlen($pass)))
9671  {
9672  $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
9673  }
9674  else
9675  {
9676  $this->testSequence = new ilTestSequence($this->getTestSession()->getActiveId(), $this->getTestSession()->getPass(), $this->isRandomTest());
9677  }
9678  return $this->testSequence;
9679  }
9680 
9682  {
9683  if ($this->getTestSession()->getActiveId() > 0)
9684  {
9685  $result = $this->getTestResult($this->getTestSession()->getActiveId(), $this->getTestSession()->getPass(), TRUE);
9686  foreach ($result as $sequence => $question)
9687  {
9688  if (is_numeric($sequence))
9689  {
9690  if ($question["reached"] == $question["max"])
9691  {
9692  $this->getTestSequence()->hideQuestion($question["qid"]);
9693  }
9694  }
9695  }
9696  $this->getTestSequence()->saveToDb();
9697  }
9698  }
9699 
9708  function getDetailedTestResults($participants)
9709  {
9710  $results = array();
9711  if (count($participants))
9712  {
9713  foreach ($participants as $active_id => $user_rec)
9714  {
9715  $row = array();
9716  $reached_points = 0;
9717  $max_points = 0;
9718  foreach ($this->questions as $value)
9719  {
9720  $question =& ilObjTest::_instanciateQuestion($value);
9721  if (is_object($question))
9722  {
9723  $max_points += $question->getMaximumPoints();
9724  $reached_points += $question->getReachedPoints($active_id);
9725  if ($max_points > 0)
9726  {
9727  $percentvalue = $reached_points / $max_points;
9728  if ($percentvalue < 0) $percentvalue = 0.0;
9729  }
9730  else
9731  {
9732  $percentvalue = 0;
9733  }
9734  if ($this->getAnonymity())
9735  {
9736  $user_rec['firstname'] = "";
9737  $user_rec['lastname'] = $this->lng->txt("unknown");
9738  }
9739  $row = array(
9740  "user_id"=>$user_rec['usr_id'],
9741  "matriculation" => $user_rec['matriculation'],
9742  "lastname" => $user_rec['lastname'],
9743  "firstname" => $user_rec['firstname'],
9744  "login"=>$user_rec['login'],
9745  "question_id" => $question->getId(),
9746  "question_title" => $question->getTitle(),
9747  "reached_points" => $reached_points,
9748  "max_points" => $max_points
9749  );
9750  $results[] = $row;
9751  }
9752  }
9753  }
9754  }
9755  return $results;
9756  }
9757 
9762  {
9763  global $ilDB;
9764 
9765  $result = $ilDB->queryF("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",
9766  array('integer'),
9767  array($a_q_id)
9768  );
9769  $rec = $ilDB->fetchAssoc($result);
9770  return $rec["obj_id"];
9771  }
9772 
9779  function isPluginActive($a_pname)
9780  {
9781  global $ilPluginAdmin;
9782  if ($ilPluginAdmin->isActive(IL_COMP_MODULE, "TestQuestionPool", "qst", $a_pname))
9783  {
9784  return TRUE;
9785  }
9786  else
9787  {
9788  return FALSE;
9789  }
9790  }
9791 
9792  protected function getPassed($active_id)
9793  {
9794  global $ilDB;
9795 
9796  $result = $ilDB->queryF("SELECT passed FROM tst_result_cache WHERE active_fi = %s",
9797  array('integer'),
9798  array($active_id)
9799  );
9800  if ($result->numRows())
9801  {
9802  $row = $ilDB->fetchAssoc($result);
9803  return $row['passed'];
9804  }
9805  else
9806  {
9807  $counted_pass = ilObjTest::_getResultPass($active_id);
9808  $result_array =& $this->getTestResult($active_id, $counted_pass);
9809  return $result_array["test"]["passed"];
9810  }
9811  }
9812 
9818  function canShowCertificate($user_id, $active_id)
9819  {
9820  if ($this->canShowTestResults($user_id))
9821  {
9822  include_once "./Services/Certificate/classes/class.ilCertificate.php";
9823  include_once "./Modules/Test/classes/class.ilTestCertificateAdapter.php";
9824  $cert = new ilCertificate(new ilTestCertificateAdapter($this));
9825  if ($cert->isComplete())
9826  {
9827  $vis = $this->getCertificateVisibility();
9828  $showcert = FALSE;
9829  switch ($vis)
9830  {
9831  case 0:
9832  $showcert = TRUE;
9833  break;
9834  case 1:
9835  if ($this->getPassed($active_id))
9836  {
9837  $showcert = TRUE;
9838  }
9839  break;
9840  case 2:
9841  $showcert = FALSE;
9842  break;
9843  }
9844  if ($showcert)
9845  {
9846  return TRUE;
9847  }
9848  else
9849  {
9850  return FALSE;
9851  }
9852  }
9853  else
9854  {
9855  return FALSE;
9856  }
9857  }
9858  else
9859  {
9860  return FALSE;
9861  }
9862  }
9863 
9870  {
9871  global $ilDB;
9872  $result = $ilDB->queryF("SELECT tst_test_result.active_fi, tst_test_result.question_fi, tst_test_result.pass FROM tst_test_result, tst_active, qpl_questions WHERE tst_active.active_id = tst_test_result.active_fi AND tst_active.test_fi = %s AND tst_test_result.question_fi = qpl_questions.question_id AND qpl_questions.original_id = %s",
9873  array('integer', 'integer'),
9874  array($test_id, $question_id)
9875  );
9876  $foundusers = array();
9877  while ($row = $ilDB->fetchAssoc($result))
9878  {
9879  if (!array_key_exists($row["active_fi"], $foundusers))
9880  {
9881  $foundusers[$row["active_fi"]] = array();
9882  }
9883  array_push($foundusers[$row["active_fi"]], array("pass" => $row["pass"], "qid" => $row["question_fi"]));
9884  }
9885  return $foundusers;
9886  }
9887 
9893  public function hasPDFProcessing()
9894  {
9895  global $ilias;
9896 
9897  include_once './Services/WebServices/RPC/classes/class.ilRPCServerSettings.php';
9898  if(ilRPCServerSettings::getInstance()->isEnabled())
9899  {
9900  return TRUE;
9901  }
9902  else
9903  {
9904  return FALSE;
9905  }
9906  }
9907 
9913  public function getAggregatedResultsData()
9914  {
9915  $data =& $this->getCompleteEvaluationData();
9916  $foundParticipants =& $data->getParticipants();
9917  $results = array("overview" => array(), "questions" => array());
9918  if (count($foundParticipants))
9919  {
9920  $results["overview"][$this->lng->txt("tst_eval_total_persons")] = count($foundParticipants);
9921  $total_finished = $this->evalTotalFinished();
9922  $results["overview"][$this->lng->txt("tst_eval_total_finished")] = $total_finished;
9923  $average_time = $this->evalTotalStartedAverageTime();
9924  $diff_seconds = $average_time;
9925  $diff_hours = floor($diff_seconds/3600);
9926  $diff_seconds -= $diff_hours * 3600;
9927  $diff_minutes = floor($diff_seconds/60);
9928  $diff_seconds -= $diff_minutes * 60;
9929  $results["overview"][$this->lng->txt("tst_eval_total_finished_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds);
9930  $total_passed = 0;
9931  $total_passed_reached = 0;
9932  $total_passed_max = 0;
9933  $total_passed_time = 0;
9934  foreach ($foundParticipants as $userdata)
9935  {
9936  if ($userdata->getPassed())
9937  {
9938  $total_passed++;
9939  $total_passed_reached += $userdata->getReached();
9940  $total_passed_max += $userdata->getMaxpoints();
9941  $total_passed_time += $userdata->getTimeOfWork();
9942  }
9943  }
9944  $average_passed_reached = $total_passed ? $total_passed_reached / $total_passed : 0;
9945  $average_passed_max = $total_passed ? $total_passed_max / $total_passed : 0;
9946  $average_passed_time = $total_passed ? $total_passed_time / $total_passed : 0;
9947  $results["overview"][$this->lng->txt("tst_eval_total_passed")] = $total_passed;
9948  $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);
9949  $average_time = $average_passed_time;
9950  $diff_seconds = $average_time;
9951  $diff_hours = floor($diff_seconds/3600);
9952  $diff_seconds -= $diff_hours * 3600;
9953  $diff_minutes = floor($diff_seconds/60);
9954  $diff_seconds -= $diff_minutes * 60;
9955  $results["overview"][$this->lng->txt("tst_eval_total_passed_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds);
9956  }
9957 
9958  foreach ($data->getQuestionTitles() as $question_id => $question_title)
9959  {
9960  $answered = 0;
9961  $reached = 0;
9962  $max = 0;
9963  foreach ($foundParticipants as $userdata)
9964  {
9965  for ($i = 0; $i <= $userdata->getLastPass(); $i++)
9966  {
9967  if (is_object($userdata->getPass($i)))
9968  {
9969  $question =& $userdata->getPass($i)->getAnsweredQuestionByQuestionId($question_id);
9970  if (is_array($question))
9971  {
9972  $answered++;
9973  $reached += $question["reached"];
9974  $max += $question["points"];
9975  }
9976  }
9977  }
9978  }
9979  $percent = $max ? $reached/$max * 100.0 : 0;
9980  $counter++;
9981  $results["questions"][$question_id] = array(
9982  $question_title,
9983  sprintf("%.2f", $answered ? $reached / $answered : 0) . " " . strtolower($this->lng->txt("of")) . " " . sprintf("%.2f", $answered ? $max / $answered : 0),
9984  sprintf("%.2f", $percent) . "%",
9985  $answered,
9986  sprintf("%.2f", $answered ? $reached / $answered : 0),
9987  sprintf("%.2f", $answered ? $max / $answered : 0),
9988  $percent / 100.0
9989  );
9990  }
9991  return $results;
9992  }
9993 
9997  function getXMLZip()
9998  {
9999  include_once("./Modules/Test/classes/class.ilTestExport.php");
10000  $test_exp = new ilTestExport($this, "xml");
10001  return $test_exp->buildExportFile();
10002  }
10003 
10007  public function getMailNotification()
10008  {
10009  return $this->mailnotification;
10010  }
10011 
10017  public function setMailNotification($a_notification)
10018  {
10019  $this->mailnotification = $a_notification;
10020  }
10021 
10022  public function sendSimpleNotification($active_id)
10023  {
10024  include_once "./Services/Mail/classes/class.ilMail.php";
10025  $mail = new ilMail(ANONYMOUS_USER_ID);
10026 
10027  $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id));
10028  $message = new ilTemplate("tpl.il_as_tst_finish_notification_simple.html", TRUE, TRUE, "Modules/Test");
10029  $message->setVariable('TEXT_TEST_TITLE', $this->lng->txt('title'));
10030  $message->setVariable('VALUE_TEST_TITLE', $this->getTitle());
10031  $message->setVariable('TEXT_USER_NAME', $this->lng->txt('username'));
10032  $message->setVariable('VALUE_USER_NAME', $usr_data);
10033  $message->setVariable('TEXT_FINISH_TIME', $this->lng->txt('tst_finished'));
10035  $message->setVariable('VALUE_FINISH_TIME', ilDatePresentation::formatDate(new ilDateTime(time(), IL_CAL_UNIX)));
10036 
10037  $res = $mail->sendMail(
10038  ilObjUser::_lookupLogin($this->getOwner()), // to
10039  "", // cc
10040  "", // bcc
10041  sprintf($this->lng->txt('tst_user_finished_test'), $this->getTitle()), // subject
10042  $message->get(), // message
10043  array(), // attachments
10044  array('normal') // type
10045  );
10046  global $ilLog; $ilLog->write("sending mail: " . $res);
10047  }
10048 
10055  {
10056  include_once "./Modules/Test/classes/class.ilObjTestGUI.php";
10057  include_once "./Modules/Test/classes/tables/class.ilEvaluationAllTableGUI.php";
10058  $table_gui = new ilEvaluationAllTableGUI(new ilObjTestGUI(), 'outEvaluation', $this->getAnonymity());
10059  return $table_gui->getSelectedColumns();
10060  }
10061 
10062  public function sendAdvancedNotification($active_id)
10063  {
10064  include_once "./Services/Mail/classes/class.ilMail.php";
10065  $mail = new ilMail(ANONYMOUS_USER_ID);
10066 
10067  $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id));
10068  $message = new ilTemplate("tpl.il_as_tst_finish_notification_simple.html", TRUE, TRUE, "Modules/Test");
10069  $message->setVariable('TEXT_TEST_TITLE', $this->lng->txt('title'));
10070  $message->setVariable('VALUE_TEST_TITLE', $this->getTitle());
10071  $message->setVariable('TEXT_USER_NAME', $this->lng->txt('username'));
10072  $message->setVariable('VALUE_USER_NAME', $usr_data);
10073  $message->setVariable('TEXT_FINISH_TIME', $this->lng->txt('tst_finished'));
10075  $message->setVariable('VALUE_FINISH_TIME', ilDatePresentation::formatDate(new ilDateTime(time(), IL_CAL_UNIX)));
10076 
10077  include_once "./Modules/Test/classes/class.ilTestExport.php";
10078  $exportObj = new ilTestExport($this, "results");
10079  $file = $exportObj->exportToExcel($deliver = FALSE, 'active_id', $active_id, $passedonly = FALSE);
10080  include_once "./classes/class.ilFileDataMail.php";
10081  $fd = new ilFileDataMail(ANONYMOUS_USER_ID);
10082  $fd->copyAttachmentFile($file, "result_" . $active_id . ".xls");
10083  $file_names[] = "result_" . $active_id . ".xls";
10084  $result = $mail->sendMail(
10085  ilObjUser::_lookupLogin($this->getOwner()), // to
10086  "", // cc
10087  "", // bcc
10088  sprintf($this->lng->txt('tst_user_finished_test'), $this->getTitle()), // subject
10089  $message->get(), // message
10090  count($file_names) ? $file_names : array(), // attachments
10091  array('normal') // type
10092  );
10093  if(count($file_names))
10094  {
10095  $fd->unlinkFiles($file_names);
10096  unset($fd);
10097  @unlink($file);
10098  }
10099  }
10100 
10101  function createRandomSolutions($number)
10102  {
10103  global $ilDB;
10104 
10105  // 1. get a user
10106  $query = "SELECT usr_id FROM usr_data";
10107  $result = $ilDB->query($query);
10108  while ($data = $ilDB->fetchAssoc($result))
10109  {
10110  $activequery = sprintf("SELECT user_fi FROM tst_active WHERE test_fi = %s AND user_fi = %s",
10111  $ilDB->quote($this->getTestId()),
10112  $ilDB->quote($data['usr_id'])
10113  );
10114  $activeresult = $ilDB->query($activequery);
10115  if ($activeresult->numRows() == 0)
10116  {
10117  $user_id = $data['usr_id'];
10118  if ($user_id != 13)
10119  {
10120  include_once "./Modules/Test/classes/class.ilTestSession.php";
10121  $testSession = FALSE;
10122  $testSession = new ilTestSession();
10123  $testSession->setRefId($this->getRefId());
10124  $testSession->setTestId($this->getTestId());
10125  $testSession->setUserId($user_id);
10126  $testSession->saveToDb();
10127  $passes = ($this->getNrOfTries()) ? $this->getNrOfTries() : 10;
10128  $nr_of_passes = rand(1, $passes);
10129  $active_id = $testSession->getActiveId();
10130  for ($pass = 0; $pass < $nr_of_passes; $pass++)
10131  {
10132  include_once "./Modules/Test/classes/class.ilTestSequence.php";
10133  $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
10134  if (!$testSequence->hasSequence())
10135  {
10136  $testSequence->createNewSequence($this->getQuestionCount(), $shuffle);
10137  $testSequence->saveToDb();
10138  }
10139  for ($seq = 1; $seq <= count($this->questions); $seq++)
10140  {
10141  $question_id = $testSequence->getQuestionForSequence($seq);
10142  $objQuestion = ilObjTest::_instanciateQuestion($question_id);
10143  $objQuestion->createRandomSolution($testSession->getActiveId(), $pass);
10144  }
10145  if ($pass < $nr_of_passes - 1)
10146  {
10147  $testSession->increasePass();
10148  $testSession->setLastSequence(0);
10149  $testSession->saveToDb();
10150  }
10151  else
10152  {
10153  $this->setActiveTestSubmitted($user_id);
10154  }
10155  }
10156  $number--;
10157  if ($number == 0) return;
10158  }
10159  }
10160  }
10161  }
10162 
10163  public function getResultsForActiveId($active_id)
10164  {
10165  global $ilDB;
10166 
10167  $result = $ilDB->queryF("SELECT * FROM tst_result_cache WHERE active_fi = %s",
10168  array('integer'),
10169  array($active_id)
10170  );
10171  if (!$result->numRows())
10172  {
10173  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
10175  $result = $ilDB->queryF("SELECT * FROM tst_result_cache WHERE active_fi = %s",
10176  array('integer'),
10177  array($active_id)
10178  );
10179  }
10180  $row = $ilDB->fetchAssoc($result);
10181  return $row;
10182 
10183  }
10184 
10185  public function getMailNotificationType()
10186  {
10187  if ($this->mailnottype == 1)
10188  {
10189  return $this->mailnottype;
10190  }
10191  else
10192  {
10193  return 0;
10194  }
10195  }
10196 
10197  public function setMailNotificationType($a_type)
10198  {
10199  if ($a_type == 1)
10200  {
10201  $this->mailnottype = 1;
10202  }
10203  else
10204  {
10205  $this->mailnottype = 0;
10206  }
10207  }
10208 
10209  public function getExportSettings()
10210  {
10211  if ($this->exportsettings)
10212  {
10213  return $this->exportsettings;
10214  }
10215  else
10216  {
10217  return 0;
10218  }
10219  }
10220 
10221  public function setExportSettings($a_settings)
10222  {
10223  if ($a_settings)
10224  {
10225  $this->exportsettings = $a_settings;
10226  }
10227  else
10228  {
10229  $this->exportsettings = 0;
10230  }
10231  }
10232 
10234  {
10235  if (($this->exportsettings & 1) > 0)
10236  {
10237  return true;
10238  }
10239  else
10240  {
10241  return false;
10242  }
10243  }
10244 
10245  public function setExportSettingsSingleChoiceShort($a_settings)
10246  {
10247  if ($a_settings)
10248  {
10249  $this->exportsettings = $this->exportsettings | 1;
10250  }
10251  else
10252  {
10254  {
10255  $this->exportsettings = $this->exportsettings ^ 1;
10256  }
10257  }
10258  }
10259 
10260  public function getQuestions()
10261  {
10262  return $this->questions;
10263  }
10264 } // END class.ilObjTest
10265 
10266 ?>