ILIAS  release_4-3 Revision
 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 "./Services/Object/classes/class.ilObject.php";
6 include_once "./Modules/Test/classes/inc.AssessmentConstants.php";
7 
18 class ilObjTest extends ilObject
19 {
27  protected $_kiosk;
28 
34  var $test_id;
35 
42 
43 
50  var $author;
51 
57  var $metadata;
58 
65 
73 
80 
89 
100 
109 
116 
126 
133 
141 
149 
159 
166 
173 
180 
187 
194 
201 
207  var $ects_fx;
208 
215 
224 
231 
239 
246 
253 
261 
268 
275 
282 
289 
296 
303 
310 
317 
324 
331 
338 
345 
352 
359 
366 
373 
379  private $_showinfo;
380 
386  private $_forcejs;
387 
393  private $_customStyle;
394 
395  protected $mailnotification;
396  protected $mailnottype;
397 
401  protected $random_questionpool_data = array();
402  protected $exportsettings;
403 
404  protected $poolUsage;
405 
406  private $template_id;
407 
413  private $online = null;
414 
419 
426 
432  private $obligationsEnabled = null;
433 
437 
438  protected $autosave;
439  protected $autosave_ival;
440 
447  private $passDeletionAllowed = null;
448 
455  function ilObjTest($a_id = 0,$a_call_by_reference = true)
456  {
457  global $ilUser;
458  $this->type = "tst";
459  include_once "./Modules/Test/classes/class.assMarkSchema.php";
460  $this->mark_schema = new ASS_MarkSchema();
461  $this->test_id = -1;
462  $this->author = $ilUser->fullname;
463  $this->introduction = "";
464  $this->questions = array();
465  $this->sequence_settings = TEST_FIXED_SEQUENCE;
466  $this->score_reporting = REPORT_AFTER_TEST;
467  $this->instant_verification = 0;
468  $this->answer_feedback_points = 0;
469  $this->reporting_date = "";
470  $this->nr_of_tries = 0;
471  $this->_kiosk = 0;
472  $this->use_previous_answers = 1;
473  $this->title_output = 0;
474  $this->starting_time = "";
475  $this->ending_time = "";
476  $this->processing_time = "00:00:00";
477  $this->enable_processing_time = "0";
478  $this->reset_processing_time = 0;
479  $this->ects_output = 0;
480  $this->ects_fx = NULL;
481  $this->random_test = 0;
482  $this->shuffle_questions = FALSE;
483  $this->mailnottype = 0;
484  $this->exportsettings = 0;
485  $this->show_summary = 8;
486  $this->random_question_count = "";
487  $this->count_system = COUNT_PARTIAL_SOLUTIONS;
488  $this->mc_scoring = SCORE_ZERO_POINTS_WHEN_UNANSWERED;
489  $this->score_cutting = SCORE_CUT_QUESTION;
490  $this->pass_scoring = SCORE_LAST_PASS;
491  $this->answer_feedback = 0;
492  $this->password = "";
493  $this->certificate_visibility = 0;
494  $this->allowedUsers = "";
495  $this->_showfinalstatement = FALSE;
496  $this->_finalstatement = "";
497  $this->_showinfo = TRUE;
498  $this->_forcejs = FALSE;
499  $this->_customStyle = "";
500  $this->allowedUsersTimeGap = "";
501  $this->anonymity = 0;
502  $this->show_cancel = 0;
503  $this->show_marker = 0;
504  $this->fixed_participants = 0;
505  $this->setShowPassDetails(TRUE);
506  $this->setShowSolutionDetails(TRUE);
507  $this->setShowSolutionAnswersOnly(FALSE);
508  $this->setShowSolutionSignature(FALSE);
509  $this->testSession = FALSE;
510  $this->testSequence = FALSE;
511  $this->mailnotification = 0;
512  $this->poolUsage = 1;
513  global $lng;
514  $lng->loadLanguageModule("assessment");
515  $this->mark_schema->createSimpleSchema($lng->txt("failed_short"), $lng->txt("failed_official"), 0, 0, $lng->txt("passed_short"), $lng->txt("passed_official"), 50, 1);
516  $this->ects_grades = array(
517  "A" => 90,
518  "B" => 65,
519  "C" => 35,
520  "D" => 10,
521  "E" => 0
522  );
523  $this->autosave = FALSE;
524  $this->autosave_ival = 30000;
525 
526  $this->express_mode = false;
527  $this->template_id = '';
528  $this->ilObject($a_id, $a_call_by_reference);
529  }
530 
534  function create($a_upload = false)
535  {
536  parent::create();
537 
538  // meta data will be created by
539  // import parser
540  if (!$a_upload)
541  {
542  $this->createMetaData();
543  }
544  }
545 
552  function update()
553  {
554  if (!parent::update())
555  {
556  return false;
557  }
558 
559  // put here object specific stuff
560 
561  return true;
562  }
563 
569  function read($a_force_db = false)
570  {
571  parent::read($a_force_db);
572  $this->loadFromDb();
573  }
574 
575 
582  function delete()
583  {
584  // always call parent delete function first!!
585  if (!parent::delete())
586  {
587  return false;
588  }
589 
590  // delet meta data
591  $this->deleteMetaData();
592 
593  //put here your module specific stuff
594  $this->deleteTest();
595 
596  return true;
597  }
598 
604  function deleteTest()
605  {
606  global $ilDB;
607 
608  // first of all remove all test editings, because the delete statements used for this
609  // contain a subquery for active ids, that are deleted in the next steps
610  $this->removeAllTestEditings();
611 
612  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s",
613  array('integer'),
614  array($this->getTestId())
615  );
616  $active_array = array();
617  while ($row = $ilDB->fetchAssoc($result))
618  {
619  array_push($active_array, $row["active_id"]);
620  }
621 
622  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
623  array('integer'),
624  array($this->getTestId())
625  );
626 
627  if (count($active_array))
628  {
629  foreach ($active_array as $active_id)
630  {
631  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_times WHERE active_fi = %s",
632  array('integer'),
633  array($active_id)
634  );
635 
636  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
637  array('integer'),
638  array($active_id)
639  );
640  }
641  }
642 
643  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_mark WHERE test_fi = %s",
644  array('integer'),
645  array($this->getTestId())
646  );
647 
648  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s",
649  array('integer'),
650  array($this->getTestId())
651  );
652  while ($row = $ilDB->fetchAssoc($result))
653  {
654  $this->removeQuestion($row["question_fi"]);
655  }
656 
657  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_tests WHERE test_id = %s",
658  array('integer'),
659  array($this->getTestId())
660  );
661 
662  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
663  array('integer'),
664  array($this->getTestId())
665  );
666 
667  // this delete is allready done by call to removeAllTestEditings some lines above
668  /*$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)",
669  array('integer'),
670  array($this->getTestId())
671  );*/
672 
673  // moved to top of this method because this method performs delete statements,
674  // that use a subquery for active ids, that are allready deleted some lines above
675  //$this->removeAllTestEditings();
676 
677  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
678  array('integer'),
679  array($this->getTestId())
680  );
681 
682  if ($this->isRandomTest())
683  {
685  }
686 
687 
688  // delete export files
689  include_once "./Services/Utilities/classes/class.ilUtil.php";
690  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
691  $directory = $tst_data_dir."/tst_".$this->getId();
692  if (is_dir($directory))
693  {
694  include_once "./Services/Utilities/classes/class.ilUtil.php";
695  ilUtil::delDir($directory);
696  }
697  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
698  $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId());
699  // remaining usages are not in text anymore -> delete them
700  // and media objects (note: delete method of ilObjMediaObject
701  // checks whether object is used in another context; if yes,
702  // the object is not deleted!)
703  foreach($mobs as $mob)
704  {
705  ilObjMediaObject::_removeUsage($mob, "tst:html", $this->getId());
706  if (ilObjMediaObject::_exists($mob))
707  {
708  $mob_obj =& new ilObjMediaObject($mob);
709  $mob_obj->delete();
710  }
711  }
712  }
713 
723  function initDefaultRoles()
724  {
725  global $rbacadmin;
726  return array();
727  }
728 
742  function notify($a_event,$a_ref_id,$a_parent_non_rbac_id,$a_node_id,$a_params = 0)
743  {
744  global $tree;
745 
746  switch ($a_event)
747  {
748  case "link":
749 
750  //var_dump("<pre>",$a_params,"</pre>");
751  //echo "Module name ".$this->getRefId()." triggered by link event. Objects linked into target object ref_id: ".$a_ref_id;
752  //exit;
753  break;
754 
755  case "cut":
756 
757  //echo "Module name ".$this->getRefId()." triggered by cut event. Objects are removed from target object ref_id: ".$a_ref_id;
758  //exit;
759  break;
760 
761  case "copy":
762 
763  //var_dump("<pre>",$a_params,"</pre>");
764  //echo "Module name ".$this->getRefId()." triggered by copy event. Objects are copied into target object ref_id: ".$a_ref_id;
765  //exit;
766  break;
767 
768  case "paste":
769 
770  //echo "Module name ".$this->getRefId()." triggered by paste (cut) event. Objects are pasted into target object ref_id: ".$a_ref_id;
771  //exit;
772  break;
773 
774  case "new":
775 
776  //echo "Module name ".$this->getRefId()." triggered by paste (new) event. Objects are applied to target object ref_id: ".$a_ref_id;
777  //exit;
778  break;
779  }
780 
781  // At the beginning of the recursive process it avoids second call of the notify function with the same parameter
782  if ($a_node_id==$_GET["ref_id"])
783  {
784  $parent_obj =& $this->ilias->obj_factory->getInstanceByRefId($a_node_id);
785  $parent_type = $parent_obj->getType();
786  if ($parent_type == $this->getType())
787  {
788  $a_node_id = (int) $tree->getParentId($a_node_id);
789  }
790  }
791 
792  parent::notify($a_event,$a_ref_id,$a_parent_non_rbac_id,$a_node_id,$a_params);
793  }
794 
801  {
802  include_once "./Services/Utilities/classes/class.ilUtil.php";
803  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
804  ilUtil::makeDir($tst_data_dir);
805  if (!is_writable($tst_data_dir))
806  {
807  $this->ilias->raiseError("Test Data Directory (".$tst_data_dir
808  .") not writeable.",$this->ilias->error_obj->MESSAGE);
809  }
810 
811  // create learning module directory (data_dir/lm_data/lm_<id>)
812  $tst_dir = $tst_data_dir."/tst_".$this->getId();
813  ilUtil::makeDir($tst_dir);
814  if (!@is_dir($tst_dir))
815  {
816  $this->ilias->raiseError("Creation of Test Directory failed.",$this->ilias->error_obj->MESSAGE);
817  }
818  // create Export subdirectory (data_dir/lm_data/lm_<id>/Export)
819  $export_dir = $tst_dir."/export";
820  ilUtil::makeDir($export_dir);
821  if (!@is_dir($export_dir))
822  {
823  $this->ilias->raiseError("Creation of Export Directory failed.",$this->ilias->error_obj->MESSAGE);
824  }
825  }
826 
833  {
834  include_once "./Services/Utilities/classes/class.ilUtil.php";
835  $export_dir = ilUtil::getDataDir()."/tst_data"."/tst_".$this->getId()."/export";
836  return $export_dir;
837  }
838 
845  function getExportFiles($dir)
846  {
847  // quit if import dir not available
848  if (!@is_dir($dir) or
849  !is_writeable($dir))
850  {
851  return array();
852  }
853 
854  // open directory
855  $dir = dir($dir);
856 
857  // initialize array
858  $file = array();
859 
860  // get files and save the in the array
861  while ($entry = $dir->read())
862  {
863  if ($entry != "." and
864  $entry != ".." and
865  //substr($entry, -4) == ".zip" and
866  ereg("^[0-9]{10}_{2}[0-9]+_{2}(tst(__results)?_)*[0-9]+\.[a-z]{1,3}\$", $entry))
867  {
868  $file[] = $entry;
869  }
870  }
871 
872  // close import directory
873  $dir->close();
874 
875  // sort files
876  sort ($file);
877  reset ($file);
878 
879  return $file;
880  }
881 
885  function _setImportDirectory($a_import_dir = null)
886  {
887  if (strlen($a_import_dir))
888  {
889  $_SESSION["tst_import_dir"] = $a_import_dir;
890  }
891  else
892  {
893  unset($_SESSION["tst_import_dir"]);
894  }
895  }
896 
904  {
905  if (strlen($_SESSION["tst_import_dir"]))
906  {
907  return $_SESSION["tst_import_dir"];
908  }
909  return null;
910  }
911 
913  {
915  }
916 
923  {
924  include_once "./Services/Utilities/classes/class.ilUtil.php";
925  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
926  ilUtil::makeDir($tst_data_dir);
927 
928  if (!is_writable($tst_data_dir))
929  {
930  $this->ilias->raiseError("Test Data Directory (".$tst_data_dir
931  .") not writeable.",$this->ilias->error_obj->FATAL);
932  }
933 
934  // create test directory (data_dir/tst_data/tst_import)
935  $tst_dir = $tst_data_dir."/tst_import";
936  ilUtil::makeDir($tst_dir);
937  if (!@is_dir($tst_dir))
938  {
939  $ilias->raiseError("Creation of test import directory failed.",$ilias->error_obj->FATAL);
940  }
941  return $tst_dir;
942  }
943 
951  {
952  global $ilDB;
953 
954  $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",
955  array('integer'),
956  array($this->getTestId())
957  );
958  $hasSC = false;
959  while ($row = $ilDB->fetchAssoc($result))
960  {
961  if (strcmp($row['foundtypes'], 'assSingleChoice') == 0)
962  {
963  $hasSC = true;
964  }
965  }
966  return $hasSC;
967  }
968 
976  {
977  global $ilDB;
978 
979  $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",
980  array('integer'),
981  array($this->getTestId())
982  );
983  if ($result->numRows() == 1)
984  {
985  $row = $ilDB->fetchAssoc($result);
986  if (strcmp($row['foundtypes'], 'assSingleChoice') == 0)
987  {
988  return TRUE;
989  }
990  else
991  {
992  return false;
993  }
994  }
995  return FALSE;
996  }
997 
1005  {
1006  global $ilDB;
1007 
1008  if (!$this->hasSingleChoiceQuestions()) return false;
1009 
1010  $result = $ilDB->queryF("
1011  SELECT DISTINCT(qpl_qst_sc.shuffle) foundshuffles
1012  FROM qpl_questions,
1013  qpl_qst_sc,
1014  tst_test_result,
1015  qpl_qst_type,
1016  tst_active
1017  WHERE tst_test_result.question_fi = qpl_questions.question_id
1018  AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id
1019  AND tst_test_result.active_fi = tst_active.active_id
1020  AND qpl_questions.question_id = qpl_qst_sc.question_fi
1021  AND tst_active.test_fi = %s
1022  AND qpl_qst_type.type_tag = %s
1023  ",
1024  array('integer', 'text'),
1025  array($this->getTestId(), 'assSingleChoice')
1026  );
1027  if ($result->numRows() == 1)
1028  {
1029  $row = $ilDB->fetchAssoc($result);
1030  return ($row['foundshuffles'] == 0);
1031  }
1032  return FALSE;
1033  }
1034 
1041  function isComplete()
1042  {
1043  if ((count($this->mark_schema->mark_steps)) and (count($this->questions)))
1044  {
1045  return 1;
1046  }
1047  else
1048  {
1049  if ($this->isRandomTest())
1050  {
1051  $arr = $this->getRandomQuestionpools();
1052  if (count($arr) && ($this->getRandomQuestionCount() > 0))
1053  {
1054  return 1;
1055  }
1056  $count = 0;
1057  foreach ($arr as $array)
1058  {
1059  $count += $array["count"];
1060  }
1061  if ($count)
1062  {
1063  return 1;
1064  }
1065  }
1066  return 0;
1067  }
1068  return 0;
1069  }
1070 
1077  function _isComplete($obj_id)
1078  {
1079  $test = new ilObjTest($obj_id, false);
1080  $test->loadFromDb();
1081  return $test->isComplete();
1082  }
1083 
1089  function saveECTSStatus($ects_output = 0, $fx_support = "", $ects_a = 90, $ects_b = 65, $ects_c = 35, $ects_d = 10, $ects_e = 0)
1090  {
1091  global $ilDB;
1092  if ($this->test_id > 0)
1093  {
1094  $fx_support = preg_replace("/,/", ".", $fx_support);
1095  if (preg_match("/\d+/", $fx_support))
1096  {
1097  $fx_support = $fx_support;
1098  }
1099  else
1100  {
1101  $fx_support = NULL;
1102  }
1103  $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",
1104  array('text','float','float','float','float','float','float','integer'),
1105  array($ects_output, $ects_a, $ects_b, $ects_c, $ects_d, $ects_e, $fx_support, $this->getTestId())
1106  );
1107  $this->ects_output = $ects_output;
1108  $this->ects_fx = $fx_support;
1109  }
1110  }
1111 
1118  {
1119  global $ilDB;
1120 
1121  $complete = 0;
1122  if ($this->isComplete())
1123  {
1124  $complete = 1;
1125  }
1126  if ($this->test_id > 0)
1127  {
1128  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET complete = %s WHERE test_id = %s",
1129  array('text','integer'),
1130  array($complete, $this->test_id)
1131  );
1132  }
1133  }
1134 
1140  function getAllRTEContent()
1141  {
1142  $result = array();
1143  array_push($result, $this->getIntroduction());
1144  array_push($result, $this->getFinalStatement());
1145  return $result;
1146  }
1147 
1154  {
1155  include_once("./Services/RTE/classes/class.ilRTE.php");
1156  $completecontent = "";
1157  foreach ($this->getAllRTEContent() as $content)
1158  {
1159  $completecontent .= $content;
1160  }
1161  ilRTE::_cleanupMediaObjectUsage($completecontent, $this->getType() . ":html",
1162  $this->getId());
1163  }
1164 
1172  function saveToDb($properties_only = FALSE)
1173  {
1174  global $ilDB, $ilLog;
1175 
1176  // moved online_status to ilObjectActivation (see below)
1177 
1178  // cleanup RTE images
1179  $this->cleanupMediaobjectUsage();
1180 
1181  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1182  if ($this->test_id == -1)
1183  {
1184  // Create new dataset
1185  $next_id = $ilDB->nextId('tst_tests');
1186 
1187  $ilDB->insert('tst_tests', array(
1188  'test_id' => array('integer', $next_id),
1189  'obj_fi' => array('integer', $this->getId()),
1190  'author' => array('text', $this->getAuthor()),
1191  'introduction' => array('text', ilRTE::_replaceMediaObjectImageSrc($this->getIntroduction(), 0)),
1192  'finalstatement' => array('text', ilRTE::_replaceMediaObjectImageSrc($this->getFinalStatement(), 0)),
1193  'showinfo' => array('integer', $this->getShowInfo()),
1194  'forcejs' => array('integer', $this->getForceJS()),
1195  'customstyle' => array('text', $this->getCustomStyle()),
1196  'showfinalstatement' => array('integer', $this->getShowFinalStatement()),
1197  'sequence_settings' => array('integer', $this->getSequenceSettings()),
1198  'score_reporting' => array('integer', $this->getScoreReporting()),
1199  'instant_verification' => array('text', $this->getInstantFeedbackSolution()),
1200  'answer_feedback_points' => array('text', $this->getAnswerFeedbackPoints()),
1201  'answer_feedback' => array('text', $this->getAnswerFeedback()),
1202  'anonymity' => array('text', $this->getAnonymity()),
1203  'show_cancel' => array('text', $this->getShowCancel()),
1204  'show_marker' => array('integer', $this->getShowMarker()),
1205  'fixed_participants' => array('text', $this->getFixedParticipants()),
1206  'nr_of_tries' => array('integer', $this->getNrOfTries()),
1207  'kiosk' => array('integer', $this->getKiosk()),
1208  'use_previous_answers' => array('text', $this->getUsePreviousAnswers()),
1209  'title_output' => array('text', $this->getTitleOutput()),
1210  'processing_time' => array('text', $this->getProcessingTime()),
1211  'enable_processing_time' => array('text', $this->getEnableProcessingTime()),
1212  'reset_processing_time' => array('integer', $this->getResetProcessingTime()),
1213  'reporting_date' => array('text', $this->getReportingDate()),
1214  'starting_time' => array('text', $this->getStartingTime()),
1215  'ending_time' => array('text', $this->getEndingTime()),
1216  'complete' => array('text', $this->isComplete()),
1217  'ects_output' => array('text', $this->getECTSOutput()),
1218  'ects_a' => array('float', strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : NULL),
1219  'ects_b' => array('float', strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : NULL),
1220  'ects_c' => array('float', strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : NULL),
1221  'ects_d' => array('float', strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : NULL),
1222  'ects_e' => array('float', strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : NULL),
1223  'ects_fx' => array('float', $this->getECTSFX()),
1224  'random_test' => array('text', $this->isRandomTest()),
1225  'random_question_count' => array('integer', $this->getRandomQuestionCount()),
1226  'count_system' => array('text', $this->getCountSystem()),
1227  'mc_scoring' => array('text', $this->getMCScoring()),
1228  'score_cutting' => array('text', $this->getScoreCutting()),
1229  'pass_scoring' => array('text', $this->getPassScoring()),
1230  'shuffle_questions' => array('text', $this->getShuffleQuestions()),
1231  'results_presentation' => array('integer', $this->getResultsPresentation()),
1232  'show_summary' => array('integer', $this->getListOfQuestionsSettings()),
1233  'password' => array('text', $this->getPassword()),
1234  'allowedusers' => array('integer', $this->getAllowedUsers()),
1235  'mailnottype' => array('integer', $this->getMailNotificationType()),
1236  'exportsettings' => array('integer', $this->getExportSettings()),
1237  'alloweduserstimegap' => array('integer', $this->getAllowedUsersTimeGap()),
1238  'certificate_visibility' => array('text', $this->getCertificateVisibility()),
1239  'mailnotification' => array('integer', $this->getMailNotification()),
1240  'created' => array('integer', time()),
1241  'tstamp' => array('integer', time()),
1242  'enabled_view_mode' => array('text', $this->getEnabledViewMode()),
1243  'template_id' => array('integer', $this->getTemplate()),
1244  'pool_usage' => array('integer', $this->getPoolUsage()),
1245  'print_bs_with_res' => array('integer', (int) $this->isBestSolutionPrintedWithResult()),
1246  'obligations_enabled' => array('integer', (int) $this->areObligationsEnabled()),
1247  'offer_question_hints' => array('integer', (int) $this->isOfferingQuestionHintsEnabled()),
1248  'highscore_enabled' => array('integer', (int) $this->getHighscoreEnabled()),
1249  'highscore_anon' => array('integer', (int) $this->getHighscoreAnon()),
1250  'highscore_achieved_ts' => array('integer', (int) $this->getHighscoreAchievedTS()),
1251  'highscore_score' => array('integer', (int) $this->getHighscoreScore()),
1252  'highscore_percentage' => array('integer', (int) $this->getHighscorePercentage()),
1253  'highscore_hints' => array('integer', (int) $this->getHighscoreHints()),
1254  'highscore_wtime' => array('integer', (int) $this->getHighscoreWTime()),
1255  'highscore_own_table' => array('integer', (int) $this->getHighscoreOwnTable()),
1256  'highscore_top_table' => array('integer', (int) $this->getHighscoreTopTable()),
1257  'highscore_top_num' => array('integer', (int) $this->getHighscoreTopNum()),
1258  'online_status' => array('integer', (int) $this->isOnline()),
1259  'specific_feedback' => array('integer', (int)$this->getSpecificAnswerFeedback()),
1260  'autosave' => array('integer', (int)$this->getAutosave()),
1261  'autosave_ival' => array('integer', (int)$this->getAutosaveIval()),
1262  'pass_deletion_allowed' => array('integer', (int)$this->isPassDeletionAllowed())
1263  ));
1264 
1265  $this->test_id = $next_id;
1266 
1268  {
1269  $this->logAction($this->lng->txtlng("assessment", "log_create_new_test", ilObjAssessmentFolder::_getLogLanguage()));
1270  }
1271  }
1272  else
1273  {
1274  // Modify existing dataset
1275  $oldrow = array();
1277  {
1278  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
1279  array('integer'),
1280  array($this->test_id)
1281  );
1282  if ($result->numRows() == 1)
1283  {
1284  $oldrow = $ilDB->fetchAssoc($result);
1285  }
1286  }
1287 
1288  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET author = %s, introduction = %s, " .
1289  "finalstatement = %s, showinfo = %s, forcejs = %s, customstyle = %s, showfinalstatement = %s, sequence_settings = %s, " .
1290  "score_reporting = %s, instant_verification = %s, answer_feedback_points = %s, answer_feedback = %s, anonymity = %s, show_cancel = %s, show_marker = %s, " .
1291  "fixed_participants = %s, nr_of_tries = %s, kiosk = %s, use_previous_answers = %s, title_output = %s, processing_time = %s, enable_processing_time = %s, " .
1292  "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, " .
1293  "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, " .
1294  "shuffle_questions = %s, results_presentation = %s, show_summary = %s, password = %s, allowedusers = %s, mailnottype = %s, exportsettings = %s, " .
1295  "print_bs_with_res = %s,".
1296  "alloweduserstimegap = %s, certificate_visibility = %s, mailnotification = %s, tstamp = %s, enabled_view_mode = %s, template_id = %s, pool_usage = %s, " .
1297  "offer_question_hints = %s, highscore_enabled = %s, highscore_anon = %s, highscore_achieved_ts = %s, " .
1298  "highscore_score = %s, highscore_percentage = %s, ".
1299  "highscore_hints = %s, highscore_wtime = %s, highscore_own_table = %s, highscore_top_table = %s, highscore_top_num = %s, " .
1300  "online_status = %s, specific_feedback = %s, obligations_enabled = %s, autosave = %s, autosave_ival = %s, pass_deletion_allowed = %s ".
1301  "WHERE test_id = %s",
1302  array(
1303  'text', 'text',
1304  'text', 'integer', 'integer', 'text', 'integer', 'integer',
1305  'integer', 'text', 'text', 'text', 'text', 'text', 'integer',
1306  'text', 'integer', 'integer', 'text', 'text', 'text', 'text',
1307  'integer', 'text', 'text', 'text', 'text', 'text', 'float', 'float', 'float', 'float',
1308  'float', 'float', 'text', 'integer', 'text', 'text', 'text', 'text',
1309  'text', 'integer', 'integer', 'text', 'integer','integer', 'integer',
1310  'integer',
1311  'integer', 'text', 'integer', 'integer', 'text', 'text', 'integer',
1312  'integer', 'integer', 'integer', 'integer',
1313  'integer', 'integer',
1314  'integer', 'integer', 'integer', 'integer', 'integer',
1315  'integer', 'integer','integer', 'integer', 'integer', 'integer',
1316  'integer'
1317  ),
1318  array(
1319  $this->getAuthor(),
1322  $this->getShowInfo(),
1323  $this->getForceJS(),
1324  $this->getCustomStyle(),
1325  $this->getShowFinalStatement(),
1326  $this->getSequenceSettings(),
1327  $this->getScoreReporting(),
1328  $this->getInstantFeedbackSolution(),
1329  $this->getAnswerFeedbackPoints(),
1330  $this->getGenericAnswerFeedback(),
1331  $this->getAnonymity(),
1332  $this->getShowCancel(),
1333  $this->getShowMarker(),
1334  $this->getFixedParticipants(),
1335  $this->getNrOfTries(),
1336  $this->getKiosk(),
1337  $this->getUsePreviousAnswers(),
1338  $this->getTitleOutput(),
1339  $this->getProcessingTime(),
1340  $this->getEnableProcessingTime(),
1341  $this->getResetProcessingTime(),
1342  $this->getReportingDate(),
1343  $this->getStartingTime(),
1344  $this->getEndingTime(),
1345  $this->isComplete(),
1346  $this->getECTSOutput(),
1347  strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : NULL,
1348  strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : NULL,
1349  strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : NULL,
1350  strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : NULL,
1351  strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : NULL,
1352  $this->getECTSFX(),
1353  $this->isRandomTest(),
1354  $this->getRandomQuestionCount(),
1355  $this->getCountSystem(),
1356  $this->getMCScoring(),
1357  $this->getScoreCutting(),
1358  $this->getPassScoring(),
1359  $this->getShuffleQuestions(),
1360  $this->getResultsPresentation(),
1361  $this->getListOfQuestionsSettings(),
1362  $this->getPassword(),
1363  $this->getAllowedUsers(),
1364  $this->getMailNotificationType(),
1365  $this->getExportSettings(),
1366  (int) $this->isBestSolutionPrintedWithResult(),
1367  $this->getAllowedUsersTimeGap(),
1368  $this->getCertificateVisibility(),
1369  $this->getMailNotification(),
1370  time(),
1371  $this->getEnabledViewMode(),
1372  $this->getTemplate(),
1373  $this->getPoolUsage(),
1374  (int)$this->isOfferingQuestionHintsEnabled(),
1375  (int) $this->getHighscoreEnabled(),
1376  (int) $this->getHighscoreAnon(),
1377  (int) $this->getHighscoreAchievedTS(),
1378  (int) $this->getHighscoreScore(),
1379  (int) $this->getHighscorePercentage(),
1380  (int) $this->getHighscoreHints(),
1381  (int) $this->getHighscoreWTime(),
1382  (int) $this->getHighscoreOwnTable(),
1383  (int) $this->getHighscoreTopTable(),
1384  (int) $this->getHighscoreTopNum(),
1385  (int) $this->isOnline(),
1386  (int) $this->getSpecificAnswerFeedback(),
1387  (int)$this->areObligationsEnabled(),
1388  $this->getAutosave(),
1389  $this->getAutosaveIval(),
1390  (int)$this->isPassDeletionAllowed(),
1391  $this->getTestId()
1392  )
1393  );
1394 
1395  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1397  {
1398  $logresult = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
1399  array('integer'),
1400  array($this->getTestId())
1401  );
1402  $newrow = array();
1403  if ($logresult->numRows() == 1)
1404  {
1405  $newrow = $ilDB->fetchAssoc($logresult);
1406  }
1407  $changed_fields = array();
1408  foreach ($oldrow as $key => $value)
1409  {
1410  if (strcmp($oldrow[$key], $newrow[$key]) != 0)
1411  {
1412  array_push($changed_fields, "$key: " . $oldrow[$key] . " => " . $newrow[$key]);
1413  }
1414  }
1415  $changes = join($changed_fields, ", ");
1416  if (count($changed_fields) > 0)
1417  {
1418  $this->logAction($this->lng->txtlng("assessment", "log_modified_test", ilObjAssessmentFolder::_getLogLanguage()) . " [".$changes."]");
1419  }
1420  }
1421  if ($this->evalTotalPersons() > 0)
1422  {
1423  // reset the finished status of participants if the nr of test passes did change
1424  if ($this->getNrOfTries() > 0)
1425  {
1426  // set all unfinished tests with nr of passes >= allowed passes finished
1427  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND tries >= %s AND submitted = %s",
1428  array('integer', 'integer', 'integer'),
1429  array($this->getTestId(), $this->getNrOfTries(), 0)
1430  );
1431  while ($row = $ilDB->fetchAssoc($aresult))
1432  {
1433  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1434  array('integer', 'timestamp', 'integer'),
1435  array(1, date('Y-m-d H:i:s'), $row["active_id"])
1436  );
1437  }
1438 
1439  // set all finished tests with nr of passes < allowed passes not finished
1440  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND tries < %s AND submitted = %s",
1441  array('integer', 'integer', 'integer'),
1442  array($this->getTestId(), $this->getNrOfTries()-1, 1)
1443  );
1444  while ($row = $ilDB->fetchAssoc($aresult))
1445  {
1446  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1447  array('integer', 'timestamp', 'integer'),
1448  array(0, NULL, $row["active_id"])
1449  );
1450  }
1451  }
1452  else
1453  {
1454  // set all finished tests with nr of passes >= allowed passes not finished
1455  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND submitted = %s",
1456  array('integer', 'integer'),
1457  array($this->getTestId(), 1)
1458  );
1459  while ($row = $ilDB->fetchAssoc($aresult))
1460  {
1461  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1462  array('integer', 'timestamp', 'integer'),
1463  array(0, NULL, $row["active_id"])
1464  );
1465  }
1466  }
1467  }
1468  }
1469 
1470  // moved activation to ilObjectActivation
1471  if($this->ref_id)
1472  {
1473  include_once "./Services/Object/classes/class.ilObjectActivation.php";
1474  ilObjectActivation::getItem($this->ref_id);
1475 
1476  $item = new ilObjectActivation;
1477  if(!$this->isActivationLimited())
1478  {
1480  }
1481  else
1482  {
1483  $item->setTimingType(ilObjectActivation::TIMINGS_ACTIVATION);
1484  $item->setTimingStart($this->getActivationStartingTime());
1485  $item->setTimingEnd($this->getActivationEndingTime());
1486  $item->toggleVisible($this->getActivationVisibility());
1487  }
1488 
1489  $item->update($this->ref_id);
1490  }
1491 
1492  if (!$this->isRandomTest())
1493  {
1495  }
1496  if (!$properties_only)
1497  {
1498  if (PEAR::isError($result))
1499  {
1500  global $ilias;
1501  $ilias->raiseError($result->getMessage());
1502  }
1503  else
1504  {
1505  if (!$this->isRandomTest())
1506  {
1507  $this->saveQuestionsToDb();
1508  }
1509  $this->mark_schema->saveToDb($this->test_id);
1510  }
1511  }
1512  }
1513 
1521  {
1522  global $ilDB;
1523 
1524  $oldquestions = array();
1525  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
1527  {
1528  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence",
1529  array('integer'),
1530  array($this->getTestId())
1531  );
1532  if ($result->numRows() > 0)
1533  {
1534  while ($row = $ilDB->fetchAssoc($result))
1535  {
1536  array_push($oldquestions, $row["question_fi"]);
1537  }
1538  }
1539  }
1540  // workaround for lost obligations
1541  // this method is called if a question is removed
1542  $currentQuestionsObligationsQuery = 'SELECT question_fi, obligatory FROM tst_test_question WHERE test_fi = %s';
1543  $rset = $ilDB->queryF($currentQuestionsObligationsQuery, array('integer'), array($this->getTestId()));
1544  while ($row = $ilDB->fetchAssoc($rset)) {
1545  $obligatoryQuestionState[$row['question_fi']] = $row['obligatory'];
1546  }
1547  // delete existing category relations
1548  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
1549  array('integer'),
1550  array($this->getTestId())
1551  );
1552  // create new category relations
1553  foreach ($this->questions as $key => $value)
1554  {
1555  // workaround for import witout obligations information
1556  if( !isset($obligatoryQuestionState[$value]) || is_null($obligatoryQuestionState[$value]) )
1557  {
1558  $obligatoryQuestionState[$value] = 0;
1559  }
1560 
1561  // insert question
1562  $next_id = $ilDB->nextId('tst_test_question');
1563  $ilDB->insert('tst_test_question', array(
1564  'test_question_id' => array('integer', $next_id),
1565  'test_fi' => array('integer', $this->getTestId()),
1566  'question_fi' => array('integer', $value),
1567  'sequence' => array('integer', $key),
1568  'obligatory' => array('integer', $obligatoryQuestionState[$value]),
1569  'tstamp' => array('integer', time())
1570  ));
1571  }
1572  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1574  {
1575  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence",
1576  array('integer'),
1577  array($this->getTestId())
1578  );
1579  $newquestions = array();
1580  if ($result->numRows() > 0)
1581  {
1582  while ($row = $ilDB->fetchAssoc($result))
1583  {
1584  array_push($newquestions, $row["question_fi"]);
1585  }
1586  }
1587  foreach ($oldquestions as $index => $question_id)
1588  {
1589  if (strcmp($newquestions[$index], $question_id) != 0)
1590  {
1591  $pos = array_search($question_id, $newquestions);
1592  if ($pos === FALSE)
1593  {
1594  $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id);
1595  }
1596  else
1597  {
1598  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index+1) . " => " . ($pos+1), $question_id);
1599  }
1600  }
1601  }
1602  foreach ($newquestions as $index => $question_id)
1603  {
1604  if (array_search($question_id, $oldquestions) === FALSE)
1605  {
1606  $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index+1), $question_id);
1607  }
1608  }
1609  }
1610  }
1611 
1615  protected function isNewRandomTest()
1616  {
1617  global $ilDB;
1618  $result = $ilDB->queryF('SELECT copy_id FROM tst_rnd_cpy WHERE tst_fi = %s',
1619  array('integer'),
1620  array($this->getTestId())
1621  );
1622  return $result->numRows() > 0;
1623  }
1624 
1631  function saveRandomQuestion($active_id, $question_id, $pass = NULL, $maxcount)
1632  {
1633  global $ilUser;
1634  global $ilDB;
1635 
1636  if (is_null($pass)) $pass = 0;
1637  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s",
1638  array('integer','integer'),
1639  array($active_id, $pass)
1640  );
1641  if ($result->numRows() < $maxcount)
1642  {
1643  $duplicate_id = $question_id;
1644  if (!$this->isNewRandomTest())
1645  {
1646  $duplicate_id = $this->getRandomQuestionDuplicate($question_id, $active_id);
1647  if ($duplicate_id === FALSE)
1648  {
1649  $duplicate_id = $this->duplicateQuestionForTest($question_id);
1650  }
1651  }
1652  $next_id = $ilDB->nextId('tst_test_rnd_qst');
1653  $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)",
1654  array('integer','integer','integer','integer','integer','integer'),
1655  array($next_id,$active_id, $duplicate_id, $result->numRows()+1, $pass, time())
1656  );
1657  }
1658  }
1659 
1669  function getRandomQuestionDuplicate($question_id, $active_id)
1670  {
1671  global $ilDB;
1672 
1673  $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",
1674  array('integer', 'integer'),
1675  array($question_id, $active_id)
1676  );
1677  $num = $result->numRows();
1678  if ($num > 0)
1679  {
1680  $row = $ilDB->fetchAssoc($result);
1681  return $row["question_id"];
1682  }
1683  else
1684  {
1685  return FALSE;
1686  }
1687  }
1688 
1694  function getNrOfResultsForPass($active_id, $pass)
1695  {
1696  global $ilDB;
1697 
1698  $result = $ilDB->queryF("SELECT test_result_id FROM tst_test_result WHERE active_fi = %s AND pass = %s",
1699  array('integer','integer'),
1700  array($active_id, $pass)
1701  );
1702  return $result->numRows();
1703  }
1704 
1713  function hasRandomQuestionsForPass($active_id, $pass)
1714  {
1715  global $ilDB;
1716  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s",
1717  array('integer','integer'),
1718  array($active_id, $pass)
1719  );
1720  return ($result->numRows() > 0) ? true : false;
1721  }
1722 
1729  function generateRandomQuestions($active_id, $pass = NULL)
1730  {
1731  global $ilUser;
1732  global $ilDB;
1733 
1734  if ($active_id > 0)
1735  {
1736  if ($this->hasRandomQuestionsForPass($active_id, $pass) > 0)
1737  {
1738  // Something went wrong. Maybe the user pressed the start button twice
1739  // Questions already exist so there is no need to create new questions
1740  return;
1741  }
1742  if (false && $pass > 0) // fixed mantis #9221
1743  {
1744  if ($this->getNrOfResultsForPass($active_id, $pass - 1) == 0)
1745  {
1746  // This means that someone maybe reloaded the test submission page
1747  // If there are no existing results for the previous test, it makes
1748  // no sense to create a new set of random questions
1749  return;
1750  }
1751  }
1752  }
1753  else
1754  {
1755  // This may not happen! If it happens, raise a fatal error...
1756  global $ilias, $ilErr;
1757  $ilias->raiseError(sprintf($this->lng->txt("error_random_question_generation"), $ilUser->getId(), $this->getTestId()), $ilErr->FATAL);
1758  }
1759 
1760  $num = $this->getRandomQuestionCount();
1761  if ($num > 0)
1762  {
1763  $qpls =& $this->getRandomQuestionpools();
1764  $rndquestions = $this->generateRandomPass($num, $qpls, $pass);
1765  $allquestions = array();
1766  foreach ($rndquestions as $question_id)
1767  {
1768  array_push($allquestions, $question_id);
1769  }
1770  if ($this->getShuffleQuestions())
1771  {
1772  srand ((float)microtime()*1000000);
1773  shuffle($allquestions);
1774  }
1775 
1776  $maxcount = 0;
1777  foreach ($qpls as $data)
1778  {
1779  $maxcount += $data["contains"];
1780  }
1781  if ($num > $maxcount) $num = $maxcount;
1782  foreach ($allquestions as $question_id)
1783  {
1784  $this->saveRandomQuestion($active_id, $question_id, $pass, $num);
1785  }
1786  }
1787  else
1788  {
1789  $qpls =& $this->getRandomQuestionpools();
1790  $allquestions = array();
1791  $maxcount = 0;
1792  foreach ($qpls as $key => $value)
1793  {
1794  if ($value["count"] > 0)
1795  {
1796  $rndquestions = $this->generateRandomPass($value["count"], array($value), $pass);
1797  foreach ($rndquestions as $question_id)
1798  {
1799  array_push($allquestions, $question_id);
1800  }
1801  }
1802  $add = ($value["count"] <= $value["contains"]) ? $value["count"] : $value["contains"];
1803  $maxcount += $add;
1804  }
1805  if ($this->getShuffleQuestions())
1806  {
1807  srand ((float)microtime()*1000000);
1808  shuffle($allquestions);
1809  }
1810  foreach ($allquestions as $question_id)
1811  {
1812  $this->saveRandomQuestion($active_id, $question_id, $pass, $maxcount);
1813  }
1814  }
1815  }
1816 
1823  function saveRandomQuestionCount($total_questions = NULL)
1824  {
1825  global $ilDB;
1826 
1827  if (strlen($total_questions))
1828  {
1829  $this->setRandomQuestionCount($total_questions);
1830  }
1831  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET random_question_count = %s, tstamp = %s WHERE test_id = %s",
1832  array('integer', 'integer', 'integer'),
1833  array($this->getRandomQuestionCount(), time(), $this->getTestId())
1834  );
1835  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1837  {
1838  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_total_amount_of_questions", ilObjAssessmentFolder::_getLogLanguage()), $this->getRandomQuestionCount()));
1839  }
1840  }
1841 
1850  {
1851  global $ilDB;
1852 
1853  $qpls = array();
1854  $counter = 0;
1855  $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",
1856  array("integer"),
1857  array($this->getTestId())
1858  );
1859  if ($result->numRows())
1860  {
1861  while ($row = $ilDB->fetchAssoc($result))
1862  {
1863  $qpls[$counter] = array(
1864  "index" => $counter,
1865  "count" => $row["num_of_q"],
1866  "qpl" => $row["questionpool_fi"],
1867  "contains" => $row["questioncount"]
1868  );
1869  $counter++;
1870  }
1871  }
1872  return $qpls;
1873  }
1874 
1878  public function saveRandomQuestionpools()
1879  {
1880  global $ilDB;
1881 
1882  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1883  // delete existing random questionpools
1884  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
1885  array('integer'),
1886  array($this->getTestId())
1887  );
1889  {
1890  $this->logAction($this->lng->txtlng("assessment", "log_random_question_pool_deleted", ilObjAssessmentFolder::_getLogLanguage()));
1891  }
1892  // delete existing duplicated questions
1894 
1895  // create new random questionpools
1896  foreach ($this->random_questionpool_data as $idx => $data)
1897  {
1898  if ($data->qpl > 0)
1899  {
1900  // save questionpool information
1901  $next_id = $ilDB->nextId('tst_test_random');
1902  $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)",
1903  array('integer','integer', 'integer', 'integer', 'integer', 'integer'),
1904  array($next_id, $this->getTestId(), $data->qpl, $data->count, time(), $idx)
1905  );
1907  {
1908  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_random_question_pool_added", ilObjAssessmentFolder::_getLogLanguage()), $value["title"] . " (" . $value["qpl"] . ")", $value["count"]));
1909  }
1910  // duplicate all questions of the questionpools
1911  $this->duplicateQuestionpoolForTest($data->qpl);
1912  }
1913  }
1914  }
1915 
1919  protected function removeDuplicatedQuestionpools()
1920  {
1921  global $ilDB;
1922 
1923  $result = $ilDB->queryF('SELECT * FROM tst_rnd_cpy WHERE tst_fi = %s',
1924  array('integer'),
1925  array($this->getTestId())
1926  );
1927  while ($row = $ilDB->fetchAssoc($result))
1928  {
1929  $question =& ilObjTest::_instanciateQuestion($row['qst_fi']);
1930  $question->delete($row['qst_fi']);
1931  }
1932 
1933  $affectedRows = $ilDB->manipulateF('DELETE FROM tst_rnd_cpy WHERE tst_fi = %s',
1934  array('integer'),
1935  array($this->getTestId())
1936  );
1937 
1938  $affectedRows = $ilDB->manipulateF('DELETE FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1939  array('integer'),
1940  array($this->getTestId())
1941  );
1942  }
1943 
1947  public function getUsedRandomQuestionpools()
1948  {
1949  global $ilDB;
1950  if ($this->isNewRandomTest())
1951  {
1952  $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',
1953  array('integer'),
1954  array($this->getTestId())
1955  );
1956  $pools = array();
1957  while ($row = $ilDB->fetchAssoc($result))
1958  {
1959  if (is_array($pools[$row['qpl_fi']]))
1960  {
1961  $pools[$row['qpl_fi']]['count']++;
1962  }
1963  else
1964  {
1965  $pools[$row['qpl_fi']]['count'] = 1;
1966  }
1967  $pools[$row['qpl_fi']]['num_of_q'] = $row['num_of_q'];
1968  }
1969  $result = $ilDB->queryF('SELECT * FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1970  array('integer'),
1971  array($this->getTestId())
1972  );
1973 
1974  while ($row = $ilDB->fetchAssoc($result))
1975  {
1976  $pools[$row['qpl_fi']]['title'] = $row['qpl_title'];
1977  }
1978  return $pools;
1979  }
1980  else
1981  {
1982  $result = $ilDB->queryF('SELECT tst_test_random.* FROM tst_test_random WHERE tst_test_random.test_fi = %s ORDER BY sequence, test_random_id',
1983  array('integer'),
1984  array($this->getTestId())
1985  );
1986  $pools = array();
1987  while ($row = $ilDB->fetchAssoc($result))
1988  {
1989  $pools[$row['questionpool_fi']]['count'] = $row['num_of_q'];
1990  $pools[$row['questionpool_fi']]['num_of_q'] = $row['num_of_q'];
1991  }
1992  $result = $ilDB->queryF('SELECT * FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1993  array('integer'),
1994  array($this->getTestId())
1995  );
1996 
1997  while ($row = $ilDB->fetchAssoc($result))
1998  {
1999  $pools[$row['qpl_fi']]['title'] = $row['qpl_title'];
2000  }
2001  return $pools;
2002  }
2003  }
2004 
2008  protected function duplicateQuestionpoolForTest($questionpool_id)
2009  {
2010  global $ilDB;
2011  $result = $ilDB->queryF('SELECT question_id FROM qpl_questions WHERE obj_fi = %s AND complete = %s AND original_id IS NULL',
2012  array('integer', 'text'),
2013  array($questionpool_id, 1)
2014  );
2015  $saved_titles = array();
2016  while ($row = $ilDB->fetchAssoc($result))
2017  {
2018  $question =& ilObjTest::_instanciateQuestion($row['question_id']);
2019  $duplicate_id = $question->duplicate(true, null, null, null, $this->getId());
2020  if ($duplicate_id > 0)
2021  {
2022  $next_id = $ilDB->nextId('tst_rnd_cpy');
2023  $ilDB->manipulateF('INSERT INTO tst_rnd_cpy (copy_id, tst_fi, qst_fi, qpl_fi) VALUES (%s, %s, %s, %s)',
2024  array('integer', 'integer', 'integer', 'integer'),
2025  array($next_id, $this->getTestId(), $duplicate_id, $questionpool_id)
2026  );
2027  if (!array_key_exists($questionpool_id, $saved_titles))
2028  {
2029  $next_id = $ilDB->nextId('tst_rnd_qpl_title');
2030  $ilDB->manipulateF('INSERT INTO tst_rnd_qpl_title (title_id, tst_fi, qpl_fi, qpl_title) VALUES (%s, %s, %s, %s)',
2031  array('integer', 'integer', 'integer', 'text'),
2032  array($next_id, $this->getTestId(), $questionpool_id, ilObject::_lookupTitle($questionpool_id))
2033  );
2034  $saved_titles[$questionpool_id] = 1;
2035  }
2036  }
2037  }
2038  }
2039 
2040  function addRandomQuestionpoolData($count = 0, $qpl = 0, $position)
2041  {
2042  include_once "./Modules/Test/classes/class.ilRandomTestData.php";
2043  if (array_key_exists($position, $this->random_questionpool_data))
2044  {
2045  $newitems = array();
2046  for ($i = 0; $i < $position; $i++)
2047  {
2048  array_push($newitems, $this->random_questionpool_data[$i]);
2049  }
2050  array_push($newitems, new ilRandomTestData($count, $qpl));
2051  for ($i = $position; $i < count($this->random_questionpool_data); $i++)
2052  {
2053  array_push($newitems, $this->random_questionpool_data[$i]);
2054  }
2055  $this->random_questionpool_data = $newitems;
2056  }
2057  else
2058  {
2059  array_push($this->random_questionpool_data, new ilRandomTestData($count, $qpl));
2060  }
2061  }
2062 
2063  function removeRandomQuestionpoolData($position)
2064  {
2065  if (array_key_exists($position, $this->random_questionpool_data))
2066  {
2067  unset($this->random_questionpool_data[$position]);
2068  }
2069  }
2070 
2071  function setRandomQuestionpoolData($a_data)
2072  {
2073  $this->random_questionpool_data = $a_data;
2074  }
2075 
2084  {
2085  if (is_array($this->random_questionpool_data) && count($this->random_questionpool_data)) return $this->random_questionpool_data;
2086 
2087  global $ilDB;
2088 
2089  $qpls = array();
2090  $counter = 0;
2091  $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",
2092  array("integer"),
2093  array($this->getTestId())
2094  );
2095  include_once "./Modules/Test/classes/class.ilRandomTestData.php";
2096  if ($result->numRows())
2097  {
2098  while ($row = $ilDB->fetchAssoc($result))
2099  {
2100  array_push($qpls, new ilRandomTestData($row['num_of_q'], $row['questionpool_fi']));
2101  }
2102  }
2103  return $qpls;
2104  }
2105 
2113  function loadFromDb()
2114  {
2115  global $ilDB;
2116 
2117  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE obj_fi = %s",
2118  array('integer'),
2119  array($this->getId())
2120  );
2121  if ($result->numRows() == 1)
2122  {
2123  $data = $ilDB->fetchObject($result);
2124  $this->setTestId($data->test_id);
2125  if (strlen($this->getAuthor()) == 0)
2126  {
2127  $this->saveAuthorToMetadata($data->author);
2128  }
2129  $this->setAuthor($data->author);
2130  include_once("./Services/RTE/classes/class.ilRTE.php");
2131  $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc($data->introduction, 1));
2132  $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc($data->finalstatement, 1));
2133  $this->setShowInfo($data->showinfo);
2134  $this->setForceJS($data->forcejs);
2135  $this->setCustomStyle($data->customstyle);
2136  $this->setShowFinalStatement($data->showfinalstatement);
2137  $this->setSequenceSettings($data->sequence_settings);
2138  $this->setScoreReporting($data->score_reporting);
2139  $this->setInstantFeedbackSolution($data->instant_verification);
2140  $this->setAnswerFeedbackPoints($data->answer_feedback_points);
2141  $this->setAnswerFeedback($data->answer_feedback);
2142  $this->setAnonymity($data->anonymity);
2143  $this->setShowCancel($data->show_cancel);
2144  $this->setShowMarker($data->show_marker);
2145  $this->setFixedParticipants($data->fixed_participants);
2146  $this->setNrOfTries($data->nr_of_tries);
2147  $this->setKiosk($data->kiosk);
2148  $this->setUsePreviousAnswers($data->use_previous_answers);
2149  $this->setTitleOutput($data->title_output);
2150  $this->setProcessingTime($data->processing_time);
2151  $this->setEnableProcessingTime($data->enable_processing_time);
2152  $this->setResetProcessingTime($data->reset_processing_time);
2153  $this->setReportingDate($data->reporting_date);
2154  $this->setStartingTime($data->starting_time);
2155  $this->setEndingTime($data->ending_time);
2156  $this->setShuffleQuestions($data->shuffle_questions);
2157  $this->setResultsPresentation($data->results_presentation);
2158  $this->setListOfQuestionsSettings($data->show_summary);
2159  $this->setECTSOutput($data->ects_output);
2160  $this->setECTSGrades(
2161  array(
2162  "A" => $data->ects_a,
2163  "B" => $data->ects_b,
2164  "C" => $data->ects_c,
2165  "D" => $data->ects_d,
2166  "E" => $data->ects_e
2167  )
2168  );
2169  $this->setECTSFX($data->ects_fx);
2170  $this->setRandomTest($data->random_test);
2171  $this->setRandomQuestionCount($data->random_question_count);
2172  $this->mark_schema->flush();
2173  $this->mark_schema->loadFromDb($this->getTestId());
2174  $this->setCountSystem($data->count_system);
2175  $this->setMCScoring($data->mc_scoring);
2176  $this->setMailNotification($data->mailnotification);
2177  $this->setMailNotificationType($data->mailnottype);
2178  $this->setExportSettings($data->exportsettings);
2179  $this->setScoreCutting($data->score_cutting);
2180  $this->setPassword($data->password);
2181  $this->setAllowedUsers($data->allowedusers);
2182  $this->setAllowedUsersTimeGap($data->alloweduserstimegap);
2183  $this->setPassScoring($data->pass_scoring);
2184  $this->setObligationsEnabled($data->obligations_enabled);
2185  $this->setOfferingQuestionHintsEnabled($data->offer_question_hints);
2186  $this->setCertificateVisibility($data->certificate_visibility);
2187  $this->setEnabledViewMode($data->enabled_view_mode);
2188  $this->setTemplate($data->template_id);
2189  $this->setPoolUsage($data->pool_usage);
2190  $this->setPrintBestSolutionWithResult((bool) $data->print_bs_with_res);
2191  $this->setHighscoreEnabled((bool) $data->highscore_enabled);
2192  $this->setHighscoreAnon((bool) $data->highscore_anon);
2193  $this->setHighscoreAchievedTS((bool) $data->highscore_achieved_ts);
2194  $this->setHighscoreScore((bool) $data->highscore_score);
2195  $this->setHighscorePercentage((bool) $data->highscore_percentage);
2196  $this->setHighscoreHints((bool) $data->highscore_hints);
2197  $this->setHighscoreWTime((bool) $data->highscore_wtime);
2198  $this->setHighscoreOwnTable((bool) $data->highscore_own_table);
2199  $this->setHighscoreTopTable((bool) $data->highscore_top_table);
2200  $this->setHighscoreTopNum((int) $data->highscore_top_num);
2201  $this->setOnline((bool) $data->online_status);
2202  $this->setSpecificAnswerFeedback((int) $data->specific_feedback);
2203  $this->setAutosave((bool)$data->autosave);
2204  $this->setAutosaveIval((int)$data->autosave_ival);
2205  $this->setPassDeletionAllowed($data->pass_deletion_allowed);
2206  $this->loadQuestions();
2207  }
2208 
2209  // moved activation to ilObjectActivation
2210  if($this->ref_id)
2211  {
2212  include_once "./Services/Object/classes/class.ilObjectActivation.php";
2213  $activation = ilObjectActivation::getItem($this->ref_id);
2214  switch($activation["timing_type"])
2215  {
2217  $this->setActivationLimited(true);
2218  $this->setActivationStartingTime($activation["timing_start"]);
2219  $this->setActivationEndingTime($activation["timing_end"]);
2220  $this->setActivationVisibility($activation["visible"]);
2221  break;
2222 
2223  default:
2224  $this->setActivationLimited(false);
2225  break;
2226  }
2227  }
2228  }
2229 
2236 function loadQuestions($active_id = "", $pass = NULL)
2237 {
2238  global $ilUser;
2239  global $ilDB;
2240 
2241  $this->questions = array();
2242  if (strcmp($active_id, "") == 0)
2243  {
2244  $active_id = $this->getActiveIdOfUser($ilUser->getId());
2245  }
2246  if ($this->isRandomTest())
2247  {
2248  if (is_null($pass))
2249  {
2250  $pass = $this->_getPass($active_id);
2251  }
2252  $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",
2253  array('integer', 'integer'),
2254  array($active_id, $pass)
2255  );
2256  // The following is a fix for random tests prior to ILIAS 3.8. If someone started a random test in ILIAS < 3.8, there
2257  // is only one test pass (pass = 0) in tst_test_rnd_qst while with ILIAS 3.8 there are questions for every test pass.
2258  // To prevent problems with tests started in an older version and continued in ILIAS 3.8, the first pass should be taken if
2259  // no questions are present for a newer pass.
2260  if ($result->numRows() == 0)
2261  {
2262  $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",
2263  array('integer'),
2264  array($active_id)
2265  );
2266  }
2267  }
2268  else
2269  {
2270  $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",
2271  array('integer'),
2272  array($this->test_id)
2273  );
2274  }
2275  $index = 1;
2276  while ($data = $ilDB->fetchAssoc($result))
2277  {
2278  $this->questions[$index++] = $data["question_fi"];
2279  }
2280 }
2281 
2290  {
2291  $this->introduction = $introduction;
2292  }
2293 
2301  public function setFinalStatement($a_statement = "")
2302  {
2303  $this->_finalstatement = $a_statement;
2304  }
2305 
2313  public function setShowInfo($a_info = 1)
2314  {
2315  $this->_showinfo = ($a_info) ? 1 : 0;
2316  }
2317 
2325  public function setForceJS($a_js = 1)
2326  {
2327  $this->_forcejs = ($a_js) ? 1 : 0;
2328  }
2329 
2337  public function setCustomStyle($a_customStyle = NULL)
2338  {
2339  $this->_customStyle = $a_customStyle;
2340  }
2341 
2349  public function getCustomStyle()
2350  {
2351  return (strlen($this->_customStyle)) ? $this->_customStyle : NULL;
2352  }
2353 
2361  public function getCustomStyles()
2362  {
2363  $css_path = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test");
2364  $css_path = str_replace("ta.css", "customstyles", $css_path) . "/";
2365  $customstyles = array();
2366  if (is_dir($css_path))
2367  {
2368  $results = array();
2369  include_once "./Services/Utilities/classes/class.ilFileUtils.php";
2371  if (is_array($results["file"]))
2372  {
2373  foreach ($results["file"] as $filename)
2374  {
2375  if (strpos($filename, ".css"))
2376  {
2377  array_push($customstyles, $filename);
2378  }
2379  }
2380  }
2381  }
2382  return $customstyles;
2383  }
2384 
2392  public function getTestStyleLocation($mode = "output")
2393  {
2394  if (strlen($this->getCustomStyle()))
2395  {
2396  $default = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test");
2397  $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $default);
2398  if (file_exists($custom))
2399  {
2400  $custom = ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2401  $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $custom);
2402  return $custom;
2403  }
2404  else
2405  {
2406  return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2407  }
2408  }
2409  else
2410  {
2411  return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2412  }
2413  }
2414 
2422  public function setShowFinalStatement($show = 0)
2423  {
2424  $this->_showfinalstatement = ($show) ? 1 : 0;
2425  }
2426 
2427 
2435  function isRandomTest()
2436  {
2437  return ($this->random_test) ? 1 : 0;
2438  }
2439 
2440 
2446  public static function _lookupRandomTest($a_obj_id)
2447  {
2448  global $ilDB;
2449 
2450  $query = "SELECT random_test FROM tst_tests ".
2451  "WHERE obj_fi = ".$ilDB->quote($a_obj_id,'integer');
2452  $res = $ilDB->query($query);
2453  while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
2454  {
2455  return $row->random_test ? true : false;
2456  }
2457  return false;
2458  }
2459 
2468  {
2469  return ($this->random_question_count) ? $this->random_question_count : 0;
2470  }
2471 
2478  public function getIntroduction()
2479  {
2480  return (strlen($this->introduction)) ? $this->introduction : NULL;
2481  }
2482 
2489  public function getFinalStatement()
2490  {
2491  return (strlen($this->_finalstatement)) ? $this->_finalstatement : NULL;
2492  }
2493 
2501  public function getShowInfo()
2502  {
2503  return ($this->_showinfo) ? 1 : 0;
2504  }
2505 
2513  public function getForceJS()
2514  {
2515  return ($this->_forcejs) ? 1 : 0;
2516  }
2517 
2525  public function getShowFinalStatement()
2526  {
2527  return ($this->_showfinalstatement) ? 1 : 0;
2528  }
2529 
2537  function getTestId()
2538  {
2539  return $this->test_id;
2540  }
2541 
2549  function getECTSOutput()
2550  {
2551  return ($this->ects_output) ? 1 : 0;
2552  }
2553 
2561  function setECTSOutput($a_ects_output)
2562  {
2563  $this->ects_output = $a_ects_output ? 1 : 0;
2564  }
2565 
2573  function getECTSFX()
2574  {
2575  return (strlen($this->ects_fx)) ? $this->ects_fx : NULL;
2576  }
2577 
2585  function setECTSFX($a_ects_fx)
2586  {
2587  $this->ects_fx = $a_ects_fx;
2588  }
2589 
2597  function &getECTSGrades()
2598  {
2599  return $this->ects_grades;
2600  }
2601 
2609  function setECTSGrades($a_ects_grades)
2610  {
2611  if (is_array($a_ects_grades))
2612  {
2613  $this->ects_grades = $a_ects_grades;
2614  }
2615  }
2616 
2625  {
2626  $this->sequence_settings = $sequence_settings;
2627  }
2628 
2637  {
2638  $this->score_reporting = $score_reporting;
2639  }
2640 
2648  function setInstantFeedbackSolution($instant_feedback = 0)
2649  {
2650  switch ($instant_feedback)
2651  {
2652  case 1:
2653  $this->instant_verification = 1;
2654  break;
2655  default:
2656  $this->instant_verification = 0;
2657  break;
2658  }
2659  }
2660 
2669 {
2670  switch ($answer_feedback)
2671  {
2672  case 1:
2673  $this->answer_feedback = 1;
2674  break;
2675  default:
2676  $this->answer_feedback = 0;
2677  break;
2678  }
2679 }
2680 
2687 function setGenericAnswerFeedback($generic_answer_feedback = 0)
2688 {
2689  switch ($generic_answer_feedback)
2690  {
2691  case 1:
2692  $this->answer_feedback = 1;
2693  break;
2694  default:
2695  $this->answer_feedback = 0;
2696  break;
2697  }
2698 }
2699 
2708  {
2709  switch ($answer_feedback_points)
2710  {
2711  case 1:
2712  $this->answer_feedback_points = 1;
2713  break;
2714  default:
2715  $this->answer_feedback_points = 0;
2716  break;
2717  }
2718  }
2719 
2727  function setRandomTest($a_random_test = 0)
2728  {
2729  $this->random_test = $a_random_test;
2730  }
2731 
2739  function setRandomQuestionCount($a_random_question_count = "")
2740  {
2741  $this->random_question_count = $a_random_question_count;
2742  }
2743 
2752  {
2753  if (!$reporting_date)
2754  {
2755  $this->reporting_date = "";
2756  $this->ects_output = 0;
2757  }
2758  else
2759  {
2760  $this->reporting_date = $reporting_date;
2761  }
2762  }
2763 
2772  {
2773  return ($this->sequence_settings) ? $this->sequence_settings : 0;
2774  }
2775 
2784  {
2785  return ($this->score_reporting) ? $this->score_reporting : 0;
2786  }
2787 
2796  {
2797  return ($this->instant_verification) ? $this->instant_verification : 0;
2798  }
2799 
2808  public function getAnswerFeedback()
2809  {
2810  return ($this->answer_feedback) ? $this->answer_feedback : 0;
2811  }
2812 
2821  public function getGenericAnswerFeedback()
2822  {
2823  return ($this->answer_feedback) ? $this->answer_feedback : 0;
2824  }
2825 
2834 {
2835  return ($this->answer_feedback_points) ? $this->answer_feedback_points : 0;
2836 }
2837 
2845  function getCountSystem()
2846  {
2847  return ($this->count_system) ? $this->count_system : 0;
2848  }
2849 
2857  function _getCountSystem($active_id)
2858  {
2859  global $ilDB;
2860  $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",
2861  array('integer'),
2862  array($active_id)
2863  );
2864  if ($result->numRows())
2865  {
2866  $row = $ilDB->fetchAssoc($result);
2867  return $row["count_system"];
2868  }
2869  return FALSE;
2870  }
2871 
2879  function getMCScoring()
2880  {
2881  return ($this->mc_scoring) ? $this->mc_scoring : 0;
2882  }
2883 
2891  function getScoreCutting()
2892  {
2893  return ($this->score_cutting) ? $this->score_cutting : 0;
2894  }
2895 
2903  function getPassword()
2904  {
2905  return (strlen($this->password)) ? $this->password : NULL;
2906  }
2907 
2915  function getPassScoring()
2916  {
2917  return ($this->pass_scoring) ? $this->pass_scoring : 0;
2918  }
2919 
2927  function _getPassScoring($active_id)
2928  {
2929  global $ilDB;
2930  $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",
2931  array('integer'),
2932  array($active_id)
2933  );
2934  if ($result->numRows())
2935  {
2936  $row = $ilDB->fetchAssoc($result);
2937  return $row["pass_scoring"];
2938  }
2939  return 0;
2940  }
2941 
2949  function _getMCScoring($active_id)
2950  {
2951  global $ilDB;
2952  $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",
2953  array('integer'),
2954  array($active_id)
2955  );
2956  if ($result->numRows())
2957  {
2958  $row = $ilDB->fetchAssoc($result);
2959  return $row["mc_scoring"];
2960  }
2961  return FALSE;
2962  }
2963 
2971  function _getScoreCutting($active_id)
2972  {
2973  global $ilDB;
2974  $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",
2975  array('integer'),
2976  array($active_id)
2977  );
2978  if ($result->numRows())
2979  {
2980  $row = $ilDB->fetchAssoc($result);
2981  return $row["score_cutting"];
2982  }
2983  return FALSE;
2984  }
2985 
2993  function getReportingDate()
2994  {
2995  return (strlen($this->reporting_date)) ? $this->reporting_date : NULL;
2996  }
2997 
3005  function getNrOfTries()
3006  {
3007  return ($this->nr_of_tries) ? $this->nr_of_tries : 0;
3008  }
3009 
3017  function getKiosk()
3018  {
3019  return ($this->_kiosk) ? $this->_kiosk : 0;
3020  }
3021 
3022 
3030  function setKiosk($kiosk = 0)
3031  {
3032  $this->_kiosk = $kiosk;
3033  }
3034 
3042  function getKioskMode()
3043  {
3044  if (($this->_kiosk & 1) > 0)
3045  {
3046  return TRUE;
3047  }
3048  else
3049  {
3050  return FALSE;
3051  }
3052  }
3053 
3061  public function setKioskMode($a_kiosk = FALSE)
3062  {
3063  if ($a_kiosk)
3064  {
3065  $this->_kiosk = $this->_kiosk | 1;
3066  }
3067  else
3068  {
3069  if ($this->getKioskMode())
3070  {
3071  $this->_kiosk = $this->_kiosk ^ 1;
3072  }
3073  }
3074  }
3075 
3083  public function getShowKioskModeTitle()
3084  {
3085  if (($this->_kiosk & 2) > 0)
3086  {
3087  return TRUE;
3088  }
3089  else
3090  {
3091  return FALSE;
3092  }
3093  }
3094 
3101  public function setShowKioskModeTitle($a_title = FALSE)
3102  {
3103  if ($a_title)
3104  {
3105  $this->_kiosk = $this->_kiosk | 2;
3106  }
3107  else
3108  {
3109  if ($this->getShowKioskModeTitle())
3110  {
3111  $this->_kiosk = $this->_kiosk ^ 2;
3112  }
3113  }
3114  }
3115 
3124  {
3125  if (($this->_kiosk & 4) > 0)
3126  {
3127  return TRUE;
3128  }
3129  else
3130  {
3131  return FALSE;
3132  }
3133  }
3134 
3141  public function setShowKioskModeParticipant($a_participant = FALSE)
3142  {
3143  if ($a_participant)
3144  {
3145  $this->_kiosk = $this->_kiosk | 4;
3146  }
3147  else
3148  {
3149  if ($this->getShowKioskModeParticipant())
3150  {
3151  $this->_kiosk = $this->_kiosk ^ 4;
3152  }
3153  }
3154  }
3155 
3164  {
3165  return ($this->use_previous_answers) ? $this->use_previous_answers : 0;
3166  }
3167 
3175  function getTitleOutput()
3176  {
3177  return ($this->title_output) ? $this->title_output : 0;
3178  }
3179 
3188  function _getTitleOutput($active_id)
3189  {
3190  global $ilDB;
3191 
3192  $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",
3193  array('integer'),
3194  array($active_id)
3195  );
3196  if ($result->numRows())
3197  {
3198  $row = $ilDB->fetchAssoc($result);
3199  return $row["title_output"];
3200  }
3201  return 0;
3202  }
3203 
3213  function _getUsePreviousAnswers($active_id, $user_active_user_setting = false)
3214  {
3215  global $ilDB;
3216  global $ilUser;
3217 
3219 
3220  $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",
3221  array("integer"),
3222  array($active_id)
3223  );
3224  if ($result->numRows())
3225  {
3226  $row = $ilDB->fetchAssoc($result);
3227  $use_previous_answers = $row["use_previous_answers"];
3228  }
3229 
3230  if ($use_previous_answers == 1)
3231  {
3232  if ($user_active_user_setting)
3233  {
3234  $res = $ilUser->getPref("tst_use_previous_answers");
3235  if ($res !== FALSE)
3236  {
3238  }
3239  }
3240  }
3241  return $use_previous_answers;
3242  }
3243 
3252  {
3253  return (strlen($this->processing_time)) ? $this->processing_time : NULL;
3254  }
3255 
3262  public function getProcessingTimeAsArray()
3263  {
3264  if (strlen($this->processing_time))
3265  {
3266  if (preg_match("/(\d{2}):(\d{2}):(\d{2})/is", $this->processing_time, $matches))
3267  {
3268  if ((int)$matches[1]+(int)$matches[2]+(int)$matches[3] == 0)
3269  {
3270  return $this->getEstimatedWorkingTime();
3271  }
3272  else
3273  {
3274  return array(
3275  'hh' => $matches[1],
3276  'mm' => $matches[2],
3277  'ss' => $matches[3],
3278  );
3279  }
3280  }
3281  }
3282  return $this->getEstimatedWorkingTime();
3283  }
3284 
3293  {
3294  if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $this->getProcessingTime(), $matches))
3295  {
3296  return ($matches[1] * 3600) + ($matches[2] * 60) + $matches[3];
3297  }
3298  else
3299  {
3300  return 0;
3301  }
3302  }
3303 
3312  {
3313  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndingTime(), $matches))
3314  {
3315  $ending = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
3316  $now = time();
3317  return $ending - $now;
3318  }
3319  else
3320  {
3321  return 0;
3322  }
3323  }
3324 
3333  {
3334  return ($this->enable_processing_time) ? $this->enable_processing_time : 0;
3335  }
3336 
3345  {
3346  return ($this->reset_processing_time) ? $this->reset_processing_time : 0;
3347  }
3348 
3356  function getStartingTime()
3357  {
3358  return (strlen($this->starting_time)) ? $this->starting_time : NULL;
3359  }
3360 
3368  function getEndingTime()
3369  {
3370  return (strlen($this->ending_time)) ? $this->ending_time : NULL;
3371  }
3372 
3381  {
3382  $this->nr_of_tries = $nr_of_tries;
3383  }
3384 
3393  {
3395  {
3396  $this->use_previous_answers = 1;
3397  }
3398  else
3399  {
3400  $this->use_previous_answers = 0;
3401  }
3402  }
3403 
3412  {
3413  switch ($title_output)
3414  {
3415  case 1:
3416  $this->title_output = 1;
3417  break;
3418  case 2:
3419  $this->title_output = 2;
3420  break;
3421  default:
3422  $this->title_output = 0;
3423  break;
3424  }
3425  }
3426 
3434  function setProcessingTime($processing_time = "00:00:00")
3435  {
3436  $this->processing_time = $processing_time;
3437  }
3438 
3446  function setEnableProcessingTime($enable = 0)
3447  {
3448  if ($enable) {
3449  $this->enable_processing_time = "1";
3450  } else {
3451  $this->enable_processing_time = "0";
3452  }
3453  }
3454 
3462  function setResetProcessingTime($reset = 0)
3463  {
3464  if ($reset)
3465  {
3466  $this->reset_processing_time = 1;
3467  }
3468  else
3469  {
3470  $this->reset_processing_time = 0;
3471  }
3472  }
3473 
3482  {
3483  $this->starting_time = $starting_time;
3484  }
3485 
3493  function setEndingTime($ending_time = NULL)
3494  {
3495  $this->ending_time = $ending_time;
3496  }
3497 
3505  function setCountSystem($a_count_system = COUNT_PARTIAL_SOLUTIONS)
3506  {
3507  $this->count_system = $a_count_system;
3508  }
3509 
3517  function setPassword($a_password = NULL)
3518  {
3519  $this->password = $a_password;
3520  }
3521 
3529  function setScoreCutting($a_score_cutting = SCORE_CUT_QUESTION)
3530  {
3531  $this->score_cutting = $a_score_cutting;
3532  }
3533 
3542  {
3543  $this->mc_scoring = $a_mc_scoring;
3544  }
3545 
3553  function setPassScoring($a_pass_scoring = SCORE_LAST_PASS)
3554  {
3555  switch ($a_pass_scoring)
3556  {
3557  case SCORE_BEST_PASS:
3558  $this->pass_scoring = SCORE_BEST_PASS;
3559  break;
3560  default:
3561  $this->pass_scoring = SCORE_LAST_PASS;
3562  break;
3563  }
3564  }
3565 
3573  function removeQuestion($question_id)
3574  {
3575  $question =& ilObjTest::_instanciateQuestion($question_id);
3576  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3578  {
3579  $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id);
3580  }
3581  $question->delete($question_id);
3582  $this->removeAllTestEditings($question_id);
3583  $this->loadQuestions();
3584  $this->saveQuestionsToDb();
3585  }
3586 
3594  public function removeAllTestEditings($question_id = "")
3595  {
3596  global $ilDB;
3597 
3598  // remove the question from tst_solutions
3599  if ($question_id)
3600  {
3601  $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",
3602  array('integer','integer'),
3603  array($this->getTestId(), $question_id)
3604  );
3605  $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",
3606  array('integer','integer'),
3607  array($this->getTestId(), $question_id)
3608  );
3609  $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",
3610  array('integer','integer'),
3611  array($this->getTestId(), $question_id)
3612  );
3613  $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)",
3614  array('integer'),
3615  array($this->getTestId())
3616  );
3617 
3618  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
3620  }
3621  else
3622  {
3623  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE tst_solutions.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3624  array('integer'),
3625  array($this->getTestId())
3626  );
3627  $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)",
3628  array('integer'),
3629  array($this->getTestId())
3630  );
3631  $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)",
3632  array('integer'),
3633  array($this->getTestId())
3634  );
3635  $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)",
3636  array('integer'),
3637  array($this->getTestId())
3638  );
3639 
3640  $query = "SELECT active_id FROM tst_active WHERE test_fi = %s";
3641  $res = $ilDB->queryF($query, array('integer'), array($this->getTestId()));
3642  $activeIds = array();
3643  while( $row = $ilDB->fetchAssoc($res) )
3644  {
3645  $activeIds[] = $row['active_id'];
3646  }
3647 
3648  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
3650 
3651  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3653  {
3654  $this->logAction($this->lng->txtlng("assessment", "log_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()));
3655  }
3656  }
3657  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE tst_sequence.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3658  array('integer'),
3659  array($this->getTestId())
3660  );
3661 
3662  if ($this->isRandomTest())
3663  {
3664  $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)",
3665  array('integer'),
3666  array($this->getTestId())
3667  );
3668  }
3669 
3670  // remove test_active entries, because test has changed
3671  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
3672  array('integer'),
3673  array($this->getTestId())
3674  );
3675 
3676  // remove saved user passwords
3677  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE keyword = %s",
3678  array('text'),
3679  array("tst_password_".$this->getTestId())
3680  );
3681 
3682  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3683  // remove file uploads
3684  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId()))
3685  {
3686  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId());
3687  }
3688  }
3689 
3690  public function removeTestResults(ilTestParticipantData $participantData)
3691  {
3692  global $ilDB;
3693 
3694  $IN_activeIds = $ilDB->in('active_fi', $participantData->getActiveIds(), false, 'integer');
3695 
3696  $ilDB->manipulate("DELETE FROM tst_solutions WHERE $IN_activeIds");
3697  $ilDB->manipulate("DELETE FROM tst_qst_solved WHERE $IN_activeIds");
3698  $ilDB->manipulate("DELETE FROM tst_test_result WHERE $IN_activeIds");
3699  $ilDB->manipulate("DELETE FROM tst_pass_result WHERE $IN_activeIds");
3700  $ilDB->manipulate("DELETE FROM tst_result_cache WHERE $IN_activeIds");
3701  $ilDB->manipulate("DELETE FROM tst_sequence WHERE $IN_activeIds");
3702 
3703  if ($this->isRandomTest())
3704  {
3705  $ilDB->manipulate("DELETE FROM tst_test_rnd_qst WHERE $IN_activeIds");
3706  }
3707 
3708  $IN_userIds = $ilDB->in('usr_id', $participantData->getUserIds(), false, 'integer');
3709  $ilDB->manipulateF(
3710  "DELETE FROM usr_pref WHERE $IN_userIds AND keyword = %s",
3711  array('text'), array("tst_password_".$this->getTestId())
3712  );
3713 
3714  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3715 
3716  foreach ($participantData->getActiveIds() as $active_id)
3717  {
3718  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3719  // remove file uploads
3720  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"))
3721  {
3722  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id");
3723  }
3724 
3726  {
3727  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id))));
3728  }
3729  }
3730 
3731  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
3733  }
3734 
3735  public function removeTestActives($activeIds)
3736  {
3737  global $ilDB;
3738 
3739  $IN_activeIds = $ilDB->in('active_id', $activeIds, false, 'integer');
3740  $ilDB->manipulate("DELETE FROM tst_active WHERE $IN_activeIds");
3741  }
3742 
3743  function removeTestResultsForUser($user_id)
3744  {
3745  global $ilDB;
3746 
3747  $active_id = $this->getActiveIdOfUser($user_id);
3748 
3749  // remove the question from tst_solutions
3750  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s",
3751  array('integer'),
3752  array($active_id)
3753  );
3754  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s",
3755  array('integer'),
3756  array($active_id)
3757  );
3758  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE active_fi = %s",
3759  array('integer'),
3760  array($active_id)
3761  );
3762  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE active_fi = %s",
3763  array('integer'),
3764  array($active_id)
3765  );
3766 
3767  if ($this->isRandomTest())
3768  {
3769  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE active_fi = %s",
3770  array('integer'),
3771  array($active_id)
3772  );
3773  }
3774 
3775  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3777  {
3778  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id))));
3779  }
3780 
3781  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
3782  array('integer'),
3783  array($active_id)
3784  );
3785 
3786  // remove test_active entry
3787  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE active_id = %s",
3788  array('integer'),
3789  array($active_id)
3790  );
3791 
3792  // remove saved user password
3793  if ($user_id > 0)
3794  {
3795  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE usr_id = %s AND keyword = %s",
3796  array('integer', 'text'),
3797  array($user_id, "tst_password_".$this->getTestId())
3798  );
3799  }
3800 
3801  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3802  // remove file uploads
3803  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"))
3804  {
3805  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id");
3806  }
3807 
3808  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
3810  }
3811 
3819  function questionMoveUp($question_id)
3820  {
3821  global $ilDB;
3822 
3823  // Move a question up in sequence
3824  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
3825  array('integer', 'integer'),
3826  array($this->getTestId(), $question_id)
3827  );
3828  $data = $ilDB->fetchObject($result);
3829  if ($data->sequence > 1)
3830  {
3831  // OK, it's not the top question, so move it up
3832  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
3833  array('integer','integer'),
3834  array($this->getTestId(), $data->sequence - 1)
3835  );
3836  $data_previous = $ilDB->fetchObject($result);
3837  // change previous dataset
3838  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3839  array('integer','integer'),
3840  array($data->sequence, $data_previous->test_question_id)
3841  );
3842  // move actual dataset up
3843  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3844  array('integer','integer'),
3845  array($data->sequence - 1, $data->test_question_id)
3846  );
3847  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3849  {
3850  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence-1), $question_id);
3851  }
3852  }
3853  $this->loadQuestions();
3854  }
3855 
3863  function questionMoveDown($question_id)
3864  {
3865  global $ilDB;
3866 
3867  // Move a question down in sequence
3868  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
3869  array('integer','integer'),
3870  array($this->getTestId(), $question_id)
3871  );
3872  $data = $ilDB->fetchObject($result);
3873  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
3874  array('integer','integer'),
3875  array($this->getTestId(), $data->sequence + 1)
3876  );
3877  if ($result->numRows() == 1)
3878  {
3879  // OK, it's not the last question, so move it down
3880  $data_next = $ilDB->fetchObject($result);
3881  // change next dataset
3882  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3883  array('integer','integer'),
3884  array($data->sequence, $data_next->test_question_id)
3885  );
3886  // move actual dataset down
3887  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3888  array('integer','integer'),
3889  array($data->sequence + 1, $data->test_question_id)
3890  );
3891  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3893  {
3894  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence+1), $question_id);
3895  }
3896  }
3897  $this->loadQuestions();
3898  }
3899 
3907  function duplicateQuestionForTest($question_id)
3908  {
3909  global $ilUser;
3910  $question =& ilObjTest::_instanciateQuestion($question_id);
3911  $duplicate_id = $question->duplicate(true, null, null, null, $this->getId());
3912 
3913  return $duplicate_id;
3914  }
3915 
3922  function insertQuestion($question_id, $linkOnly = false)
3923  {
3924  global $ilDB;
3925 #var_dump($question_id);
3926  if ($linkOnly)
3927  {
3928  $duplicate_id = $question_id;
3929  }
3930  else
3931  {
3932  $duplicate_id = $this->duplicateQuestionForTest($question_id);
3933  }
3934 
3935  // get maximum sequence index in test
3936  $result = $ilDB->queryF("SELECT MAX(sequence) seq FROM tst_test_question WHERE test_fi=%s",
3937  array('integer'),
3938  array($this->getTestId())
3939  );
3940  $sequence = 1;
3941 
3942  if ($result->numRows() == 1)
3943  {
3944  $data = $ilDB->fetchObject($result);
3945  $sequence = $data->seq + 1;
3946  }
3947 
3948  $next_id = $ilDB->nextId('tst_test_question');
3949  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
3950  array('integer', 'integer','integer','integer','integer'),
3951  array($next_id, $this->getTestId(), $duplicate_id, $sequence, time())
3952  );
3953  if ($affectedRows == 1)
3954  {
3955  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3957  {
3958  $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . $sequence, $duplicate_id);
3959  }
3960  }
3961  // remove test_active entries, because test has changed
3962  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
3963  array('integer'),
3964  array($this->getTestId())
3965  );
3966  $this->loadQuestions();
3967  $this->saveCompleteStatus();
3968  return $duplicate_id;
3969  }
3970 
3978  function &getQuestionTitles()
3979  {
3980  $titles = array();
3981  if (!$this->isRandomTest())
3982  {
3983  global $ilDB;
3984  $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",
3985  array('integer'),
3986  array($this->getTestId())
3987  );
3988  while ($row = $ilDB->fetchAssoc($result))
3989  {
3990  array_push($titles, $row["title"]);
3991  }
3992  }
3993  return $titles;
3994  }
3995 
4004  {
4005  $titles = array();
4006  if (!$this->isRandomTest())
4007  {
4008  global $ilDB;
4009  $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",
4010  array('integer'),
4011  array($this->getTestId())
4012  );
4013  while ($row = $ilDB->fetchAssoc($result))
4014  {
4015  $titles[$row['question_id']] = $row["title"];
4016  }
4017  }
4018  return $titles;
4019  }
4020 
4030  {
4031  if ($this->getTitleOutput() == 2)
4032  {
4033  return $this->lng->txt("ass_question");
4034  }
4035  else
4036  {
4037  return $title;
4038  }
4039  }
4040 
4049  function getQuestionDataset($question_id)
4050  {
4051  global $ilDB;
4052 
4053  $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",
4054  array('integer'),
4055  array($question_id)
4056  );
4057  $row = $ilDB->fetchObject($result);
4058  return $row;
4059  }
4060 
4065  public function &getExistingQuestions($pass = NULL)
4066  {
4071  global $ilUser, $ilDB;
4072 
4073  $existing_questions = array();
4074  $active_id = $this->getActiveIdOfUser($ilUser->getId());
4075  if($this->isRandomTest())
4076  {
4077  if(is_null($pass)) $pass = 0;
4078  $result = $ilDB->queryF(
4079  "SELECT qpl_questions.original_id
4080  FROM qpl_questions, tst_test_rnd_qst
4081  WHERE tst_test_rnd_qst.active_fi = %s
4082  AND tst_test_rnd_qst.question_fi = qpl_questions.question_id
4083  AND tst_test_rnd_qst.pass = %s
4084  AND qpl_questions.original_id IS NOT NULL",
4085  array('integer', 'integer'),
4086  array($active_id, $pass)
4087  );
4088  }
4089  else
4090  {
4091  $result = $ilDB->queryF(
4092  "SELECT qpl_questions.original_id
4093  FROM qpl_questions, tst_test_question
4094  WHERE tst_test_question.test_fi = %s
4095  AND tst_test_question.question_fi = qpl_questions.question_id
4096  AND qpl_questions.original_id IS NOT NULL",
4097  array('integer'),
4098  array($this->getTestId())
4099  );
4100  }
4101  while($data = $ilDB->fetchObject($result))
4102  {
4103  array_push($existing_questions, $data->original_id);
4104  }
4105  return $existing_questions;
4106  }
4107 
4115  function getQuestionType($question_id)
4116  {
4117  global $ilDB;
4118 
4119  if ($question_id < 1) return -1;
4120  $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",
4121  array('integer'),
4122  array($question_id)
4123  );
4124  if ($result->numRows() == 1)
4125  {
4126  $data = $ilDB->fetchObject($result);
4127  return $data->type_tag;
4128  }
4129  else
4130  {
4131  return "";
4132  }
4133  }
4134 
4141  function startWorkingTime($active_id, $pass)
4142  {
4143  global $ilDB;
4144 
4145  $next_id = $ilDB->nextId('tst_times');
4146  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_times (times_id, active_fi, started, finished, pass, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
4147  array('integer', 'integer', 'timestamp', 'timestamp', 'integer', 'integer'),
4148  array($next_id, $active_id, strftime("%Y-%m-%d %H:%M:%S"), strftime("%Y-%m-%d %H:%M:%S"), $pass, time())
4149  );
4150  return $next_id;
4151  }
4152 
4159  function updateWorkingTime($times_id)
4160  {
4161  global $ilDB;
4162 
4163  $affectedRows = $ilDB->manipulateF("UPDATE tst_times SET finished = %s, tstamp = %s WHERE times_id = %s",
4164  array('timestamp', 'integer', 'integer'),
4165  array(strftime("%Y-%m-%d %H:%M:%S"), time(), $times_id)
4166  );
4167  }
4168 
4175  function &getWorkedQuestions($active_id, $pass = NULL)
4176  {
4177  global $ilUser;
4178  global $ilDB;
4179 
4180  if (is_null($pass))
4181  {
4182  $result = $ilDB->queryF("SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
4183  array('integer','integer'),
4184  array($active_id, 0)
4185  );
4186  }
4187  else
4188  {
4189  $result = $ilDB->queryF("SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
4190  array('integer','integer'),
4191  array($active_id, $pass)
4192  );
4193  }
4194  $result_array = array();
4195  while ($row = $ilDB->fetchAssoc($result))
4196  {
4197  array_push($result_array, $row["question_fi"]);
4198  }
4199  return $result_array;
4200  }
4201 
4210  function isTestFinishedToViewResults($active_id, $currentpass)
4211  {
4212  $num = $this->getPassFinishDate($active_id, $currentpass);
4213  return ((($currentpass > 0) && ($num == 0)) || $this->isTestFinished($active_id)) ? true : false;
4214  }
4215 
4222  function &getAllQuestions($pass = NULL)
4223  {
4224  global $ilUser;
4225  global $ilDB;
4226 
4227  $result_array = array();
4228  if ($this->isRandomTest())
4229  {
4230  $active_id = $this->getActiveIdOfUser($ilUser->getId());
4231  $this->loadQuestions($active_id, $pass);
4232  if (count($this->questions) == 0) return $result_array;
4233  if (is_null($pass))
4234  {
4235  $pass = $this->_getPass($active_id);
4236  }
4237  $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'),
4238  array('integer','integer'),
4239  array($active_id, $pass)
4240  );
4241  }
4242  else
4243  {
4244  if (count($this->questions) == 0) return $result_array;
4245  $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'));
4246  }
4247  while ($row = $ilDB->fetchAssoc($result))
4248  {
4249  $result_array[$row["question_id"]] = $row;
4250  }
4251  return $result_array;
4252  }
4253 
4262  function getActiveIdOfUser($user_id = "", $anonymous_id = "")
4263  {
4264  global $ilDB;
4265  global $ilUser;
4266 
4267  if (!$user_id) $user_id = $ilUser->getId();
4268  if (($_SESSION["AccountId"] == ANONYMOUS_USER_ID) && (strlen($_SESSION["tst_access_code"][$this->getTestId()])))
4269  {
4270  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
4271  array('integer','integer','text'),
4272  array($user_id, $this->test_id, $_SESSION["tst_access_code"][$this->getTestId()])
4273  );
4274  }
4275  else if (strlen($anonymous_id))
4276  {
4277  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
4278  array('integer','integer','text'),
4279  array($user_id, $this->test_id, $anonymous_id)
4280  );
4281  }
4282  else
4283  {
4284  if ($_SESSION["AccountId"] == ANONYMOUS_USER_ID)
4285  {
4286  return NULL;
4287  }
4288  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
4289  array('integer','integer'),
4290  array($user_id, $this->test_id)
4291  );
4292  }
4293  if ($result->numRows())
4294  {
4295  $row = $ilDB->fetchAssoc($result);
4296  return $row["active_id"];
4297  }
4298  else
4299  {
4300  return 0;
4301  }
4302  }
4303 
4312  function _getActiveIdOfUser($user_id = "", $test_id = "")
4313  {
4314  global $ilDB;
4315  global $ilUser;
4316 
4317  if (!$user_id) {
4318  $user_id = $ilUser->id;
4319  }
4320  if (!$test_id)
4321  {
4322  return "";
4323  }
4324  $result = $ilDB->queryF("SELECT tst_active.active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
4325  array('integer', 'integer'),
4326  array($user_id, $test_id)
4327  );
4328  if ($result->numRows())
4329  {
4330  $row = $ilDB->fetchAssoc($result);
4331  return $row["active_id"];
4332  }
4333  else
4334  {
4335  return "";
4336  }
4337  }
4338 
4345  function pcArrayShuffle($array)
4346  {
4347  $keys = array_keys($array);
4348  shuffle($keys);
4349  $result = array();
4350  foreach ($keys as $key)
4351  {
4352  $result[$key] = $array[$key];
4353  }
4354  return $result;
4355  }
4356 
4364  function &getTestResult($active_id, $pass = NULL, $ordered_sequence = FALSE)
4365  {
4366  global $ilDB;
4367 
4368  $results = $this->getResultsForActiveId($active_id);
4369 
4370  if( is_null($pass) )
4371  {
4372  $pass = $results['pass'];
4373  }
4374 
4375  include_once "./Modules/Test/classes/class.ilTestSequence.php";
4376 
4377  $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
4378 
4379  $sequence = array();
4380 
4381  if( $ordered_sequence )
4382  {
4383  $sequence = $testSequence->getOrderedSequenceQuestions();
4384  }
4385  else
4386  {
4387  $sequence = $testSequence->getUserSequenceQuestions();
4388  }
4389 
4390  $arrResults = array();
4391 
4392  $query = "
4393  SELECT tst_test_result.question_fi,
4394  tst_test_result.points reached,
4395  tst_test_result.hint_count requested_hints,
4396  tst_test_result.hint_points hint_points,
4397  tst_test_result.answered answered
4398 
4399  FROM tst_test_result
4400 
4401  LEFT JOIN tst_solutions
4402  ON tst_solutions.active_fi = tst_test_result.active_fi
4403  AND tst_solutions.question_fi = tst_test_result.question_fi
4404 
4405  WHERE tst_test_result.active_fi = %s
4406  AND tst_test_result.pass = %s
4407  ";
4408 
4409  $solutionresult = $ilDB->queryF(
4410  $query, array('integer', 'integer'), array($active_id, $pass)
4411  );
4412 
4413  while( $row = $ilDB->fetchAssoc($solutionresult) )
4414  {
4415  $arrResults[ $row['question_fi'] ] = $row;
4416  }
4417 
4418  $numWorkedThrough = count($arrResults);
4419 
4420  require_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
4421 
4422  $IN_question_ids = $ilDB->in('qpl_questions.question_id', $sequence, false, 'integer');
4423 
4424  $query = "
4425  SELECT qpl_questions.*,
4426  qpl_qst_type.type_tag,
4427  qpl_sol_sug.question_fi has_sug_sol
4428 
4429  FROM qpl_qst_type,
4430  qpl_questions
4431 
4432  LEFT JOIN qpl_sol_sug
4433  ON qpl_sol_sug.question_fi = qpl_questions.question_id
4434 
4435  WHERE qpl_qst_type.question_type_id = qpl_questions.question_type_fi
4436  AND $IN_question_ids
4437  ";
4438 
4439  $result = $ilDB->query($query);
4440 
4441  $unordered = array();
4442 
4443  $key = 1;
4444 
4445  $obligationsAnswered = true;
4446 
4447  while( $row = $ilDB->fetchAssoc($result) )
4448  {
4449  $percentvalue = (
4450  $row['points'] ? $arrResults[ $row['question_id'] ]['reached'] / $row['points'] : 0
4451  );
4452 
4453  if( $percentvalue < 0 ) $percentvalue = 0.0;
4454 
4455  $data = array(
4456  "nr" => "$key",
4457  "title" => ilUtil::prepareFormOutput($row['title']),
4458  "max" => round($row['points'], 2),
4459  "reached" => round($arrResults[$row['question_id']]['reached'],2),
4460  'requested_hints' => $arrResults[$row['question_id']]['requested_hints'],
4461  'hint_points' => $arrResults[$row['question_id']]['hint_points'],
4462  "percent" => sprintf("%2.2f ", ($percentvalue) * 100) . "%",
4463  "solution" => ($row['has_sug_sol']) ? assQuestion::_getSuggestedSolutionOutput($row['question_id']) : '',
4464  "type" => $row["type_tag"],
4465  "qid" => $row['question_id'],
4466  "original_id" => $row["original_id"],
4467  "workedthrough" => isset($arrResults[$row['question_id']]) ? 1 : 0,
4468  'answered' => $arrResults[$row['question_id']]['answered']
4469  );
4470 
4471  if( !$arrResults[ $row['question_id'] ]['answered'] )
4472  {
4473  $obligationsAnswered = false;
4474  }
4475 
4476  $unordered[ $row['question_id'] ] = $data;
4477 
4478  $key++;
4479  }
4480 
4481  $pass_max = 0;
4482  $pass_reached = 0;
4483  $pass_requested_hints = 0;
4484  $pass_hint_points = 0;
4485  $key = 1;
4486 
4487  $found = array();
4488 
4489  foreach( $sequence as $qid )
4490  {
4491  // building pass point sums based on prepared data
4492  // for question that exists in users qst sequence
4493  $pass_max += round($unordered[$qid]['max'], 2);
4494  $pass_reached += round($unordered[$qid]['reached'], 2);
4495  $pass_requested_hints += $unordered[$qid]['requested_hints'];
4496  $pass_hint_points += $unordered[$qid]['hint_points'];
4497 
4498  // pickup prepared data for question
4499  // that exists in users qst sequence
4500  $unordered[$qid]['nr'] = $key;
4501  array_push($found, $unordered[$qid]);
4502 
4503  // increment key counter
4504  $key++;
4505  }
4506 
4507  $unordered = null;
4508 
4509  if( $this->getScoreCutting() == 1 )
4510  {
4511  if( $results['reached_points'] < 0 )
4512  {
4513  $results['reached_points'] = 0;
4514  }
4515 
4516  if( $pass_reached < 0 ) $pass_reached = 0;
4517  }
4518 
4519  $found['pass']['total_max_points'] = $pass_max;
4520  $found['pass']['total_reached_points'] = $pass_reached;
4521  $found['pass']['total_requested_hints'] = $pass_requested_hints;
4522  $found['pass']['total_hint_points'] = $pass_hint_points;
4523  $found['pass']['percent'] = ($pass_max > 0) ? $pass_reached / $pass_max : 0;
4524  $found['pass']['obligationsAnswered'] = $obligationsAnswered;
4525  $found['pass']['num_workedthrough'] = $numWorkedThrough;
4526 
4527  $found["test"]["total_max_points"] = $results['max_points'];
4528  $found["test"]["total_reached_points"] = $results['reached_points'];
4529  $found["test"]["total_requested_hints"] = $results['hint_count'];
4530  $found["test"]["total_hint_points"] = $results['hint_points'];
4531  $found["test"]["result_pass"] = $results['pass'];
4532  $found['test']['obligations_answered'] = $results['obligations_answered'];
4533 
4534  if( (!$total_reached_points) or (!$total_max_points) )
4535  {
4536  $percentage = 0.0;
4537  }
4538  else
4539  {
4540  $percentage = ($total_reached_points / $total_max_points) * 100.0;
4541 
4542  if( $percentage < 0 ) $percentage = 0.0;
4543  }
4544 
4545  $found["test"]["passed"] = $results['passed'];
4546 
4547  return $found;
4548  }
4549 
4556  function evalTotalPersons()
4557  {
4558  global $ilDB;
4559 
4560  $result = $ilDB->queryF("SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s",
4561  array('integer'),
4562  array($this->getTestId())
4563  );
4564  $row = $ilDB->fetchAssoc($result);
4565  return $row["total"];
4566  }
4567 
4574  function getCompleteWorkingTime($user_id)
4575  {
4576  global $ilDB;
4577 
4578  $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",
4579  array('integer','integer'),
4580  array($this->getTestId(), $user_id)
4581  );
4582  $time = 0;
4583  while ($row = $ilDB->fetchAssoc($result))
4584  {
4585  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4586  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4587  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4588  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4589  $time += ($epoch_2 - $epoch_1);
4590  }
4591  return $time;
4592  }
4593 
4601  {
4602  return $this->_getCompleteWorkingTimeOfParticipants($this->getTestId());
4603  }
4604 
4613  {
4614  global $ilDB;
4615 
4616  $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",
4617  array('integer'),
4618  array($test_id)
4619  );
4620  $time = 0;
4621  $times = array();
4622  while ($row = $ilDB->fetchAssoc($result))
4623  {
4624  if (!array_key_exists($row["active_fi"], $times))
4625  {
4626  $times[$row["active_fi"]] = 0;
4627  }
4628  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4629  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4630  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4631  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4632  $times[$row["active_fi"]] += ($epoch_2 - $epoch_1);
4633  }
4634  return $times;
4635  }
4636 
4644  {
4645  global $ilDB;
4646 
4647  $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",
4648  array('integer','integer'),
4649  array($this->getTestId(), $active_id)
4650  );
4651  $time = 0;
4652  while ($row = $ilDB->fetchAssoc($result))
4653  {
4654  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4655  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4656  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4657  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4658  $time += ($epoch_2 - $epoch_1);
4659  }
4660  return $time;
4661  }
4662 
4670  {
4671  global $ilDB;
4672 
4673  $result = $ilDB->queryF("SELECT * FROM tst_times WHERE active_fi = %s AND pass = %s ORDER BY started",
4674  array('integer','integer'),
4675  array($active_id, $pass)
4676  );
4677  $time = 0;
4678  while ($row = $ilDB->fetchAssoc($result))
4679  {
4680  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4681  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4682  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4683  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4684  $time += ($epoch_2 - $epoch_1);
4685  }
4686  return $time;
4687  }
4688 
4696  function getVisitTimeOfParticipant($active_id)
4697  {
4698  return ilObjTest::_getVisitTimeOfParticipant($this->getTestId(), $active_id);
4699  }
4700 
4709  function _getVisitTimeOfParticipant($test_id, $active_id)
4710  {
4711  global $ilDB;
4712 
4713  $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",
4714  array('integer','integer'),
4715  array($test_id, $active_id)
4716  );
4717  $firstvisit = 0;
4718  $lastvisit = 0;
4719  while ($row = $ilDB->fetchAssoc($result))
4720  {
4721  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4722  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4723  if ($firstvisit == 0 || $epoch_1 < $firstvisit) $firstvisit = $epoch_1;
4724  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4725  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4726  if ($epoch_2 > $lastvisit) $lastvisit = $epoch_2;
4727  }
4728  return array("firstvisit" => $firstvisit, "lastvisit" => $lastvisit);
4729  }
4730 
4737  function &evalStatistical($active_id)
4738  {
4739  global $ilDB;
4740 // global $ilBench;
4741  $pass = ilObjTest::_getResultPass($active_id);
4742  $test_result =& $this->getTestResult($active_id, $pass);
4743  $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",
4744  array('integer'),
4745  array($active_id)
4746  );
4747  $times = array();
4748  $first_visit = 0;
4749  $last_visit = 0;
4750  while ($row = $ilDB->fetchObject($result))
4751  {
4752  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
4753  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4754  if (!$first_visit) {
4755  $first_visit = $epoch_1;
4756  }
4757  if ($epoch_1 < $first_visit) {
4758  $first_visit = $epoch_1;
4759  }
4760  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
4761  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4762  if (!$last_visit) {
4763  $last_visit = $epoch_2;
4764  }
4765  if ($epoch_2 > $last_visit) {
4766  $last_visit = $epoch_2;
4767  }
4768  $times[$row->active_fi] += ($epoch_2 - $epoch_1);
4769  }
4770  $max_time = 0;
4771  foreach ($times as $key => $value) {
4772  $max_time += $value;
4773  }
4774  if ((!$test_result["test"]["total_reached_points"]) or (!$test_result["test"]["total_max_points"]))
4775  {
4776  $percentage = 0.0;
4777  }
4778  else
4779  {
4780  $percentage = ($test_result["test"]["total_reached_points"] / $test_result["test"]["total_max_points"]) * 100.0;
4781  if ($percentage < 0) $percentage = 0.0;
4782  }
4783  $mark_obj = $this->mark_schema->getMatchingMark($percentage);
4784  $first_date = getdate($first_visit);
4785  $last_date = getdate($last_visit);
4786  $qworkedthrough = 0;
4787  foreach ($test_result as $key => $value)
4788  {
4789  if (preg_match("/\d+/", $key))
4790  {
4791  $qworkedthrough += $value["workedthrough"];
4792  }
4793  }
4794  if (!$qworkedthrough)
4795  {
4796  $atimeofwork = 0;
4797  }
4798  else
4799  {
4800  $atimeofwork = $max_time / $qworkedthrough;
4801  }
4802 
4803  $obligationsAnswered = $test_result["test"]["obligations_answered"];
4804 
4805  $result_mark = "";
4806  $passed = "";
4807 
4808  if ($mark_obj)
4809  {
4810  $result_mark = $mark_obj->getShortName();
4811 
4812  if( $mark_obj->getPassed() && $obligationsAnswered )
4813  {
4814  $passed = 1;
4815  }
4816  else
4817  {
4818  $passed = 0;
4819  }
4820  }
4821  $percent_worked_through = 0;
4822  if (count($this->questions))
4823  {
4824  $percent_worked_through = $qworkedthrough / count($this->questions);
4825  }
4826  $result_array = array(
4827  "qworkedthrough" => $qworkedthrough,
4828  "qmax" => count($this->questions),
4829  "pworkedthrough" => $percent_worked_through,
4830  "timeofwork" => $max_time,
4831  "atimeofwork" => $atimeofwork,
4832  "firstvisit" => $first_date,
4833  "lastvisit" => $last_date,
4834  "resultspoints" => $test_result["test"]["total_reached_points"],
4835  "maxpoints" => $test_result["test"]["total_max_points"],
4836  "resultsmarks" => $result_mark,
4837  "passed" => $passed,
4838  "distancemedian" => "0"
4839  );
4840  foreach ($test_result as $key => $value)
4841  {
4842  if (preg_match("/\d+/", $key))
4843  {
4844  $result_array[$key] = $value;
4845  }
4846  }
4847  return $result_array;
4848  }
4849 
4858  {
4859  $totalpoints_array = array();
4860  $all_users =& $this->evalTotalParticipantsArray();
4861  foreach ($all_users as $active_id => $user_name)
4862  {
4863  $test_result =& $this->getTestResult($active_id);
4864  $reached = $test_result["test"]["total_reached_points"];
4865  $total = $test_result["test"]["total_max_points"];
4866  $percentage = $total != 0 ? $reached/$total : 0;
4867  $mark = $this->mark_schema->getMatchingMark($percentage*100.0);
4868 
4869  $obligationsAnswered = $test_result["test"]["obligations_answered"];
4870 
4871  if ($mark)
4872  {
4873  if( $mark->getPassed() && $obligationsAnswered )
4874  {
4875  array_push($totalpoints_array, $test_result["test"]["total_reached_points"]);
4876  }
4877  }
4878  }
4879  return $totalpoints_array;
4880  }
4881 
4888  function &getParticipants()
4889  {
4890  global $ilDB;
4891  $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",
4892  array('integer'),
4893  array($this->getTestId())
4894  );
4895  $persons_array = array();
4896  while ($row = $ilDB->fetchAssoc($result))
4897  {
4898  $name = $this->lng->txt("unknown");
4899  $fullname = $this->lng->txt("unknown");
4900  $login = "";
4901  if (!$this->getAnonymity())
4902  {
4903  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4904  {
4905  $name = $this->lng->txt("deleted_user");
4906  $fullname = $this->lng->txt("deleted_user");
4907  $login = $this->lng->txt("unknown");
4908  }
4909  else
4910  {
4911  $login = $row["login"];
4912  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4913  {
4914  $name = $this->lng->txt("unknown");
4915  $fullname = $this->lng->txt("unknown");
4916  }
4917  else
4918  {
4919  $name = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]);
4920  $fullname = trim($row["title"] . " " . $row["firstname"] . " " . $row["lastname"]);
4921  }
4922  }
4923  }
4924  $persons_array[$row["active_id"]] = array(
4925  "name" => $name,
4926  "fullname" => $fullname,
4927  "login" => $login
4928  );
4929  }
4930  return $persons_array;
4931  }
4932 
4939  function &evalTotalPersonsArray($name_sort_order = "asc")
4940  {
4941  global $ilDB;
4942  $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),
4943  array('integer'),
4944  array($this->getTestId())
4945  );
4946  $persons_array = array();
4947  while ($row = $ilDB->fetchAssoc($result))
4948  {
4949  if ($this->getAnonymity())
4950  {
4951  $persons_array[$row["active_id"]] = $this->lng->txt("unknown");
4952  }
4953  else
4954  {
4955  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4956  {
4957  $persons_array[$row["active_id"]] = $this->lng->txt("deleted_user");
4958  }
4959  else
4960  {
4961  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4962  {
4963  $persons_array[$row["active_id"]] = $row["lastname"];
4964  }
4965  else
4966  {
4967  $persons_array[$row["active_id"]] = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]);
4968  }
4969  }
4970  }
4971  }
4972  return $persons_array;
4973  }
4974 
4981  function &evalTotalParticipantsArray($name_sort_order = "asc")
4982  {
4983  global $ilDB;
4984  $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),
4985  array('integer'),
4986  array($this->getTestId())
4987  );
4988  $persons_array = array();
4989  while ($row = $ilDB->fetchAssoc($result))
4990  {
4991  if ($this->getAnonymity())
4992  {
4993  $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("unknown"));
4994  }
4995  else
4996  {
4997  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4998  {
4999  $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("deleted_user"));
5000  }
5001  else
5002  {
5003  if ($row["user_fi"] == ANONYMOUS_USER_ID)
5004  {
5005  $persons_array[$row["active_id"]] = array("name" => $row["lastname"]);
5006  }
5007  else
5008  {
5009  $persons_array[$row["active_id"]] = array("name" => trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]), "login" => $row["login"]);
5010  }
5011  }
5012  }
5013  }
5014  return $persons_array;
5015  }
5016 
5024  {
5025  global $ilDB;
5026 
5027  $result = $ilDB->queryF("SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s AND submitted = %s",
5028  array('integer', 'integer'),
5029  array($this->getTestId(), 1)
5030  );
5031  $row = $ilDB->fetchAssoc($result);
5032  return $row["total"];
5033  }
5034 
5041  function &getQuestionsOfTest($active_id)
5042  {
5043  global $ilDB;
5044  if ($this->isRandomTest())
5045  {
5046  $ilDB->setLimit($this->getQuestionCount(), 0);
5047  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " .
5048  "tst_test_rnd_qst.pass, qpl_questions.points " .
5049  "FROM tst_test_rnd_qst, qpl_questions " .
5050  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
5051  "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence",
5052  array('integer'),
5053  array($active_id)
5054  );
5055  }
5056  else
5057  {
5058  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
5059  "qpl_questions.points " .
5060  "FROM tst_test_question, tst_active, qpl_questions " .
5061  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
5062  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi",
5063  array('integer'),
5064  array($active_id)
5065  );
5066  }
5067  $qtest = array();
5068  if ($result->numRows())
5069  {
5070  while ($row = $ilDB->fetchAssoc($result))
5071  {
5072  array_push($qtest, $row);
5073  }
5074  }
5075  return $qtest;
5076  }
5077 
5084  function &getQuestionsOfPass($active_id, $pass)
5085  {
5086  global $ilDB;
5087  if ($this->isRandomTest())
5088  {
5089  $ilDB->setLimit($this->getQuestionCount(), 0);
5090  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " .
5091  "qpl_questions.points " .
5092  "FROM tst_test_rnd_qst, qpl_questions " .
5093  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
5094  "AND tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.pass = %s " .
5095  "ORDER BY tst_test_rnd_qst.sequence",
5096  array('integer', 'integer'),
5097  array($active_id, $pass)
5098  );
5099  }
5100  else
5101  {
5102  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
5103  "qpl_questions.points " .
5104  "FROM tst_test_question, tst_active, qpl_questions " .
5105  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
5106  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi",
5107  array('integer'),
5108  array($active_id)
5109  );
5110  }
5111  $qpass = array();
5112  if ($result->numRows())
5113  {
5114  while ($row = $ilDB->fetchAssoc($result))
5115  {
5116  array_push($qpass, $row);
5117  }
5118  }
5119  return $qpass;
5120  }
5121 
5123  {
5124  global $ilDB;
5125 
5126  include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php";
5127  include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php";
5128  include_once "./Modules/Test/classes/class.ilTestEvaluationData.php";
5129 
5130  $data = new ilTestEvaluationData($this);
5131 
5132  $query = "
5133  SELECT tst_test_result.*,
5134  qpl_questions.original_id,
5135  qpl_questions.title questiontitle,
5136  qpl_questions.points maxpoints
5137 
5138  FROM tst_test_result, qpl_questions, tst_active
5139 
5140  WHERE tst_active.active_id = tst_test_result.active_fi
5141  AND qpl_questions.question_id = tst_test_result.question_fi
5142  AND tst_active.test_fi = %s
5143 
5144  ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp
5145  ";
5146 
5147  $result = $ilDB->queryF(
5148  $query, array('integer'), array($this->getTestId())
5149  );
5150 
5151  $pass = NULL;
5152  $checked = array();
5153  $datasets = 0;
5154 
5155  while( $row = $ilDB->fetchAssoc($result) )
5156  {
5157  $participantObject = $data->getParticipant($row["active_fi"]);
5158 
5159  if( !($participantObject instanceof ilTestEvaluationUserData) )
5160  {
5161  continue;
5162  }
5163 
5164  $passObject = $participantObject->getPass($row["pass"]);
5165 
5166  if( !($passObject instanceof ilTestEvaluationPassData) )
5167  {
5168  continue;
5169  }
5170 
5171  $passObject->addAnsweredQuestion(
5172  $row["question_fi"], $row["maxpoints"], $row["points"], $row['answered']
5173  );
5174  }
5175 
5176  foreach( array_keys($data->getParticipants()) as $active_id )
5177  {
5178  if( $this->isRandomTest() )
5179  {
5180  for( $testpass = 0; $testpass <= $data->getParticipant($active_id)->getLastPass(); $testpass++ )
5181  {
5182  $ilDB->setLimit($this->getQuestionCount(), 0);
5183  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, qpl_questions.original_id, " .
5184  "tst_test_rnd_qst.pass, qpl_questions.points, qpl_questions.title " .
5185  "FROM tst_test_rnd_qst, qpl_questions " .
5186  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
5187  "AND tst_test_rnd_qst.pass = %s " .
5188  "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence",
5189  array('integer','integer'),
5190  array($testpass, $active_id)
5191  );
5192  if ($result->numRows())
5193  {
5194  while ($row = $ilDB->fetchAssoc($result))
5195  {
5196  $tpass = array_key_exists("pass", $row) ? $row["pass"] : 0;
5197  $data->getParticipant($active_id)->addQuestion($row["original_id"], $row["question_fi"], $row["points"], $row["sequence"], $tpass);
5198  $data->addQuestionTitle($row["question_fi"], $row["title"]);
5199  }
5200  }
5201  }
5202  }
5203  else
5204  {
5205  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
5206  "qpl_questions.points, qpl_questions.title, qpl_questions.original_id " .
5207  "FROM tst_test_question, tst_active, qpl_questions " .
5208  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
5209  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi ORDER BY tst_test_question.sequence",
5210  array('integer'),
5211  array($active_id)
5212  );
5213  if ($result->numRows())
5214  {
5215  $questionsbysequence = array();
5216 
5217  while ($row = $ilDB->fetchAssoc($result))
5218  {
5219  $questionsbysequence[$row["sequence"]] = $row;
5220  }
5221 
5222  $seqresult = $ilDB->queryF("SELECT * FROM tst_sequence WHERE active_fi = %s",
5223  array('integer'),
5224  array($active_id)
5225  );
5226 
5227  while ($seqrow = $ilDB->fetchAssoc($seqresult))
5228  {
5229  $questionsequence = unserialize($seqrow["sequence"]);
5230  foreach ($questionsequence as $sidx => $seq)
5231  {
5232  $data->getParticipant($active_id)->addQuestion($questionsbysequence[$seq]["original_id"], $questionsbysequence[$seq]["question_fi"], $questionsbysequence[$seq]["points"], $sidx + 1, $seqrow["pass"]);
5233  $data->addQuestionTitle($questionsbysequence[$seq]["question_fi"], $questionsbysequence[$seq]["title"]);
5234  }
5235  }
5236  }
5237  }
5238  }
5239 
5240  if ($this->ects_output)
5241  {
5242  $passed_array =& $this->getTotalPointsPassedArray();
5243  }
5244 
5245  foreach( array_keys($data->getParticipants()) as $active_id )
5246  {
5247  $tstUserData = $data->getParticipant($active_id);
5248 
5249  $percentage = $tstUserData->getReachedPointsInPercent();
5250 
5251  $obligationsAnswered = $tstUserData->areObligationsAnswered();
5252 
5253  $mark = $this->mark_schema->getMatchingMark($percentage);
5254 
5255  if (is_object($mark))
5256  {
5257  $tstUserData->setMark($mark->getShortName());
5258  $tstUserData->setMarkOfficial($mark->getOfficialName());
5259 
5260  $tstUserData->setPassed(
5261  $mark->getPassed() && $tstUserData->areObligationsAnswered()
5262  );
5263  }
5264 
5265  if ($this->ects_output)
5266  {
5267  $ects_mark = $this->getECTSGrade(
5268  $passed_array, $tstUserData->getReached(), $tstUserData->getMaxPoints()
5269  );
5270 
5271  $tstUserData->setECTSMark($ects_mark);
5272  }
5273 
5274  $visitingTime =& $this->getVisitTimeOfParticipant($active_id);
5275 
5276  $tstUserData->setFirstVisit($visitingTime["firstvisit"]);
5277  $tstUserData->setLastVisit($visitingTime["lastvisit"]);
5278  }
5279 
5280  return $data;
5281  }
5282 
5284  {
5285  global $ilDB;
5286  $random = ilObjTest::_lookupRandomTestFromActiveId($active_id);
5287  if ($random)
5288  {
5289  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.pass, COUNT(tst_test_rnd_qst.question_fi) qcount, " .
5290  "SUM(qpl_questions.points) qsum FROM tst_test_rnd_qst, qpl_questions " .
5291  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id AND " .
5292  "tst_test_rnd_qst.active_fi = %s and pass = %s GROUP BY tst_test_rnd_qst.active_fi, " .
5293  "tst_test_rnd_qst.pass",
5294  array('integer', 'integer'),
5295  array($active_id, $pass)
5296  );
5297  }
5298  else
5299  {
5300  $result = $ilDB->queryF("SELECT COUNT(tst_test_question.question_fi) qcount, " .
5301  "SUM(qpl_questions.points) qsum FROM tst_test_question, qpl_questions, tst_active " .
5302  "WHERE tst_test_question.question_fi = qpl_questions.question_id AND tst_test_question.test_fi = tst_active.test_fi AND " .
5303  "tst_active.active_id = %s GROUP BY tst_test_question.test_fi",
5304  array('integer'),
5305  array($active_id)
5306  );
5307  }
5308  if ($result->numRows())
5309  {
5310  $row = $ilDB->fetchAssoc($result);
5311  return array("count" => $row["qcount"], "points" => $row["qsum"]);
5312  }
5313  else
5314  {
5315  return array("count" => 0, "points" => 0);
5316  }
5317  }
5318 
5319  function &getCompleteEvaluationData($withStatistics = TRUE, $filterby = "", $filtertext = "")
5320  {
5321  include_once "./Modules/Test/classes/class.ilTestEvaluationData.php";
5322  include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php";
5323  include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php";
5325  if ($withStatistics)
5326  {
5327  $data->calculateStatistics();
5328  }
5329  $data->setFilter($filterby, $filtertext);
5330  return $data;
5331  }
5332 
5340  {
5341  return $this->_evalResultsOverview($this->getTestId());
5342  }
5343 
5351  {
5352  global $ilDB;
5353 
5354  $result = $ilDB->queryF("SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " .
5355  "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
5356  "qpl_questions.points maxpoints " .
5357  "FROM tst_test_result, qpl_questions, tst_active " .
5358  "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " .
5359  "WHERE tst_active.active_id = tst_test_result.active_fi " .
5360  "AND qpl_questions.question_id = tst_test_result.question_fi " .
5361  "AND tst_active.test_fi = %s " .
5362  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
5363  array('integer'),
5364  array($test_id)
5365  );
5366  $overview = array();
5367  while ($row = $ilDB->fetchAssoc($result))
5368  {
5369  if (!array_key_exists($row["active_fi"], $overview))
5370  {
5371  $overview[$row["active_fi"]] = array();
5372  $overview[$row["active_fi"]]["firstname"] = $row["firstname"];
5373  $overview[$row["active_fi"]]["lastname"] = $row["lastname"];
5374  $overview[$row["active_fi"]]["title"] = $row["title"];
5375  $overview[$row["active_fi"]]["login"] = $row["login"];
5376  $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"];
5377  $overview[$row["active_fi"]]["started"] = $row["started"];
5378  $overview[$row["active_fi"]]["finished"] = $row["finished"];
5379  }
5380  if (!array_key_exists($row["pass"], $overview[$row["active_fi"]]))
5381  {
5382  $overview[$row["active_fi"]][$row["pass"]] = array();
5383  $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0;
5384  $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"];
5385  }
5386  array_push($overview[$row["active_fi"]][$row["pass"]], $row);
5387  $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"];
5388  }
5389  return $overview;
5390  }
5391 
5399  function &evalResultsOverviewOfParticipant($active_id)
5400  {
5401  global $ilDB;
5402 
5403  $result = $ilDB->queryF("SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " .
5404  "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
5405  "qpl_questions.points maxpoints " .
5406  "FROM tst_test_result, qpl_questions, tst_active " .
5407  "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " .
5408  "WHERE tst_active.active_id = tst_test_result.active_fi " .
5409  "AND qpl_questions.question_id = tst_test_result.question_fi " .
5410  "AND tst_active.test_fi = %s AND tst_active.active_id = %s" .
5411  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
5412  array('integer', 'integer'),
5413  array($this->getTestId(), $active_id)
5414  );
5415  $overview = array();
5416  while ($row = $ilDB->fetchAssoc($result))
5417  {
5418  if (!array_key_exists($row["active_fi"], $overview))
5419  {
5420  $overview[$row["active_fi"]] = array();
5421  $overview[$row["active_fi"]]["firstname"] = $row["firstname"];
5422  $overview[$row["active_fi"]]["lastname"] = $row["lastname"];
5423  $overview[$row["active_fi"]]["title"] = $row["title"];
5424  $overview[$row["active_fi"]]["login"] = $row["login"];
5425  $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"];
5426  $overview[$row["active_fi"]]["started"] = $row["started"];
5427  $overview[$row["active_fi"]]["finished"] = $row["finished"];
5428  }
5429  if (!array_key_exists($row["pass"], $overview[$row["active_fi"]]))
5430  {
5431  $overview[$row["active_fi"]][$row["pass"]] = array();
5432  $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0;
5433  $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"];
5434  }
5435  array_push($overview[$row["active_fi"]][$row["pass"]], $row);
5436  $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"];
5437  }
5438  return $overview;
5439  }
5440 
5452  function buildName($user_id, $firstname, $lastname, $title)
5453  {
5454  $name = "";
5455  if (strlen($firstname.$lastname.$title) == 0)
5456  {
5457  $name = $this->lng->txt("deleted_user");
5458  }
5459  else
5460  {
5461  if ($user_id == ANONYMOUS_USER_ID)
5462  {
5463  $name = $lastname;
5464  }
5465  else
5466  {
5467  $name = trim($lastname . ", " . $firstname . " " . $title);
5468  }
5469  if ($this->getAnonymity())
5470  {
5471  $name = $this->lng->txt("anonymous");
5472  }
5473  }
5474  return $name;
5475  }
5476 
5489  function _buildName($is_anonymous, $user_id, $firstname, $lastname, $title)
5490  {
5491  global $lng;
5492  $name = "";
5493  if (strlen($firstname.$lastname.$title) == 0)
5494  {
5495  $name = $lng->txt("deleted_user");
5496  }
5497  else
5498  {
5499  if ($user_id == ANONYMOUS_USER_ID)
5500  {
5501  $name = $lastname;
5502  }
5503  else
5504  {
5505  $name = trim($lastname . ", " . $firstname . " " . $title);
5506  }
5507  if ($is_anonymous)
5508  {
5509  $name = $lng->txt("anonymous");
5510  }
5511  }
5512  return $name;
5513  }
5514 
5522  {
5523  global $ilDB;
5524 
5525  $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",
5526  array('integer'),
5527  array($this->getTestId())
5528  );
5529  $times = array();
5530  while ($row = $ilDB->fetchObject($result))
5531  {
5532  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
5533  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5534  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
5535  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5536  $times[$row->active_fi] += ($epoch_2 - $epoch_1);
5537  }
5538  $max_time = 0;
5539  $counter = 0;
5540  foreach ($times as $key => $value)
5541  {
5542  $max_time += $value;
5543  $counter++;
5544  }
5545  if ($counter)
5546  {
5547  $average_time = round($max_time / $counter);
5548  }
5549  else
5550  {
5551  $average_time = 0;
5552  }
5553  return $average_time;
5554  }
5555 
5562  function &getAvailableQuestionpools($use_object_id = false, $equal_points = false, $could_be_offline = false, $show_path = FALSE, $with_questioncount = FALSE, $permission = "read")
5563  {
5564  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5565  return ilObjQuestionPool::_getAvailableQuestionpools($use_object_id, $equal_points, $could_be_offline, $show_path, $with_questioncount, $permission);
5566  }
5567 
5575  {
5576  $time_in_seconds = 0;
5577  foreach ($this->questions as $question_id)
5578  {
5579  $question =& ilObjTest::_instanciateQuestion($question_id);
5580  $est_time = $question->getEstimatedWorkingTime();
5581  $time_in_seconds += $est_time["h"] * 3600 + $est_time["m"] * 60 + $est_time["s"];
5582  }
5583  $hours = (int)($time_in_seconds / 3600) ;
5584  $time_in_seconds = $time_in_seconds - ($hours * 3600);
5585  $minutes = (int)($time_in_seconds / 60);
5586  $time_in_seconds = $time_in_seconds - ($minutes * 60);
5587  $result = array("hh" => $hours, "mm" => $minutes, "ss" => $time_in_seconds);
5588  return $result;
5589  }
5590 
5599  public function generateRandomPass($nr, $qpls, $pass = NULL)
5600  {
5601  global $ilDB;
5602  $qplids = array();
5603  foreach ($qpls as $arr) array_push($qplids, $arr['qpl']);
5604  $result = $ilDB->queryF('SELECT * FROM tst_rnd_cpy WHERE tst_fi = %s AND ' . $ilDB->in('qpl_fi', $qplids, false, 'integer'),
5605  array('integer'),
5606  array($this->getTestId())
5607  );
5608  if ($result->numRows())
5609  {
5610  $ids = array();
5611  while ($row = $ilDB->fetchAssoc($result)) array_push($ids, $row['qst_fi']);
5612  $nr = ($nr > count($ids)) ? count($ids) : $nr;
5613  if ($nr == 0) return array();
5614  $rand_keys = array_rand($ids, $nr);
5615  $selection = array();
5616  if (is_array($rand_keys))
5617  {
5618  foreach ($rand_keys as $key)
5619  {
5620  $selection[$ids[$key]] = $ids[$key];
5621  }
5622  }
5623  else
5624  {
5625  $selection[$ids[$rand_keys]] = $ids[$rand_keys];
5626  }
5627  return $selection;
5628  }
5629  else
5630  {
5631  // old style random questions
5632  return $this->randomSelectQuestions($nr, 0, 1, $qplids, $pass);
5633  }
5634  }
5635 
5646  function randomSelectQuestions($nr_of_questions, $questionpool, $use_obj_id = 0, $qpls = "", $pass = NULL)
5647  {
5648  global $rbacsystem;
5649  global $ilDB;
5650 
5651  // retrieve object id instead of ref id if necessary
5652  if (($questionpool != 0) && (!$use_obj_id)) $questionpool = ilObject::_lookupObjId($questionpool);
5653 
5654  // get original ids of all existing questions in the test
5655  $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",
5656  array("integer"),
5657  array($this->getTestId())
5658  );
5659  $original_ids = array();
5660  $paramtypes = array();
5661  $paramvalues = array();
5662  while ($row = $ilDB->fetchAssoc($result))
5663  {
5664  array_push($original_ids, $row['original_id']);
5665  }
5666 
5667  $available = "";
5668  // get a list of all available questionpools
5669  if (($questionpool == 0) && (!is_array($qpls)))
5670  {
5671  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5672  $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())));
5673  if (count($available_pools))
5674  {
5675  $available = " AND " . $ilDB->in('obj_fi', $available_pools, false, 'integer');
5676  }
5677  else
5678  {
5679  return array();
5680  }
5681  }
5682 
5683  $constraint_qpls = "";
5684  $result_array = array();
5685  if ($questionpool == 0)
5686  {
5687  if (is_array($qpls))
5688  {
5689  if (count($qpls) > 0)
5690  {
5691  $constraint_qpls = " AND " . $ilDB->in('obj_fi', $qpls, false, 'integer');
5692  }
5693  }
5694  }
5695 
5696  $original_clause = "";
5697  if (count($original_ids))
5698  {
5699  $original_clause = " AND " . $ilDB->in('question_id', $original_ids, true, 'integer');
5700  }
5701 
5702  if ($questionpool == 0)
5703  {
5704  $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",
5705  array('integer', 'text'),
5706  array(0, "1")
5707  );
5708  }
5709  else
5710  {
5711  $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",
5712  array('integer','integer', 'text'),
5713  array($questionpool, 0, "1")
5714  );
5715  }
5716  $found_ids = array();
5717  while ($row = $ilDB->fetchAssoc($result))
5718  {
5719  array_push($found_ids, $row['question_id']);
5720  }
5721  $nr_of_questions = ($nr_of_questions > count($found_ids)) ? count($found_ids) : $nr_of_questions;
5722  if ($nr_of_questions == 0) return array();
5723  $rand_keys = array_rand($found_ids, $nr_of_questions);
5724  $result = array();
5725  if (is_array($rand_keys))
5726  {
5727  foreach ($rand_keys as $key)
5728  {
5729  $result[$found_ids[$key]] = $found_ids[$key];
5730  }
5731  }
5732  else
5733  {
5734  $result[$found_ids[$rand_keys]] = $found_ids[$rand_keys];
5735  }
5736  return $result;
5737  }
5738 
5745  function getImagePath()
5746  {
5747  return CLIENT_WEB_DIR . "/assessment/" . $this->getId() . "/images/";
5748  }
5749 
5756  function getImagePathWeb()
5757  {
5758  include_once "./Services/Utilities/classes/class.ilUtil.php";
5759  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/" . $this->getId() . "/images/";
5761  }
5762 
5771  function &createQuestionGUI($question_type, $question_id = -1)
5772  {
5773  if ((!$question_type) and ($question_id > 0))
5774  {
5775  $question_type = $this->getQuestionType($question_id);
5776  }
5777 
5778  if (!strlen($question_type)) return null;
5779 
5780  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
5781  assQuestion::_includeClass($question_type, 1);
5782 
5783  $question_type_gui = $question_type . "GUI";
5784  $question = new $question_type_gui();
5785 
5786  $question->object->setObligationsToBeConsidered( $this->areObligationsEnabled() );
5787 
5788  if ($question_id > 0)
5789  {
5790  $question->object->loadFromDb($question_id);
5791  }
5792 
5793  return $question;
5794  }
5795 
5803  function &_instanciateQuestion($question_id)
5804  {
5805  if (strcmp($question_id, "") != 0)
5806  {
5807  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
5808  return assQuestion::_instanciateQuestion($question_id);
5809  }
5810  }
5811 
5820  function moveQuestions($move_questions, $target_index, $insert_mode)
5821  {
5822  $this->questions = array_values($this->questions);
5823  $array_pos = array_search($target_index, $this->questions);
5824  if ($insert_mode == 0)
5825  {
5826  $part1 = array_slice($this->questions, 0, $array_pos);
5827  $part2 = array_slice($this->questions, $array_pos);
5828  }
5829  else if ($insert_mode == 1)
5830  {
5831  $part1 = array_slice($this->questions, 0, $array_pos + 1);
5832  $part2 = array_slice($this->questions, $array_pos + 1);
5833  }
5834  foreach ($move_questions as $question_id)
5835  {
5836  if (!(array_search($question_id, $part1) === FALSE))
5837  {
5838  unset($part1[array_search($question_id, $part1)]);
5839  }
5840  if (!(array_search($question_id, $part2) === FALSE))
5841  {
5842  unset($part2[array_search($question_id, $part2)]);
5843  }
5844  }
5845  $part1 = array_values($part1);
5846  $part2 = array_values($part2);
5847  $new_array = array_values(array_merge($part1, $move_questions, $part2));
5848  $this->questions = array();
5849  $counter = 1;
5850  foreach ($new_array as $question_id)
5851  {
5852  $this->questions[$counter] = $question_id;
5853  $counter++;
5854  }
5855  $this->saveQuestionsToDb();
5856  }
5857 
5858 
5867  {
5868  if ($this->getStartingTime())
5869  {
5870  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getStartingTime(), $matches))
5871  {
5872  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5873  $now = mktime();
5874  if ($now < $epoch_time)
5875  {
5876  // starting time not reached
5877  return false;
5878  }
5879  }
5880  }
5881  return true;
5882  }
5883 
5892  {
5893  if ($this->getEndingTime())
5894  {
5895  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndingTime(), $matches))
5896  {
5897  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5898  $now = mktime();
5899  if ($now > $epoch_time)
5900  {
5901  // ending time reached
5902  return true;
5903  }
5904  }
5905  }
5906  return false;
5907  }
5908 
5914  function getAvailableQuestions($arrFilter, $completeonly = 0)
5915  {
5916  global $ilUser;
5917  global $ilDB;
5918 
5919  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5920  $available_pools = array_keys(ilObjQuestionPool::_getAvailableQuestionpools($use_object_id = TRUE, $equal_points = FALSE, $could_be_offline = FALSE, $showPath = FALSE, $with_questioncount = FALSE));
5921  $available = "";
5922  if (count($available_pools))
5923  {
5924  $available = " AND " . $ilDB->in('qpl_questions.obj_fi', $available_pools, false, 'integer');
5925  }
5926  else
5927  {
5928  return array();
5929  }
5930  if ($completeonly)
5931  {
5932  $available .= " AND qpl_questions.complete = " . $ilDB->quote("1", 'text');
5933  }
5934 
5935  $where = "";
5936  if (is_array($arrFilter))
5937  {
5938  if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title']))
5939  {
5940  $where .= " AND " . $ilDB->like('qpl_questions.title', 'text', "%%" . $arrFilter['title'] . "%%");
5941  }
5942  if (array_key_exists('description', $arrFilter) && strlen($arrFilter['description']))
5943  {
5944  $where .= " AND " . $ilDB->like('qpl_questions.description', 'text', "%%" . $arrFilter['description'] . "%%");
5945  }
5946  if (array_key_exists('author', $arrFilter) && strlen($arrFilter['author']))
5947  {
5948  $where .= " AND " . $ilDB->like('qpl_questions.author', 'text', "%%" . $arrFilter['author'] . "%%");
5949  }
5950  if (array_key_exists('type', $arrFilter) && strlen($arrFilter['type']))
5951  {
5952  $where .= " AND qpl_qst_type.type_tag = " . $ilDB->quote($arrFilter['type'], 'text');
5953  }
5954  if (array_key_exists('qpl', $arrFilter) && strlen($arrFilter['qpl']))
5955  {
5956  $where .= " AND " . $ilDB->like('object_data.title', 'text', "%%" . $arrFilter['qpl'] . "%%");
5957  }
5958  }
5959 
5960  $original_ids =& $this->getExistingQuestions();
5961  $original_clause = " qpl_questions.original_id IS NULL";
5962  if (count($original_ids))
5963  {
5964  $original_clause = " qpl_questions.original_id IS NULL AND " . $ilDB->in('qpl_questions.question_id', $original_ids, true, 'integer');
5965  }
5966 
5967  $query_result = $ilDB->query(
5968  "SELECT qpl_questions.*, qpl_questions.tstamp, qpl_qst_type.type_tag, qpl_qst_type.plugin, object_data.title qpl " .
5969  "FROM qpl_questions, qpl_qst_type, object_data WHERE $original_clause $available AND " .
5970  "object_data.obj_id = qpl_questions.obj_fi AND qpl_questions.tstamp > 0 AND " .
5971  "qpl_questions.question_type_fi = qpl_qst_type.question_type_id$where");
5972  $rows = array();
5973  $types = $this->getQuestionTypeTranslations();
5974  if ($query_result->numRows())
5975  {
5976  while ($row = $ilDB->fetchAssoc($query_result))
5977  {
5978  $row['ttype'] = $types[$row['type_tag']];
5979  if ($row["plugin"])
5980  {
5981  if ($this->isPluginActive($row["type_tag"]))
5982  {
5983  array_push($rows, $row);
5984  }
5985  }
5986  else
5987  {
5988  array_push($rows, $row);
5989  }
5990  }
5991  }
5992  return $rows;
5993  }
5994 
5995  public function &getQuestionTypeTranslations()
5996  {
5997  global $ilDB;
5998  global $lng;
5999  global $ilLog;
6000  global $ilPluginAdmin;
6001 
6002  $lng->loadLanguageModule("assessment");
6003  $result = $ilDB->query("SELECT * FROM qpl_qst_type");
6004  $types = array();
6005  while ($row = $ilDB->fetchAssoc($result))
6006  {
6007  if ($row["plugin"] == 0)
6008  {
6009  $types[$row['type_tag']] = $lng->txt($row["type_tag"]);
6010  }
6011  else
6012  {
6013  $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
6014  foreach ($pl_names as $pl_name)
6015  {
6016  $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
6017  if (strcmp($pl->getQuestionType(), $row["type_tag"]) == 0)
6018  {
6019  $types[$row['type_tag']] = $pl->getQuestionTypeTranslation();
6020  }
6021  }
6022  }
6023  }
6024  ksort($types);
6025  return $types;
6026  }
6027 
6032  public function fromXML(ilQTIAssessment $assessment)
6033  {
6034  unset($_SESSION["import_mob_xhtml"]);
6035 
6036  $this->setDescription($assessment->getComment());
6037  $this->setTitle($assessment->getTitle());
6038 
6039  foreach ($assessment->objectives as $objectives)
6040  {
6041  foreach ($objectives->materials as $material)
6042  {
6043  $this->setIntroduction($this->QTIMaterialToString($material));
6044  }
6045  }
6046 
6047  if(
6048  $assessment->getPresentationMaterial() &&
6049  $assessment->getPresentationMaterial()->getFlowMat(0) &&
6050  $assessment->getPresentationMaterial()->getFlowMat(0)->getMaterial(0)
6051  )
6052  {
6053  $this->setFinalStatement($this->QTIMaterialToString($assessment->getPresentationMaterial()->getFlowMat(0)->getMaterial(0)));
6054  }
6055 
6056  foreach ($assessment->assessmentcontrol as $assessmentcontrol)
6057  {
6058  switch ($assessmentcontrol->getSolutionswitch())
6059  {
6060  case "Yes":
6061  $this->setInstantFeedbackSolution(1);
6062  break;
6063  default:
6064  $this->setInstantFeedbackSolution(0);
6065  break;
6066  }
6067  }
6068 
6069  foreach ($assessment->qtimetadata as $metadata)
6070  {
6071  switch ($metadata["label"])
6072  {
6073  case "test_type":
6074  // for old tests with a test type
6075  $type = $metadata["entry"];
6076  switch ($type)
6077  {
6078  case 1:
6079  // assessment
6080  $this->setAnonymity(1);
6081  break;
6082  case 2:
6083  // self assessment
6084  break;
6085  case 4:
6086  // online exam
6087  $this->setFixedParticipants(1);
6088  $this->setListOfQuestionsSettings(7);
6089  $this->setShowSolutionPrintview(1);
6090  break;
6091  case 5:
6092  // varying random test
6093  break;
6094  }
6095  break;
6096  case "sequence_settings":
6097  $this->setSequenceSettings($metadata["entry"]);
6098  break;
6099  case "author":
6100  $this->setAuthor($metadata["entry"]);
6101  break;
6102  case "nr_of_tries":
6103  $this->setNrOfTries($metadata["entry"]);
6104  break;
6105  case "kiosk":
6106  $this->setKiosk($metadata["entry"]);
6107  break;
6108  case "showfinalstatement":
6109  $this->setShowFinalStatement($metadata["entry"]);
6110  break;
6111  case "showinfo":
6112  $this->setShowInfo($metadata["entry"]);
6113  break;
6114  case "forcejs":
6115  $this->setForceJS($metadata["entry"]);
6116  break;
6117  case "customstyle":
6118  $this->setCustomStyle($metadata["entry"]);
6119  break;
6120 
6121  case "highscore_enabled":
6122  $this->setHighscoreEnabled($metadata["entry"]);
6123  break;
6124 
6125  case "highscore_anon":
6126  $this->setHighscoreAnon($metadata["entry"]);
6127  break;
6128 
6129  case "highscore_achieved_ts":
6130  $this->setHighscoreAchievedTS($metadata["entry"]);
6131  break;
6132 
6133  case "highscore_score":
6134  $this->setHighscoreScore($metadata["entry"]);
6135  break;
6136 
6137  case "highscore_percentage":
6138  $this->setHighscorePercentage($metadata["entry"]);
6139  break;
6140 
6141  case "highscore_hints":
6142  $this->setHighscoreHints($metadata["entry"]);
6143  break;
6144 
6145  case "highscore_wtime":
6146  $this->setHighscoreWTime($metadata["entry"]);
6147  break;
6148 
6149  case "highscore_own_table":
6150  $this->setHighscoreOwnTable($metadata["entry"]);
6151  break;
6152 
6153  case "highscore_top_table":
6154  $this->setHighscoreTopTable($metadata["entry"]);
6155  break;
6156 
6157  case "highscore_top_num":
6158  $this->setHighscoreTopNum($metadata["entry"]);
6159  break;
6160 
6161  case "hide_previous_results":
6162  if ($metadata["entry"] == 0)
6163  {
6164  $this->setUsePreviousAnswers(1);
6165  }
6166  else
6167  {
6168  $this->setUsePreviousAnswers(0);
6169  }
6170  break;
6171  case "use_previous_answers":
6172  $this->setUsePreviousAnswers($metadata["entry"]);
6173  break;
6174  case "answer_feedback":
6175  $this->setAnswerFeedback($metadata["entry"]);
6176  break;
6177  case "hide_title_points":
6178  $this->setTitleOutput($metadata["entry"]);
6179  break;
6180  case "title_output":
6181  $this->setTitleOutput($metadata["entry"]);
6182  break;
6183  case "random_test":
6184  $this->setRandomTest($metadata["entry"]);
6185  break;
6186  case "random_question_count":
6187  $this->setRandomQuestionCount($metadata["entry"]);
6188  break;
6189  case "results_presentation":
6190  $this->setResultsPresentation($metadata["entry"]);
6191  break;
6192  case "reset_processing_time":
6193  $this->setResetProcessingTime($metadata["entry"]);
6194  break;
6195  case "instant_verification":
6196  $this->setInstantFeedbackSolution($metadata["entry"]);
6197  break;
6198  case "answer_feedback_points":
6199  $this->setAnswerFeedbackPoints($metadata["entry"]);
6200  break;
6201  case "anonymity":
6202  $this->setAnonymity($metadata["entry"]);
6203  break;
6204  case "show_cancel":
6205  $this->setShowCancel($metadata["entry"]);
6206  break;
6207  case "show_marker":
6208  $this->setShowMarker($metadata["entry"]);
6209  break;
6210  case "fixed_participants":
6211  $this->setFixedParticipants($metadata["entry"]);
6212  break;
6213  case "score_reporting":
6214  $this->setScoreReporting($metadata["entry"]);
6215  break;
6216  case "shuffle_questions":
6217  $this->setShuffleQuestions($metadata["entry"]);
6218  break;
6219  case "count_system":
6220  $this->setCountSystem($metadata["entry"]);
6221  break;
6222  case "mc_scoring":
6223  $this->setMCScoring($metadata["entry"]);
6224  break;
6225  case "mailnotification":
6226  $this->setMailNotification($metadata["entry"]);
6227  break;
6228  case "mailnottype":
6229  $this->setMailNotificationType($metadata["entry"]);
6230  break;
6231  case "exportsettings":
6232  $this->setExportSettings($metadata['entry']);
6233  break;
6234  case "score_cutting":
6235  $this->setScoreCutting($metadata["entry"]);
6236  break;
6237  case "password":
6238  $this->setPassword($metadata["entry"]);
6239  break;
6240  case "allowedUsers":
6241  $this->setAllowedUsers($metadata["entry"]);
6242  break;
6243  case "allowedUsersTimeGap":
6244  $this->setAllowedUsersTimeGap($metadata["entry"]);
6245  break;
6246  case "pass_scoring":
6247  $this->setPassScoring($metadata["entry"]);
6248  break;
6249  case "show_summary":
6250  $this->setListOfQuestionsSettings($metadata["entry"]);
6251  break;
6252  case "reporting_date":
6253  $iso8601period = $metadata["entry"];
6254  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
6255  {
6256  $this->setReportingDate(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
6257  }
6258  break;
6259  case 'enable_processing_time':
6260  $this->setEnableProcessingTime($metadata['entry']);
6261  break;
6262  case "processing_time":
6263  $this->setProcessingTime($metadata['entry']);
6264  break;
6265  case "starting_time":
6266  $iso8601period = $metadata["entry"];
6267  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
6268  {
6269  $this->setStartingTime(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
6270  }
6271  break;
6272  case "ending_time":
6273  $iso8601period = $metadata["entry"];
6274  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
6275  {
6276  $this->setEndingTime(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
6277  }
6278  break;
6279  case 'activation_limited':
6280  $this->setActivationLimited($metadata['entry']);
6281  break;
6282  case 'activation_start_time':
6283  $this->setActivationStartingTime($metadata['entry']);
6284  break;
6285  case 'activation_end_time':
6286  $this->setActivationEndingTime($metadata['entry']);
6287  break;
6288  case 'activation_visibility':
6289  $this->setActivationVisibility($metadata['entry']);
6290  break;
6291  case 'autosave':
6292  $this->setAutosave($metadata['entry']);
6293  break;
6294  case 'autosave_ival':
6295  $this->setAutosaveIval($metadata['entry']);
6296  break;
6297  case 'offer_question_hints':
6298  $this->setOfferingQuestionHintsEnabled($metadata['entry']);
6299  break;
6300  case 'instant_feedback_specific':
6301  $this->setSpecificAnswerFeedback($metadata['entry']);
6302  break;
6303  case 'obligations_enabled':
6304  $this->setObligationsEnabled($metadata['entry']);
6305  break;
6306  }
6307  if (preg_match("/mark_step_\d+/", $metadata["label"]))
6308  {
6309  $xmlmark = $metadata["entry"];
6310  preg_match("/<short>(.*?)<\/short>/", $xmlmark, $matches);
6311  $mark_short = $matches[1];
6312  preg_match("/<official>(.*?)<\/official>/", $xmlmark, $matches);
6313  $mark_official = $matches[1];
6314  preg_match("/<percentage>(.*?)<\/percentage>/", $xmlmark, $matches);
6315  $mark_percentage = $matches[1];
6316  preg_match("/<passed>(.*?)<\/passed>/", $xmlmark, $matches);
6317  $mark_passed = $matches[1];
6318  $this->mark_schema->addMarkStep($mark_short, $mark_official, $mark_percentage, $mark_passed);
6319  }
6320  }
6321  // handle the import of media objects in XHTML code
6322  if (is_array($_SESSION["import_mob_xhtml"]))
6323  {
6324  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
6325  include_once "./Services/RTE/classes/class.ilRTE.php";
6326  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
6327  foreach ($_SESSION["import_mob_xhtml"] as $mob)
6328  {
6329  $importfile = ilObjTest::_getImportDirectory() . '/' . $mob["uri"];
6330  if (file_exists($importfile))
6331  {
6332  $media_object =& ilObjMediaObject::_saveTempFileAsMediaObject(basename($importfile), $importfile, FALSE);
6333  ilObjMediaObject::_saveUsage($media_object->getId(), "tst:html", $this->getId());
6334  $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getIntroduction()), 1));
6335  $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getFinalStatement()), 1));
6336  }
6337  else
6338  {
6339  global $ilLog;
6340  $ilLog->write("Error: Could not open XHTML mob file for test introduction during test import. File $importfile does not exist!");
6341  }
6342  }
6343  $this->saveToDb();
6344  }
6345  }
6346 
6353  function toXML()
6354  {
6355  include_once("./Services/Xml/classes/class.ilXmlWriter.php");
6356  $a_xml_writer = new ilXmlWriter;
6357  // set xml header
6358  $a_xml_writer->xmlHeader();
6359  $a_xml_writer->xmlSetDtdDef("<!DOCTYPE questestinterop SYSTEM \"ims_qtiasiv1p2p1.dtd\">");
6360  $a_xml_writer->xmlStartTag("questestinterop");
6361 
6362  $attrs = array(
6363  "ident" => "il_".IL_INST_ID."_tst_".$this->getTestId(),
6364  "title" => $this->getTitle()
6365  );
6366  $a_xml_writer->xmlStartTag("assessment", $attrs);
6367  // add qti comment
6368  $a_xml_writer->xmlElement("qticomment", NULL, $this->getDescription());
6369 
6370  // add qti duration
6371  if ($this->enable_processing_time)
6372  {
6373  preg_match("/(\d+):(\d+):(\d+)/", $this->processing_time, $matches);
6374  $a_xml_writer->xmlElement("duration", NULL, sprintf("P0Y0M0DT%dH%dM%dS", $matches[1], $matches[2], $matches[3]));
6375  }
6376 
6377  // add the rest of the preferences in qtimetadata tags, because there is no correspondent definition in QTI
6378  $a_xml_writer->xmlStartTag("qtimetadata");
6379  $a_xml_writer->xmlStartTag("qtimetadatafield");
6380  $a_xml_writer->xmlElement("fieldlabel", NULL, "ILIAS_VERSION");
6381  $a_xml_writer->xmlElement("fieldentry", NULL, $this->ilias->getSetting("ilias_version"));
6382  $a_xml_writer->xmlEndTag("qtimetadatafield");
6383 
6384  // anonymity
6385  $a_xml_writer->xmlStartTag("qtimetadatafield");
6386  $a_xml_writer->xmlElement("fieldlabel", NULL, "anonymity");
6387  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnonymity()));
6388  $a_xml_writer->xmlEndTag("qtimetadatafield");
6389 
6390  // random test
6391  $a_xml_writer->xmlStartTag("qtimetadatafield");
6392  $a_xml_writer->xmlElement("fieldlabel", NULL, "random_test");
6393  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->isRandomTest()));
6394  $a_xml_writer->xmlEndTag("qtimetadatafield");
6395 
6396  // sequence settings
6397  $a_xml_writer->xmlStartTag("qtimetadatafield");
6398  $a_xml_writer->xmlElement("fieldlabel", NULL, "sequence_settings");
6399  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getSequenceSettings());
6400  $a_xml_writer->xmlEndTag("qtimetadatafield");
6401 
6402  // author
6403  $a_xml_writer->xmlStartTag("qtimetadatafield");
6404  $a_xml_writer->xmlElement("fieldlabel", NULL, "author");
6405  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAuthor());
6406  $a_xml_writer->xmlEndTag("qtimetadatafield");
6407 
6408  // reset processing time
6409  $a_xml_writer->xmlStartTag("qtimetadatafield");
6410  $a_xml_writer->xmlElement("fieldlabel", NULL, "reset_processing_time");
6411  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getResetProcessingTime());
6412  $a_xml_writer->xmlEndTag("qtimetadatafield");
6413 
6414  // count system
6415  $a_xml_writer->xmlStartTag("qtimetadatafield");
6416  $a_xml_writer->xmlElement("fieldlabel", NULL, "count_system");
6417  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getCountSystem());
6418  $a_xml_writer->xmlEndTag("qtimetadatafield");
6419 
6420  // multiple choice scoring
6421  $a_xml_writer->xmlStartTag("qtimetadatafield");
6422  $a_xml_writer->xmlElement("fieldlabel", NULL, "mc_scoring");
6423  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMCScoring());
6424  $a_xml_writer->xmlEndTag("qtimetadatafield");
6425 
6426  // multiple choice scoring
6427  $a_xml_writer->xmlStartTag("qtimetadatafield");
6428  $a_xml_writer->xmlElement("fieldlabel", NULL, "score_cutting");
6429  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getScoreCutting());
6430  $a_xml_writer->xmlEndTag("qtimetadatafield");
6431 
6432  // multiple choice scoring
6433  $a_xml_writer->xmlStartTag("qtimetadatafield");
6434  $a_xml_writer->xmlElement("fieldlabel", NULL, "password");
6435  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getPassword());
6436  $a_xml_writer->xmlEndTag("qtimetadatafield");
6437 
6438  // allowed users
6439  $a_xml_writer->xmlStartTag("qtimetadatafield");
6440  $a_xml_writer->xmlElement("fieldlabel", NULL, "allowedUsers");
6441  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAllowedUsers());
6442  $a_xml_writer->xmlEndTag("qtimetadatafield");
6443 
6444  // allowed users time gap
6445  $a_xml_writer->xmlStartTag("qtimetadatafield");
6446  $a_xml_writer->xmlElement("fieldlabel", NULL, "allowedUsersTimeGap");
6447  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAllowedUsersTimeGap());
6448  $a_xml_writer->xmlEndTag("qtimetadatafield");
6449 
6450  // pass scoring
6451  $a_xml_writer->xmlStartTag("qtimetadatafield");
6452  $a_xml_writer->xmlElement("fieldlabel", NULL, "pass_scoring");
6453  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getPassScoring());
6454  $a_xml_writer->xmlEndTag("qtimetadatafield");
6455 
6456  // score reporting date
6457  if ($this->getReportingDate())
6458  {
6459  $a_xml_writer->xmlStartTag("qtimetadatafield");
6460  $a_xml_writer->xmlElement("fieldlabel", NULL, "reporting_date");
6461  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->reporting_date, $matches);
6462  $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]));
6463  $a_xml_writer->xmlEndTag("qtimetadatafield");
6464  }
6465  // number of tries
6466  $a_xml_writer->xmlStartTag("qtimetadatafield");
6467  $a_xml_writer->xmlElement("fieldlabel", NULL, "nr_of_tries");
6468  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getNrOfTries()));
6469  $a_xml_writer->xmlEndTag("qtimetadatafield");
6470 
6471  // kiosk
6472  $a_xml_writer->xmlStartTag("qtimetadatafield");
6473  $a_xml_writer->xmlElement("fieldlabel", NULL, "kiosk");
6474  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getKiosk()));
6475  $a_xml_writer->xmlEndTag("qtimetadatafield");
6476 
6477  // use previous answers
6478  $a_xml_writer->xmlStartTag("qtimetadatafield");
6479  $a_xml_writer->xmlElement("fieldlabel", NULL, "use_previous_answers");
6480  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getUsePreviousAnswers());
6481  $a_xml_writer->xmlEndTag("qtimetadatafield");
6482 
6483  // hide title points
6484  $a_xml_writer->xmlStartTag("qtimetadatafield");
6485  $a_xml_writer->xmlElement("fieldlabel", NULL, "title_output");
6486  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getTitleOutput()));
6487  $a_xml_writer->xmlEndTag("qtimetadatafield");
6488 
6489  // random question count
6490  $a_xml_writer->xmlStartTag("qtimetadatafield");
6491  $a_xml_writer->xmlElement("fieldlabel", NULL, "random_question_count");
6492  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getRandomQuestionCount()));
6493  $a_xml_writer->xmlEndTag("qtimetadatafield");
6494 
6495  // results presentation
6496  $a_xml_writer->xmlStartTag("qtimetadatafield");
6497  $a_xml_writer->xmlElement("fieldlabel", NULL, "results_presentation");
6498  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getResultsPresentation()));
6499  $a_xml_writer->xmlEndTag("qtimetadatafield");
6500 
6501  // solution details
6502  $a_xml_writer->xmlStartTag("qtimetadatafield");
6503  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_summary");
6504  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getListOfQuestionsSettings()));
6505  $a_xml_writer->xmlEndTag("qtimetadatafield");
6506 
6507  // solution details
6508  $a_xml_writer->xmlStartTag("qtimetadatafield");
6509  $a_xml_writer->xmlElement("fieldlabel", NULL, "score_reporting");
6510  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getScoreReporting()));
6511  $a_xml_writer->xmlEndTag("qtimetadatafield");
6512 
6513  // solution details
6514  $a_xml_writer->xmlStartTag("qtimetadatafield");
6515  $a_xml_writer->xmlElement("fieldlabel", NULL, "instant_verification");
6516  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getInstantFeedbackSolution()));
6517  $a_xml_writer->xmlEndTag("qtimetadatafield");
6518 
6519  // answer specific feedback
6520  $a_xml_writer->xmlStartTag("qtimetadatafield");
6521  $a_xml_writer->xmlElement("fieldlabel", NULL, "answer_feedback");
6522  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnswerFeedback()));
6523  $a_xml_writer->xmlEndTag("qtimetadatafield");
6524 
6525  // answer specific feedback of reached points
6526  $a_xml_writer->xmlStartTag("qtimetadatafield");
6527  $a_xml_writer->xmlElement("fieldlabel", NULL, "answer_feedback_points");
6528  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnswerFeedbackPoints()));
6529  $a_xml_writer->xmlEndTag("qtimetadatafield");
6530 
6531  // highscore
6532  $highscore_metadata = array(
6533  'highscore_enabled' => array('value' => $this->getHighscoreEnabled()),
6534  'highscore_anon' => array('value' => $this->getHighscoreAnon()),
6535  'highscore_achieved_ts' => array('value' => $this->getHighscoreAchievedTS()),
6536  'highscore_score' => array('value' => $this->getHighscoreScore()),
6537  'highscore_percentage' => array('value' => $this->getHighscorePercentage()),
6538  'highscore_hints' => array('value' => $this->getHighscoreHints()),
6539  'highscore_wtime' => array('value' => $this->getHighscoreWTime()),
6540  'highscore_own_table' => array('value' => $this->getHighscoreOwnTable()),
6541  'highscore_top_table' => array('value' => $this->getHighscoreTopTable()),
6542  'highscore_top_num' => array('value' => $this->getHighscoreTopNum()),
6543  );
6544  foreach($highscore_metadata as $label => $data)
6545  {
6546  $a_xml_writer->xmlStartTag("qtimetadatafield");
6547  $a_xml_writer->xmlElement("fieldlabel", NULL, $label);
6548  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $data['value']));
6549  $a_xml_writer->xmlEndTag("qtimetadatafield");
6550  }
6551 
6552  // show cancel
6553  $a_xml_writer->xmlStartTag("qtimetadatafield");
6554  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_cancel");
6555  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShowCancel()));
6556  $a_xml_writer->xmlEndTag("qtimetadatafield");
6557 
6558  // show marker
6559  $a_xml_writer->xmlStartTag("qtimetadatafield");
6560  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_marker");
6561  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShowMarker()));
6562  $a_xml_writer->xmlEndTag("qtimetadatafield");
6563 
6564  // fixed participants
6565  $a_xml_writer->xmlStartTag("qtimetadatafield");
6566  $a_xml_writer->xmlElement("fieldlabel", NULL, "fixed_participants");
6567  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getFixedParticipants()));
6568  $a_xml_writer->xmlEndTag("qtimetadatafield");
6569 
6570  // show final statement
6571  $a_xml_writer->xmlStartTag("qtimetadatafield");
6572  $a_xml_writer->xmlElement("fieldlabel", NULL, "showfinalstatement");
6573  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getShowFinalStatement()) ? "1" : "0")));
6574  $a_xml_writer->xmlEndTag("qtimetadatafield");
6575 
6576  // show introduction only
6577  $a_xml_writer->xmlStartTag("qtimetadatafield");
6578  $a_xml_writer->xmlElement("fieldlabel", NULL, "showinfo");
6579  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getShowInfo()) ? "1" : "0")));
6580  $a_xml_writer->xmlEndTag("qtimetadatafield");
6581 
6582  // mail notification
6583  $a_xml_writer->xmlStartTag("qtimetadatafield");
6584  $a_xml_writer->xmlElement("fieldlabel", NULL, "mailnotification");
6585  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMailNotification());
6586  $a_xml_writer->xmlEndTag("qtimetadatafield");
6587 
6588  // mail notification type
6589  $a_xml_writer->xmlStartTag("qtimetadatafield");
6590  $a_xml_writer->xmlElement("fieldlabel", NULL, "mailnottype");
6591  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMailNotificationType());
6592  $a_xml_writer->xmlEndTag("qtimetadatafield");
6593 
6594  // export settings
6595  $a_xml_writer->xmlStartTag("qtimetadatafield");
6596  $a_xml_writer->xmlElement("fieldlabel", NULL, "exportsettings");
6597  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->getExportSettings());
6598  $a_xml_writer->xmlEndTag("qtimetadatafield");
6599 
6600  // force JavaScript
6601  $a_xml_writer->xmlStartTag("qtimetadatafield");
6602  $a_xml_writer->xmlElement("fieldlabel", NULL, "forcejs");
6603  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getForceJS()) ? "1" : "0")));
6604  $a_xml_writer->xmlEndTag("qtimetadatafield");
6605 
6606  // custom style
6607  $a_xml_writer->xmlStartTag("qtimetadatafield");
6608  $a_xml_writer->xmlElement("fieldlabel", NULL, "customstyle");
6609  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getCustomStyle());
6610  $a_xml_writer->xmlEndTag("qtimetadatafield");
6611 
6612  // shuffle questions
6613  $a_xml_writer->xmlStartTag("qtimetadatafield");
6614  $a_xml_writer->xmlElement("fieldlabel", NULL, "shuffle_questions");
6615  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShuffleQuestions()));
6616  $a_xml_writer->xmlEndTag("qtimetadatafield");
6617 
6618  // processing time
6619  $a_xml_writer->xmlStartTag("qtimetadatafield");
6620  $a_xml_writer->xmlElement("fieldlabel", NULL, "processing_time");
6621  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->getProcessingTime());
6622  $a_xml_writer->xmlEndTag("qtimetadatafield");
6623 
6624  // starting time
6625  if ($this->getStartingTime())
6626  {
6627  $a_xml_writer->xmlStartTag("qtimetadatafield");
6628  $a_xml_writer->xmlElement("fieldlabel", NULL, "starting_time");
6629  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->starting_time, $matches);
6630  $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]));
6631  $a_xml_writer->xmlEndTag("qtimetadatafield");
6632  }
6633  // ending time
6634  if ($this->getEndingTime())
6635  {
6636  $a_xml_writer->xmlStartTag("qtimetadatafield");
6637  $a_xml_writer->xmlElement("fieldlabel", NULL, "ending_time");
6638  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->ending_time, $matches);
6639  $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]));
6640  $a_xml_writer->xmlEndTag("qtimetadatafield");
6641  }
6642 
6643 
6644  //activation_limited
6645  $a_xml_writer->xmlStartTag("qtimetadatafield");
6646  $a_xml_writer->xmlElement("fieldlabel", NULL, "activation_limited");
6647  $a_xml_writer->xmlElement("fieldentry", NULL,(int)$this->isActivationLimited());
6648  $a_xml_writer->xmlEndTag("qtimetadatafield");
6649 
6650  //activation_start_time
6651  $a_xml_writer->xmlStartTag("qtimetadatafield");
6652  $a_xml_writer->xmlElement("fieldlabel", NULL, "activation_start_time");
6653  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->getActivationStartingTime());
6654  $a_xml_writer->xmlEndTag("qtimetadatafield");
6655 
6656  //activation_end_time
6657  $a_xml_writer->xmlStartTag("qtimetadatafield");
6658  $a_xml_writer->xmlElement("fieldlabel", NULL, "activation_end_time");
6659  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->getActivationEndingTime());
6660  $a_xml_writer->xmlEndTag("qtimetadatafield");
6661 
6662  //activation_visibility
6663  $a_xml_writer->xmlStartTag("qtimetadatafield");
6664  $a_xml_writer->xmlElement("fieldlabel", NULL, "activation_visibility");
6665  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->getActivationVisibility());
6666  $a_xml_writer->xmlEndTag("qtimetadatafield");
6667 
6668  // autosave
6669  $a_xml_writer->xmlStartTag("qtimetadatafield");
6670  $a_xml_writer->xmlElement("fieldlabel", NULL, "autosave");
6671  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->getAutosave());
6672  $a_xml_writer->xmlEndTag("qtimetadatafield");
6673 
6674  // autosave_ival
6675  $a_xml_writer->xmlStartTag("qtimetadatafield");
6676  $a_xml_writer->xmlElement("fieldlabel", NULL, "autosave_ival");
6677  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->getAutosaveIval());
6678  $a_xml_writer->xmlEndTag("qtimetadatafield");
6679 
6680  //offer_question_hints
6681  $a_xml_writer->xmlStartTag("qtimetadatafield");
6682  $a_xml_writer->xmlElement("fieldlabel", NULL, "offer_question_hints");
6683  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->isOfferingQuestionHintsEnabled());
6684  $a_xml_writer->xmlEndTag("qtimetadatafield");
6685 
6686  //instant_feedback_specific
6687  $a_xml_writer->xmlStartTag("qtimetadatafield");
6688  $a_xml_writer->xmlElement("fieldlabel", NULL, "instant_feedback_specific");
6689  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->getSpecificAnswerFeedback());
6690  $a_xml_writer->xmlEndTag("qtimetadatafield");
6691 
6692  //obligations_enabled
6693  $a_xml_writer->xmlStartTag("qtimetadatafield");
6694  $a_xml_writer->xmlElement("fieldlabel", NULL, "obligations_enabled");
6695  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->areObligationsEnabled());
6696  $a_xml_writer->xmlEndTag("qtimetadatafield");
6697 
6698  //enable_processing_time
6699  $a_xml_writer->xmlStartTag("qtimetadatafield");
6700  $a_xml_writer->xmlElement("fieldlabel", NULL, "enable_processing_time");
6701  $a_xml_writer->xmlElement("fieldentry", NULL, (int)$this->getEnableProcessingTime());
6702  $a_xml_writer->xmlEndTag("qtimetadatafield");
6703 
6704  foreach ($this->mark_schema->mark_steps as $index => $mark)
6705  {
6706  // mark steps
6707  $a_xml_writer->xmlStartTag("qtimetadatafield");
6708  $a_xml_writer->xmlElement("fieldlabel", NULL, "mark_step_$index");
6709  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf(
6710  "<short>%s</short><official>%s</official><percentage>%.2f</percentage><passed>%d</passed>",
6711  $mark->getShortName(), $mark->getOfficialName(), $mark->getMinimumLevel(), $mark->getPassed()
6712  ));
6713  $a_xml_writer->xmlEndTag("qtimetadatafield");
6714  }
6715  $a_xml_writer->xmlEndTag("qtimetadata");
6716 
6717  // add qti objectives
6718  $a_xml_writer->xmlStartTag("objectives");
6719  $this->addQTIMaterial($a_xml_writer, $this->getIntroduction());
6720  $a_xml_writer->xmlEndTag("objectives");
6721 
6722  // add qti assessmentcontrol
6723  if ($this->getInstantFeedbackSolution() == 1)
6724  {
6725  $attrs = array(
6726  "solutionswitch" => "Yes"
6727  );
6728  }
6729  else
6730  {
6731  $attrs = NULL;
6732  }
6733  $a_xml_writer->xmlElement("assessmentcontrol", $attrs, NULL);
6734 
6735  if (strlen($this->getFinalStatement()))
6736  {
6737  // add qti presentation_material
6738  $a_xml_writer->xmlStartTag("presentation_material");
6739  $a_xml_writer->xmlStartTag("flow_mat");
6740  $this->addQTIMaterial($a_xml_writer, $this->getFinalStatement());
6741  $a_xml_writer->xmlEndTag("flow_mat");
6742  $a_xml_writer->xmlEndTag("presentation_material");
6743  }
6744 
6745  $attrs = array(
6746  "ident" => "1"
6747  );
6748  $a_xml_writer->xmlElement("section", $attrs, NULL);
6749  $a_xml_writer->xmlEndTag("assessment");
6750  $a_xml_writer->xmlEndTag("questestinterop");
6751 
6752  $xml = $a_xml_writer->xmlDumpMem(FALSE);
6753 
6754  foreach ($this->questions as $question_id)
6755  {
6756  $question =& ilObjTest::_instanciateQuestion($question_id);
6757  $qti_question = $question->toXML(false);
6758  $qti_question = preg_replace("/<questestinterop>/", "", $qti_question);
6759  $qti_question = preg_replace("/<\/questestinterop>/", "", $qti_question);
6760  if (strpos($xml, "</section>") !== false)
6761  {
6762  $xml = str_replace("</section>", "$qti_question</section>", $xml);
6763  }
6764  else
6765  {
6766  $xml = str_replace("<section ident=\"1\"/>", "<section ident=\"1\">\n$qti_question</section>", $xml);
6767  }
6768  }
6769  return $xml;
6770  }
6771 
6778  function exportPagesXML(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
6779  {
6780  global $ilBench;
6781 
6782  $this->mob_ids = array();
6783  $this->file_ids = array();
6784 
6785  $attrs = array();
6786  $attrs["Type"] = "Test";
6787  $a_xml_writer->xmlStartTag("ContentObject", $attrs);
6788 
6789  // MetaData
6790  $this->exportXMLMetaData($a_xml_writer);
6791 
6792  // PageObjects
6793  $expLog->write(date("[y-m-d H:i:s] ")."Start Export Page Objects");
6794  $ilBench->start("ContentObjectExport", "exportPageObjects");
6795  $this->exportXMLPageObjects($a_xml_writer, $a_inst, $expLog);
6796  $ilBench->stop("ContentObjectExport", "exportPageObjects");
6797  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export Page Objects");
6798 
6799  // MediaObjects
6800  $expLog->write(date("[y-m-d H:i:s] ")."Start Export Media Objects");
6801  $ilBench->start("ContentObjectExport", "exportMediaObjects");
6802  $this->exportXMLMediaObjects($a_xml_writer, $a_inst, $a_target_dir, $expLog);
6803  $ilBench->stop("ContentObjectExport", "exportMediaObjects");
6804  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export Media Objects");
6805 
6806  // FileItems
6807  $expLog->write(date("[y-m-d H:i:s] ")."Start Export File Items");
6808  $ilBench->start("ContentObjectExport", "exportFileItems");
6809  $this->exportFileItems($a_target_dir, $expLog);
6810  $ilBench->stop("ContentObjectExport", "exportFileItems");
6811  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export File Items");
6812 
6813  $a_xml_writer->xmlEndTag("ContentObject");
6814  }
6815 
6822  function exportXMLMetaData(&$a_xml_writer)
6823  {
6824  include_once "./Services/MetaData/classes/class.ilMD2XML.php";
6825  $md2xml = new ilMD2XML($this->getId(), 0, $this->getType());
6826  $md2xml->setExportMode(true);
6827  $md2xml->startExport();
6828  $a_xml_writer->appendXML($md2xml->getXML());
6829  }
6830 
6836  function modifyExportIdentifier($a_tag, $a_param, $a_value)
6837  {
6838  if ($a_tag == "Identifier" && $a_param == "Entry")
6839  {
6840  include_once "./Services/Utilities/classes/class.ilUtil.php";
6841  $a_value = ilUtil::insertInstIntoID($a_value);
6842  }
6843 
6844  return $a_value;
6845  }
6846 
6847 
6854  function exportXMLPageObjects(&$a_xml_writer, $a_inst, &$expLog)
6855  {
6856  global $ilBench;
6857 
6858  include_once "./Modules/LearningModule/classes/class.ilLMPageObject.php";
6859 
6860  foreach ($this->questions as $question_id)
6861  {
6862  $ilBench->start("ContentObjectExport", "exportPageObject");
6863  $expLog->write(date("[y-m-d H:i:s] ")."Page Object ".$question_id);
6864 
6865  $attrs = array();
6866  $a_xml_writer->xmlStartTag("PageObject", $attrs);
6867 
6868 
6869  // export xml to writer object
6870  $ilBench->start("ContentObjectExport", "exportPageObject_XML");
6871  $page_object = new ilPageObject("qpl", $question_id);
6872  $page_object->buildDom();
6873  $page_object->insertInstIntoIDs($a_inst);
6874  $mob_ids = $page_object->collectMediaObjects(false);
6875  $file_ids = $page_object->collectFileItems();
6876  $xml = $page_object->getXMLFromDom(false, false, false, "", true);
6877  $xml = str_replace("&","&amp;", $xml);
6878  $a_xml_writer->appendXML($xml);
6879  $page_object->freeDom();
6880  unset ($page_object);
6881 
6882  $ilBench->stop("ContentObjectExport", "exportPageObject_XML");
6883 
6884  // collect media objects
6885  $ilBench->start("ContentObjectExport", "exportPageObject_CollectMedia");
6886  //$mob_ids = $page_obj->getMediaObjectIDs();
6887  foreach($mob_ids as $mob_id)
6888  {
6889  $this->mob_ids[$mob_id] = $mob_id;
6890  }
6891  $ilBench->stop("ContentObjectExport", "exportPageObject_CollectMedia");
6892 
6893  // collect all file items
6894  $ilBench->start("ContentObjectExport", "exportPageObject_CollectFileItems");
6895  //$file_ids = $page_obj->getFileItemIds();
6896  foreach($file_ids as $file_id)
6897  {
6898  $this->file_ids[$file_id] = $file_id;
6899  }
6900  $ilBench->stop("ContentObjectExport", "exportPageObject_CollectFileItems");
6901 
6902  $a_xml_writer->xmlEndTag("PageObject");
6903  //unset($page_obj);
6904 
6905  $ilBench->stop("ContentObjectExport", "exportPageObject");
6906 
6907 
6908  }
6909  }
6910 
6917  function exportXMLMediaObjects(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
6918  {
6919  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
6920 
6921  foreach ($this->mob_ids as $mob_id)
6922  {
6923  $expLog->write(date("[y-m-d H:i:s] ")."Media Object ".$mob_id);
6924  if (ilObjMediaObject::_exists($mob_id))
6925  {
6926  $media_obj = new ilObjMediaObject($mob_id);
6927  $media_obj->exportXML($a_xml_writer, $a_inst);
6928  $media_obj->exportFiles($a_target_dir);
6929  unset($media_obj);
6930  }
6931  }
6932  }
6933 
6938  function exportFileItems($a_target_dir, &$expLog)
6939  {
6940  include_once "./Modules/File/classes/class.ilObjFile.php";
6941 
6942  foreach ($this->file_ids as $file_id)
6943  {
6944  $expLog->write(date("[y-m-d H:i:s] ")."File Item ".$file_id);
6945  $file_obj = new ilObjFile($file_id, false);
6946  $file_obj->export($a_target_dir);
6947  unset($file_obj);
6948  }
6949  }
6950 
6955  function getImportMapping()
6956  {
6957  if (!is_array($this->import_mapping))
6958  {
6959  return array();
6960  }
6961  else
6962  {
6963  return $this->import_mapping;
6964  }
6965  }
6966 
6976  function getECTSGrade($passed_array, $reached_points, $max_points)
6977  {
6978  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);
6979  }
6980 
6989  function _getECTSGrade($points_passed, $reached_points, $max_points, $a, $b, $c, $d, $e, $fx)
6990  {
6991  include_once "./Modules/Test/classes/class.ilStatistics.php";
6992  // calculate the median
6993  $passed_statistics = new ilStatistics();
6994  $passed_statistics->setData($points_passed);
6995  $ects_percentiles = array
6996  (
6997  "A" => $passed_statistics->quantile($a),
6998  "B" => $passed_statistics->quantile($b),
6999  "C" => $passed_statistics->quantile($c),
7000  "D" => $passed_statistics->quantile($d),
7001  "E" => $passed_statistics->quantile($e)
7002  );
7003  if (count($points_passed) && ($reached_points >= $ects_percentiles["A"]))
7004  {
7005  return "A";
7006  }
7007  else if (count($points_passed) && ($reached_points >= $ects_percentiles["B"]))
7008  {
7009  return "B";
7010  }
7011  else if (count($points_passed) && ($reached_points >= $ects_percentiles["C"]))
7012  {
7013  return "C";
7014  }
7015  else if (count($points_passed) && ($reached_points >= $ects_percentiles["D"]))
7016  {
7017  return "D";
7018  }
7019  else if (count($points_passed) && ($reached_points >= $ects_percentiles["E"]))
7020  {
7021  return "E";
7022  }
7023  else if (strcmp($fx, "") != 0)
7024  {
7025  if ($max_points > 0)
7026  {
7027  $percentage = ($reached_points / $max_points) * 100.0;
7028  if ($percentage < 0) $percentage = 0.0;
7029  }
7030  else
7031  {
7032  $percentage = 0.0;
7033  }
7034  if ($percentage >= $fx)
7035  {
7036  return "FX";
7037  }
7038  else
7039  {
7040  return "F";
7041  }
7042  }
7043  else
7044  {
7045  return "F";
7046  }
7047  }
7048 
7049  function checkMarks()
7050  {
7051  return $this->mark_schema->checkMarks();
7052  }
7053 
7054  function getMarkSchema()
7055  {
7056  return $this->mark_schema;
7057  }
7058 
7066  function setAuthor($author = "")
7067  {
7068  $this->author = $author;
7069  }
7070 
7080  function saveAuthorToMetadata($a_author = "")
7081  {
7082  $md =& new ilMD($this->getId(), 0, $this->getType());
7083  $md_life =& $md->getLifecycle();
7084  if (!$md_life)
7085  {
7086  if (strlen($a_author) == 0)
7087  {
7088  global $ilUser;
7089  $a_author = $ilUser->getFullname();
7090  }
7091 
7092  $md_life =& $md->addLifecycle();
7093  $md_life->save();
7094  $con =& $md_life->addContribute();
7095  $con->setRole("Author");
7096  $con->save();
7097  $ent =& $con->addEntity();
7098  $ent->setEntity($a_author);
7099  $ent->save();
7100  }
7101  }
7102 
7108  function createMetaData()
7109  {
7111  $this->saveAuthorToMetadata();
7112  }
7113 
7121  function getAuthor()
7122  {
7123  $author = array();
7124  include_once "./Services/MetaData/classes/class.ilMD.php";
7125  $md =& new ilMD($this->getId(), 0, $this->getType());
7126  $md_life =& $md->getLifecycle();
7127  if ($md_life)
7128  {
7129  $ids =& $md_life->getContributeIds();
7130  foreach ($ids as $id)
7131  {
7132  $md_cont =& $md_life->getContribute($id);
7133  if (strcmp($md_cont->getRole(), "Author") == 0)
7134  {
7135  $entids =& $md_cont->getEntityIds();
7136  foreach ($entids as $entid)
7137  {
7138  $md_ent =& $md_cont->getEntity($entid);
7139  array_push($author, $md_ent->getEntity());
7140  }
7141  }
7142  }
7143  }
7144  return join($author, ",");
7145  }
7146 
7154  function _lookupAuthor($obj_id)
7155  {
7156  $author = array();
7157  include_once "./Services/MetaData/classes/class.ilMD.php";
7158  $md =& new ilMD($obj_id, 0, "tst");
7159  $md_life =& $md->getLifecycle();
7160  if ($md_life)
7161  {
7162  $ids =& $md_life->getContributeIds();
7163  foreach ($ids as $id)
7164  {
7165  $md_cont =& $md_life->getContribute($id);
7166  if (strcmp($md_cont->getRole(), "Author") == 0)
7167  {
7168  $entids =& $md_cont->getEntityIds();
7169  foreach ($entids as $entid)
7170  {
7171  $md_ent =& $md_cont->getEntity($entid);
7172  array_push($author, $md_ent->getEntity());
7173  }
7174  }
7175  }
7176  }
7177  return join($author, ",");
7178  }
7179 
7186  function &_getAvailableTests($use_object_id = FALSE)
7187  {
7188  global $ilUser;
7189  global $ilDB;
7190 
7191  $result_array = array();
7192  $tests = ilUtil::_getObjectsByOperations("tst","write", $ilUser->getId(), -1);
7193  if (count($tests))
7194  {
7195  $titles = ilObject::_prepareCloneSelection($tests, "tst");
7196  foreach ($tests as $ref_id)
7197  {
7198  if ($use_object_id)
7199  {
7200  $obj_id = ilObject::_lookupObjId($ref_id);
7201  $result_array[$obj_id] = $titles[$ref_id];
7202  }
7203  else
7204  {
7205  $result_array[$ref_id] = $titles[$ref_id];
7206  }
7207  }
7208  }
7209  return $result_array;
7210  }
7211 
7218  function cloneRandomQuestions($new_id)
7219  {
7220  global $ilDB;
7221 
7222  if ($new_id > 0)
7223  {
7224  $result = $ilDB->queryF("SELECT * FROM tst_test_random WHERE test_fi = %s ORDER BY sequence, test_random_id",
7225  array('integer'),
7226  array($this->getTestId())
7227  );
7228  if ($result->numRows())
7229  {
7230  while ($row = $ilDB->fetchAssoc($result))
7231  {
7232  $next_id = $ilDB->nextId('tst_test_random');
7233  $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)",
7234  array('integer', 'integer', 'integer', 'integer', 'integer', 'integer'),
7235  array($next_id, $new_id, $row["questionpool_fi"], $row["num_of_q"], time(), $row['sequence'])
7236  );
7237  }
7238  }
7239  }
7240  }
7241 
7242 
7251  public function cloneObject($a_target_id,$a_copy_id = 0)
7252  {
7253  global $ilDB,$ilLog;
7254 
7255  $this->loadFromDb();
7256 
7257  // Copy settings
7258  $newObj = parent::cloneObject($a_target_id,$a_copy_id);
7259  $this->cloneMetaData($newObj);
7260  $newObj->setAnonymity($this->getAnonymity());
7261  $newObj->setAnswerFeedback($this->getAnswerFeedback());
7262  $newObj->setAnswerFeedbackPoints($this->getAnswerFeedbackPoints());
7263  $newObj->setAuthor($this->getAuthor());
7264  $newObj->setAllowedUsers($this->getAllowedUsers());
7265  $newObj->setAllowedUsersTimeGap($this->getAllowedUsersTimeGap());
7266  $newObj->setCountSystem($this->getCountSystem());
7267  $newObj->setECTSFX($this->getECTSFX());
7268  $newObj->setECTSGrades($this->getECTSGrades());
7269  $newObj->setECTSOutput($this->getECTSOutput());
7270  $newObj->setEnableProcessingTime($this->getEnableProcessingTime());
7271  $newObj->setEndingTime($this->getEndingTime());
7272  $newObj->setFixedParticipants($this->getFixedParticipants());
7273  $newObj->setInstantFeedbackSolution($this->getInstantFeedbackSolution());
7274  $newObj->setIntroduction($this->getIntroduction());
7275  $newObj->setFinalStatement($this->getFinalStatement());
7276  $newObj->setShowInfo($this->getShowInfo());
7277  $newObj->setForceJS($this->getForceJS());
7278  $newObj->setCustomStyle($this->getCustomStyle());
7279  $newObj->setKiosk($this->getKiosk());
7280  $newObj->setShowFinalStatement($this->getShowFinalStatement());
7281  $newObj->setListOfQuestionsSettings($this->getListOfQuestionsSettings());
7282  $newObj->setMCScoring($this->getMCScoring());
7283  $newObj->setMailNotification($this->getMailNotification());
7284  $newObj->setMailNotificationType($this->getMailNotificationType());
7285  $newObj->setNrOfTries($this->getNrOfTries());
7286  $newObj->setPassScoring($this->getPassScoring());
7287  $newObj->setPassword($this->getPassword());
7288  $newObj->setProcessingTime($this->getProcessingTime());
7289  $newObj->setRandomQuestionCount($this->getRandomQuestionCount());
7290  $newObj->setRandomTest($this->isRandomTest());
7291  $newObj->setReportingDate($this->getReportingDate());
7292  $newObj->setResetProcessingTime($this->getResetProcessingTime());
7293  $newObj->setResultsPresentation($this->getResultsPresentation());
7294  $newObj->setScoreCutting($this->getScoreCutting());
7295  $newObj->setScoreReporting($this->getScoreReporting());
7296  $newObj->setSequenceSettings($this->getSequenceSettings());
7297  $newObj->setShowCancel($this->getShowCancel());
7298  $newObj->setShowMarker($this->getShowMarker());
7299  $newObj->setShuffleQuestions($this->getShuffleQuestions());
7300  $newObj->setStartingTime($this->getStartingTime());
7301  $newObj->setTitleOutput($this->getTitleOutput());
7302  $newObj->setUsePreviousAnswers($this->getUsePreviousAnswers());
7303  $newObj->setCertificateVisibility($this->getCertificateVisibility());
7304  $newObj->mark_schema = clone $this->mark_schema;
7305  $newObj->setEnabledViewMode($this->getEnabledViewMode());
7306  $newObj->setTemplate($this->getTemplate());
7307  $newObj->setPoolUsage($this->getPoolUsage());
7308  $newObj->setPrintBestSolutionWithResult($this->isBestSolutionPrintedWithResult());
7309  $newObj->saveToDb();
7310 
7311  // clone certificate
7312  include_once "./Services/Certificate/classes/class.ilCertificate.php";
7313  include_once "./Modules/Test/classes/class.ilTestCertificateAdapter.php";
7314  $cert = new ilCertificate(new ilTestCertificateAdapter($this));
7315  $newcert = new ilCertificate(new ilTestCertificateAdapter($newObj));
7316  $cert->cloneCertificate($newcert);
7317 
7318  if ($this->isRandomTest())
7319  {
7320  $newObj->saveRandomQuestionCount($newObj->getRandomQuestionCount());
7321  $this->cloneRandomQuestions($newObj->getTestId());
7322  }
7323  else
7324  {
7325  include_once("./Services/CopyWizard/classes/class.ilCopyWizardOptions.php");
7326  $cwo = ilCopyWizardOptions::_getInstance($a_copy_id);
7327 
7328  // clone the questions
7329  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7330  foreach ($this->questions as $key => $question_id)
7331  {
7332  $question = ilObjTest::_instanciateQuestion($question_id);
7333  $newObj->questions[$key] = $question->duplicate(true, null, null, null, $newObj->getId());
7334  $original_id = assQuestion::_getOriginalId($question_id);
7335  $question = ilObjTest::_instanciateQuestion($newObj->questions[$key]);
7336  $question->saveToDb($original_id);
7337 
7338  // Save the mapping of old question id <-> new question id
7339  // This will be used in class.ilObjCourse::cloneDependencies to copy learning objectives
7340  $cwo->appendMapping($this->getRefId().'_'.$question_id,$newObj->getRefId().'_'.$newObj->questions[$key]);
7341  $ilLog->write(__METHOD__.': Added mapping '.$this->getRefId().'_'.$question_id.' <-> ' .
7342  $newObj->getRefId().'_'.$newObj->questions[$key]);
7343  }
7344  }
7345  $newObj->saveToDb();
7346  return $newObj;
7347  }
7348 
7355  function getQuestionCount()
7356  {
7357  $num = 0;
7358 
7359  if ($this->isRandomTest())
7360  {
7361  if ($this->getRandomQuestionCount())
7362  {
7363  $num = $this->getRandomQuestionCount();
7364  $qpls =& $this->getRandomQuestionpools();
7365  $maxcount = 0;
7366  foreach ($qpls as $data)
7367  {
7368  $maxcount += $data["contains"];
7369  }
7370  if ($num > $maxcount) $num = $maxcount;
7371  }
7372  else
7373  {
7374  $qpls =& $this->getRandomQuestionpools();
7375  foreach ($qpls as $data)
7376  {
7377  $add = ($data["count"] <= $data["contains"]) ? $data["count"] : $data["contains"];
7378  $num += $add;
7379  }
7380  }
7381  }
7382  else
7383  {
7384  $num = count($this->questions);
7385  }
7386 
7387  return $num;
7388  }
7389 
7397  {
7398  global $ilDB;
7399 
7400  $num = 0;
7401 
7402  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
7403  array('integer'),
7404  array($test_id)
7405  );
7406  if (!$result->numRows())
7407  {
7408  return 0;
7409  }
7410  $test = $ilDB->fetchAssoc($result);
7411 
7412  if ($test["random_test"] == 1)
7413  {
7414  $qpls = array();
7415  $counter = 0;
7416  $result = $ilDB->queryF("SELECT * FROM tst_test_random WHERE test_fi = %s ORDER BY sequence, test_random_id",
7417  array('integer'),
7418  array($test_id)
7419  );
7420  if ($result->numRows())
7421  {
7422  while ($row = $ilDB->fetchAssoc($result))
7423  {
7424  $countresult = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE obj_fi = %s AND qpl_questions.tstamp > 0 AND original_id IS NULL",
7425  array('integer'),
7426  $row["questionpool_fi"]
7427  );
7428  $contains = $countresult->numRows();
7429  $qpls[$counter] = array(
7430  "index" => $counter,
7431  "count" => $row["num_of_q"],
7432  "qpl" => $row["questionpool_fi"],
7433  "contains" => $contains
7434  );
7435  $counter++;
7436  }
7437  }
7438  if ($test["random_question_count"] > 0)
7439  {
7440  $num = $test["random_question_count"];
7441  $maxcount = 0;
7442  foreach ($qpls as $data)
7443  {
7444  $maxcount += $data["contains"];
7445  }
7446  if ($num > $maxcount) $num = $maxcount;
7447  }
7448  else
7449  {
7450  $num = 0;
7451  foreach ($qpls as $data)
7452  {
7453  $add = ($data["count"] <= $data["contains"]) ? $data["count"] : $data["contains"];
7454  $num += $add;
7455  }
7456  }
7457  }
7458  else
7459  {
7460  $result = $ilDB->queryF("SELECT test_question_id FROM tst_test_question WHERE test_fi = %s",
7461  array('integer'),
7462  array($test_id)
7463  );
7464  $num = $result->numRows();
7465  }
7466  return $num;
7467  }
7468 
7475  {
7476  global $ilDB;
7477 
7478  // delete eventually set questions of a previous non-random test
7479  $this->removeAllTestEditings();
7480  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
7481  array('integer'),
7482  array($this->getTestId())
7483  );
7484  $this->questions = array();
7485  $this->saveCompleteStatus();
7486  }
7487 
7494  {
7495  global $ilDB;
7496  // delete eventually set random question pools of a previous random test
7497  $this->removeAllTestEditings();
7498  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
7499  array('integer'),
7500  array($this->getTestId())
7501  );
7502  $this->questions = array();
7503  $this->saveCompleteStatus();
7504  }
7505 
7513  function logAction($logtext = "", $question_id = "")
7514  {
7515  global $ilUser;
7516 
7517  $original_id = "";
7518  if (strcmp($question_id, "") != 0)
7519  {
7520  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7521  $original_id = assQuestion::_getOriginalId($question_id);
7522  }
7523  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
7524  ilObjAssessmentFolder::_addLog($ilUser->getId(), $this->getId(), $logtext, $question_id, $original_id, TRUE, $this->getRefId());
7525  }
7526 
7535  {
7536  global $ilDB;
7537  $object_id = FALSE;
7538  $result = $ilDB->queryF("SELECT obj_fi FROM tst_tests WHERE test_id = %s",
7539  array('integer'),
7540  array($test_id)
7541  );
7542  if ($result->numRows())
7543  {
7544  $row = $ilDB->fetchAssoc($result);
7545  $object_id = $row["obj_fi"];
7546  }
7547  return $object_id;
7548  }
7549 
7557  function _getObjectIDFromActiveID($active_id)
7558  {
7559  global $ilDB;
7560  $object_id = FALSE;
7561  $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",
7562  array('integer'),
7563  array($active_id)
7564  );
7565  if ($result->numRows())
7566  {
7567  $row = $ilDB->fetchAssoc($result);
7568  $object_id = $row["obj_fi"];
7569  }
7570  return $object_id;
7571  }
7572 
7580  function _getTestIDFromObjectID($object_id)
7581  {
7582  global $ilDB;
7583  $test_id = FALSE;
7584  $result = $ilDB->queryF("SELECT test_id FROM tst_tests WHERE obj_fi = %s",
7585  array('integer'),
7586  array($object_id)
7587  );
7588  if ($result->numRows())
7589  {
7590  $row = $ilDB->fetchAssoc($result);
7591  $test_id = $row["test_id"];
7592  }
7593  return $test_id;
7594  }
7595 
7604  function getTextAnswer($active_id, $question_id, $pass = NULL)
7605  {
7606  global $ilDB;
7607 
7608  $res = "";
7609  if (($active_id) && ($question_id))
7610  {
7611  if (is_null($pass))
7612  {
7613  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7614  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
7615  }
7616  $result = $ilDB->queryF("SELECT value1 FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
7617  array('integer', 'integer', 'integer'),
7618  array($active_id, $question_id, $pass)
7619  );
7620  if ($result->numRows() == 1)
7621  {
7622  $row = $ilDB->fetchAssoc($result);
7623  $res = $row["value1"];
7624  }
7625  }
7626  return $res;
7627  }
7628 
7636  function getQuestiontext($question_id)
7637  {
7638  global $ilDB;
7639 
7640  $res = "";
7641  if ($question_id)
7642  {
7643  $result = $ilDB->queryF("SELECT question_text FROM qpl_questions WHERE question_id = %s",
7644  array('integer'),
7645  array($question_id)
7646  );
7647  if ($result->numRows() == 1)
7648  {
7649  $row = $ilDB->fetchAssoc($result);
7650  $res = $row["question_text"];
7651  }
7652  }
7653  return $res;
7654  }
7655 
7662  function &getInvitedUsers($user_id="", $order="login, lastname, firstname")
7663  {
7664  global $ilDB;
7665 
7666  $result_array = array();
7667 
7668  if ($this->getAnonymity())
7669  {
7670  if (is_numeric($user_id))
7671  {
7672  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " .
7673  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7674  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7675  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
7676  "ORDER BY $order",
7677  array('text', 'text', 'text', 'integer', 'integer'),
7678  array("", $this->lng->txt("unknown"), "", $this->getTestId(), $user_id)
7679  );
7680  }
7681  else
7682  {
7683  $result = $ilDB->queryF("SELECT tst_active.active_id, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " .
7684  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7685  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7686  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
7687  "ORDER BY $order",
7688  array('text', 'text', 'text', 'integer'),
7689  array("", $this->lng->txt("unknown"), "", $this->getTestId())
7690  );
7691  }
7692  }
7693  else
7694  {
7695  if (is_numeric($user_id))
7696  {
7697  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " .
7698  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7699  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7700  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
7701  "ORDER BY $order",
7702  array('integer', 'integer'),
7703  array($this->getTestId(), $user_id)
7704  );
7705  }
7706  else
7707  {
7708  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " .
7709  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7710  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7711  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
7712  "ORDER BY $order",
7713  array('integer'),
7714  array($this->getTestId())
7715  );
7716  }
7717  }
7718  $result_array = array();
7719  while ($row = $ilDB->fetchAssoc($result))
7720  {
7721  $result_array[$row['usr_id']]= $row;
7722  }
7723  return $result_array;
7724  }
7725 
7733  {
7734  global $ilDB;
7735 
7736  if ($this->getAnonymity())
7737  {
7738  $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 ".
7739  "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),
7740  array('text', 'text', 'text', 'integer'),
7741  array("", $this->lng->txt("unknown"), "", $this->getTestId())
7742  );
7743  }
7744  else
7745  {
7746  $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 ".
7747  "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),
7748  array('integer'),
7749  array($this->getTestId())
7750  );
7751  }
7752  $data = array();
7753  while ($row = $ilDB->fetchAssoc($result))
7754  {
7755  $data[$row['active_id']] = $row;
7756  }
7757  foreach ($data as $index => $participant)
7758  {
7759  if (strlen(trim($participant["firstname"].$participant["lastname"])) == 0)
7760  {
7761  $data[$index]["lastname"] = $this->lng->txt("deleted_user");
7762  }
7763  }
7764  return $data;
7765  }
7766 
7767  public function getTestParticipantsForManualScoring($filter = NULL)
7768  {
7769  global $ilDB;
7770 
7771  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
7773  if (count($scoring) == 0) return array();
7774 
7775  $participants =& $this->getTestParticipants();
7776  $filtered_participants = array();
7777  foreach ($participants as $active_id => $participant)
7778  {
7779  $qstType_IN_manScoreableQstTypes = $ilDB->in('qpl_questions.question_type_fi', $scoring, false, 'integer');
7780 
7781  $queryString = "
7782  SELECT tst_test_result.manual
7783 
7784  FROM tst_test_result
7785 
7786  INNER JOIN qpl_questions
7787  ON tst_test_result.question_fi = qpl_questions.question_id
7788 
7789  WHERE tst_test_result.active_fi = %s
7790  AND $qstType_IN_manScoreableQstTypes
7791  ";
7792 
7793  $result = $ilDB->queryF(
7794  $queryString, array("integer"), array($active_id)
7795  );
7796 
7797  $count = $result->numRows();
7798 
7799  if ($count > 0)
7800  {
7801  switch ($filter)
7802  {
7803  case 1: // only active users
7804  if ($participant->active) $filtered_participants[$active_id] = $participant;
7805  break;
7806  case 2: // only inactive users
7807  if (!$participant->active) $filtered_participants[$active_id] = $participant;
7808  break;
7809  case 3: // all users
7810  $filtered_participants[$active_id] = $participant;
7811  break;
7812  case 4:
7813  // already scored participants
7814  //$found = 0;
7815  //while ($row = $ilDB->fetchAssoc($result))
7816  //{
7817  // if ($row["manual"]) $found++;
7818  //}
7819  //if ($found == $count)
7820  //{
7821  //$filtered_participants[$active_id] = $participant;
7822  //}
7823  //else
7824  //{
7825  $assessmentSetting = new ilSetting("assessment");
7826  $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id);
7827  if ($manscoring_done) $filtered_participants[$active_id] = $participant;
7828  //}
7829  break;
7830  case 5:
7831  // unscored participants
7832  //$found = 0;
7833  //while ($row = $ilDB->fetchAssoc($result))
7834  //{
7835  // if ($row["manual"]) $found++;
7836  //}
7837  //if ($found == 0)
7838  //{
7839  $assessmentSetting = new ilSetting("assessment");
7840  $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id);
7841  if (!$manscoring_done) $filtered_participants[$active_id] = $participant;
7842  //}
7843  break;
7844  case 6:
7845  // partially scored participants
7846  $found = 0;
7847  while ($row = $ilDB->fetchAssoc($result))
7848  {
7849  if ($row["manual"]) $found++;
7850  }
7851  if (($found > 0) && ($found < $count)) $filtered_participants[$active_id] = $participant;
7852  break;
7853  default:
7854  $filtered_participants[$active_id] = $participant;
7855  break;
7856  }
7857  }
7858  }
7859  return $filtered_participants;
7860  }
7861 
7869  function &getUserData($ids)
7870  {
7871  global $ilDB;
7872 
7873  if (!is_array($ids) || count($ids) ==0) return array();
7874 
7875  if ($this->getAnonymity())
7876  {
7877  $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",
7878  array('text', 'text', 'text'),
7879  array("", $this->lng->txt("unknown"), "")
7880  );
7881  }
7882  else
7883  {
7884  $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");
7885  }
7886 
7887  $result_array = array();
7888  while ($row = $ilDB->fetchAssoc($result))
7889  {
7890  $result_array[$row["usr_id"]]= $row;
7891  }
7892  return $result_array;
7893  }
7894 
7895  function &getGroupData($ids)
7896  {
7897  if (!is_array($ids) || count($ids) ==0) return array();
7898  $result = array();
7899  foreach ($ids as $ref_id)
7900  {
7901  $obj_id = ilObject::_lookupObjId($ref_id);
7902  $result[$ref_id] = array("ref_id" => $ref_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id));
7903  }
7904  return $result;
7905  }
7906 
7907  function &getRoleData($ids)
7908  {
7909  if (!is_array($ids) || count($ids) ==0) return array();
7910  $result = array();
7911  foreach ($ids as $obj_id)
7912  {
7913  $result[$obj_id] = array("obj_id" => $obj_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id));
7914  }
7915  return $result;
7916  }
7917 
7918 
7925  function inviteGroup($group_id)
7926  {
7927  include_once "./Modules/Group/classes/class.ilObjGroup.php";
7928  $group = new ilObjGroup($group_id);
7929  $members = $group->getGroupMemberIds();
7930  include_once './Services/User/classes/class.ilObjUser.php';
7931  foreach ($members as $user_id)
7932  {
7933  $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id));
7934  }
7935  }
7936 
7943  function inviteRole($role_id)
7944  {
7945  global $rbacreview;
7946  $members = $rbacreview->assignedUsers($role_id,"usr_id");
7947  include_once './Services/User/classes/class.ilObjUser.php';
7948  foreach ($members as $user_id)
7949  {
7950  $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id));
7951  }
7952  }
7953 
7954 
7955 
7962  function disinviteUser($user_id)
7963  {
7964  global $ilDB;
7965 
7966  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
7967  array('integer', 'integer'),
7968  array($this->getTestId(), $user_id)
7969  );
7970  }
7971 
7978  function inviteUser($user_id, $client_ip="")
7979  {
7980  global $ilDB;
7981 
7982  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
7983  array('integer', 'integer'),
7984  array($this->getTestId(), $user_id)
7985  );
7986  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_invited_user (test_fi, user_fi, clientip, tstamp) VALUES (%s, %s, %s, %s)",
7987  array('integer', 'integer', 'text', 'integer'),
7988  array($this->getTestId(), $user_id, (strlen($client_ip)) ? $client_ip : NULL, time())
7989  );
7990  }
7991 
7992 
7993  function setClientIP($user_id, $client_ip)
7994  {
7995  global $ilDB;
7996 
7997  $affectedRows = $ilDB->manipulateF("UPDATE tst_invited_user SET clientip = %s, tstamp = %s WHERE test_fi=%s and user_fi=%s",
7998  array('text', 'integer', 'integer', 'integer'),
7999  array((strlen($client_ip)) ? $client_ip : NULL, time(), $this->getTestId(), $user_id)
8000  );
8001  }
8002 
8008  function _getSolvedQuestions($active_id, $question_fi = null)
8009  {
8010  global $ilDB;
8011  if (is_numeric($question_fi))
8012  {
8013  $result = $ilDB->queryF("SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s AND question_fi=%s",
8014  array('integer', 'integer'),
8015  array($active_id, $question_fi)
8016  );
8017  }
8018  else
8019  {
8020  $result = $ilDB->queryF("SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s",
8021  array('integer'),
8022  array($active_id)
8023  );
8024  }
8025  $result_array = array();
8026  while ($row = $ilDB->fetchAssoc($result))
8027  {
8028  $result_array[$row["question_fi"]]= $row;
8029  }
8030  return $result_array;
8031  }
8032 
8033 
8037  function setQuestionSetSolved($value, $question_id, $user_id)
8038  {
8039  global $ilDB;
8040 
8041  $active_id = $this->getActiveIdOfUser($user_id);
8042  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s AND question_fi = %s",
8043  array('integer', 'integer'),
8044  array($active_id, $question_id)
8045  );
8046  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_qst_solved (solved, question_fi, active_fi) VALUES (%s, %s, %s)",
8047  array('integer', 'integer', 'integer'),
8048  array($value, $question_id, $active_id)
8049  );
8050  }
8051 
8052 
8056  function setActiveTestSubmitted($user_id)
8057  {
8058  global $ilDB, $ilLog;
8059 
8060  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s, tstamp = %s WHERE test_fi = %s AND user_fi = %s",
8061  array('integer', 'timestamp', 'integer', 'integer', 'integer'),
8062  array(1, date('Y-m-d H:i:s'), time(), $this->getTestId(), $user_id)
8063  );
8064  $this->testSession = NULL;
8065  }
8066 
8070  function isTestFinished($active_id)
8071  {
8072  global $ilDB;
8073 
8074  $result = $ilDB->queryF("SELECT submitted FROM tst_active WHERE active_id=%s AND submitted=%s",
8075  array('integer', 'integer'),
8076  array($active_id, 1)
8077  );
8078  return $result->numRows() == 1;
8079  }
8080 
8084  function isActiveTestSubmitted($user_id = null)
8085  {
8086  global $ilUser;
8087  global $ilDB;
8088 
8089  if (!is_numeric($user_id))
8090  $user_id = $ilUser->getId();
8091 
8092  $result = $ilDB->queryF("SELECT submitted FROM tst_active WHERE test_fi=%s AND user_fi=%s AND submitted=%s",
8093  array('integer', 'integer', 'integer'),
8094  array($this->getTestId(), $user_id, 1)
8095  );
8096  return $result->numRows() == 1;
8097  }
8098 
8103  {
8104  return $this->getNrOfTries() != 0;
8105  }
8106 
8107 
8112  function isNrOfTriesReached($tries)
8113  {
8114  return $tries >= (int) $this->getNrOfTries();
8115  }
8116 
8117 
8126  function getAllTestResults($participants, $prepareForCSV = true)
8127  {
8128  $results = array();
8129  $row = array(
8130  "user_id" => $this->lng->txt("user_id"),
8131  "matriculation" => $this->lng->txt("matriculation"),
8132  "lastname" => $this->lng->txt("lastname"),
8133  "firstname" => $this->lng->txt("firstname"),
8134  "login" =>$this->lng->txt("login"),
8135  "reached_points" => $this->lng->txt("tst_reached_points"),
8136  "max_points" => $this->lng->txt("tst_maximum_points"),
8137  "percent_value" => $this->lng->txt("tst_percent_solved"),
8138  "mark" => $this->lng->txt("tst_mark"),
8139  "ects" => $this->lng->txt("ects_grade")
8140  );
8141  $results[] = $row;
8142  if (count($participants))
8143  {
8144  if ($this->ects_output)
8145  {
8146  $passed_array =& $this->getTotalPointsPassedArray();
8147  }
8148  foreach ($participants as $active_id => $user_rec)
8149  {
8150  $row = array();
8151  $reached_points = 0;
8152  $max_points = 0;
8153  foreach ($this->questions as $value)
8154  {
8155  $question =& ilObjTest::_instanciateQuestion($value);
8156  if (is_object($question))
8157  {
8158  $max_points += $question->getMaximumPoints();
8159  $reached_points += $question->getReachedPoints($active_id);
8160  }
8161  }
8162  if ($max_points > 0)
8163  {
8164  $percentvalue = $reached_points / $max_points;
8165  if ($percentvalue < 0) $percentvalue = 0.0;
8166  }
8167  else
8168  {
8169  $percentvalue = 0;
8170  }
8171  $mark_obj = $this->mark_schema->getMatchingMark($percentvalue * 100);
8172  $passed = "";
8173  if ($mark_obj)
8174  {
8175  $mark = $mark_obj->getOfficialName();
8176  $ects_mark = $this->getECTSGrade($passed_array, $reached_points, $max_points);
8177  }
8178  if ($this->getAnonymity())
8179  {
8180  $user_rec['firstname'] = "";
8181  $user_rec['lastname'] = $this->lng->txt("unknown");
8182  }
8183  $row = array(
8184  "user_id"=>$user_rec['usr_id'],
8185  "matriculation" => $user_rec['matriculation'],
8186  "lastname" => $user_rec['lastname'],
8187  "firstname" => $user_rec['firstname'],
8188  "login"=>$user_rec['login'],
8189  "reached_points" => $reached_points,
8190  "max_points" => $max_points,
8191  "percent_value" => $percentvalue,
8192  "mark" => $mark,
8193  "ects" => $ects_mark
8194  );
8195  $results[] = $prepareForCSV ? $this->processCSVRow ($row, true) : $row;
8196  }
8197  }
8198  return $results;
8199  }
8200 
8211  function &processCSVRow($row, $quoteAll = FALSE, $separator = ";")
8212  {
8213  $resultarray = array();
8214  foreach ($row as $rowindex => $entry)
8215  {
8216  $surround = FALSE;
8217  if ($quoteAll)
8218  {
8219  $surround = TRUE;
8220  }
8221  if (strpos($entry, "\"") !== FALSE)
8222  {
8223  $entry = str_replace("\"", "\"\"", $entry);
8224  $surround = TRUE;
8225  }
8226  if (strpos($entry, $separator) !== FALSE)
8227  {
8228  $surround = TRUE;
8229  }
8230  // replace all CR LF with LF (for Excel for Windows compatibility
8231  $entry = str_replace(chr(13).chr(10), chr(10), $entry);
8232 
8233  if ($surround)
8234  {
8235  $entry = "\"" . $entry . "\"";
8236  }
8237 
8238  $resultarray[$rowindex] = $entry;
8239  }
8240  return $resultarray;
8241  }
8242 
8251  function _getPass($active_id)
8252  {
8253  global $ilDB;
8254  $result = $ilDB->queryF("SELECT tries FROM tst_active WHERE active_id = %s",
8255  array('integer'),
8256  array($active_id)
8257  );
8258  if ($result->numRows())
8259  {
8260  $row = $ilDB->fetchAssoc($result);
8261  return $row["tries"];
8262  }
8263  else
8264  {
8265  return 0;
8266  }
8267  }
8268 
8278  function _getMaxPass($active_id)
8279  {
8280  global $ilDB;
8281  $result = $ilDB->queryF("SELECT MAX(pass) maxpass FROM tst_test_result WHERE active_fi = %s",
8282  array('integer'),
8283  array($active_id)
8284  );
8285  if ($result->numRows())
8286  {
8287  $row = $ilDB->fetchAssoc($result);
8288  $max = $row["maxpass"];
8289  }
8290  else
8291  {
8292  $max = NULL;
8293  }
8294  return $max;
8295  }
8296 
8307  function _getBestPass($active_id)
8308  {
8309  global $ilDB;
8310 
8311  $result = $ilDB->queryF("SELECT * FROM tst_pass_result WHERE active_fi = %s",
8312  array('integer'),
8313  array($active_id)
8314  );
8315  if ($result->numRows())
8316  {
8317  $bestrow = null;
8318  $bestfactor = 0;
8319  while ($row = $ilDB->fetchAssoc($result))
8320  {
8321  if($row["maxpoints"] > 0)
8322  {
8323  $factor = $row["points"] / $row["maxpoints"];
8324  }
8325  else
8326  {
8327  $factor = 0;
8328  }
8329 
8330  if($factor > $bestfactor)
8331  {
8332  $bestrow = $row;
8333  $bestfactor = $factor;
8334  }
8335  }
8336  if (is_array($bestrow))
8337  {
8338  return $bestrow["pass"];
8339  }
8340  else
8341  {
8342  return 0;
8343  }
8344  }
8345  else
8346  {
8347  return 0;
8348  }
8349  }
8350 
8359  function _getResultPass($active_id)
8360  {
8361  $counted_pass = NULL;
8362  if (ilObjTest::_getPassScoring($active_id) == SCORE_BEST_PASS)
8363  {
8364  $counted_pass = ilObjTest::_getBestPass($active_id);
8365  }
8366  else
8367  {
8368  $counted_pass = ilObjTest::_getMaxPass($active_id);
8369  }
8370  return $counted_pass;
8371  }
8372 
8382  function getAnsweredQuestionCount($active_id, $pass = NULL)
8383  {
8384  if ($this->isRandomTest())
8385  {
8386  $this->loadQuestions($active_id, $pass);
8387  }
8388  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
8389  $workedthrough = 0;
8390  foreach ($this->questions as $value)
8391  {
8392  if (assQuestion::_isWorkedThrough($active_id, $value, $pass))
8393  {
8394  $workedthrough += 1;
8395  }
8396  }
8397  return $workedthrough;
8398  }
8399 
8409  function getPassFinishDate($active_id, $pass)
8410  {
8411  global $ilDB;
8412  if (is_null($pass)) $pass = 0;
8413  $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",
8414  array('integer', 'integer'),
8415  array($active_id, $pass)
8416  );
8417  if ($result->numRows())
8418  {
8419  $row = $ilDB->fetchAssoc($result);
8420  return $row["tstamp"];
8421  }
8422  else
8423  {
8424  return 0;
8425  }
8426  }
8427 
8435  function isExecutable($user_id, $allowPassIncrease = FALSE)
8436  {
8437  $result = array(
8438  "executable" => true,
8439  "errormessage" => ""
8440  );
8441  if (!$this->startingTimeReached())
8442  {
8443  $result["executable"] = false;
8444  $result["errormessage"] = sprintf($this->lng->txt("detail_starting_time_not_reached"), ilFormat::ftimestamp2datetimeDB($this->getStartingTime()));
8445  return $result;
8446  }
8447  if ($this->endingTimeReached())
8448  {
8449  $result["executable"] = false;
8450  $result["errormessage"] = sprintf($this->lng->txt("detail_ending_time_reached"), ilFormat::ftimestamp2datetimeDB($this->getEndingTime()));
8451  return $result;
8452  }
8453 
8454  $active_id = $this->getActiveIdOfUser($user_id);
8455 
8456  if ($this->getEnableProcessingTime())
8457  {
8458  if ($active_id > 0)
8459  {
8460  $starting_time = $this->getStartingTimeOfUser($active_id);
8461  if ($starting_time !== FALSE)
8462  {
8464  {
8465  if ($allowPassIncrease && $this->getResetProcessingTime() && (($this->getNrOfTries() == 0) || ($this->getNrOfTries() > ($this->_getPass($active_id)+1))))
8466  {
8467  // a test pass was quitted because the maximum processing time was reached, but the time
8468  // will be resetted for future passes, so if there are more passes allowed, the participant may
8469  // start the test again.
8470  // This code block is only called when $allowPassIncrease is TRUE which only happens when
8471  // the test info page is opened. Otherwise this will lead to unexpected results!
8472  $this->getTestSession()->increasePass();
8473  $this->getTestSession()->setLastSequence(0);
8474  $this->getTestSession()->saveToDb();
8475  }
8476  else
8477  {
8478  $result["executable"] = false;
8479  $result["errormessage"] = $this->lng->txt("detail_max_processing_time_reached");
8480  }
8481  return $result;
8482  }
8483  }
8484  }
8485  }
8486 
8487  if ($this->hasNrOfTriesRestriction() && ($active_id > 0) && $this->isNrOfTriesReached($this->getTestSession($active_id)->getPass()))
8488  {
8489  $result["executable"] = false;
8490  $result["errormessage"] = $this->lng->txt("maximum_nr_of_tries_reached");
8491  return $result;
8492  }
8493 
8494  return $result;
8495  }
8496 
8503  function canViewResults()
8504  {
8505  $result = true;
8506  if ($this->getScoreReporting() == 4) return false;
8507  if ($this->getReportingDate())
8508  {
8509  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches))
8510  {
8511  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
8512  $now = mktime();
8513  if ($now < $epoch_time)
8514  {
8515  $result = false;
8516  }
8517  }
8518  }
8519  return $result;
8520  }
8521 
8522  function canShowTestResults($user_id)
8523  {
8524  $active_id = $this->getActiveIdOfUser($user_id);
8525  if ($active_id > 0)
8526  {
8527  $starting_time = $this->getStartingTimeOfUser($active_id);
8528  }
8529  $notimeleft = FALSE;
8530  if ($starting_time !== FALSE)
8531  {
8533  {
8534  $notimeleft = TRUE;
8535  }
8536  }
8537  $result = TRUE;
8538  if (!$this->isTestFinishedToViewResults($active_id, $this->getTestSession($active_id)->getPass()) && ($this->getScoreReporting() == REPORT_AFTER_TEST))
8539  {
8540  $result = FALSE;
8541  }
8542  if (($this->endingTimeReached()) || $notimeleft) $result = TRUE;
8543  $result = $result & $this->canViewResults();
8544  return $result;
8545  }
8546 
8547  function canEditMarks()
8548  {
8549  $total = $this->evalTotalPersons();
8550  if ($total > 0)
8551  {
8552  if ($this->getReportingDate())
8553  {
8554  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches))
8555  {
8556  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
8557  $now = mktime();
8558  if ($now < $epoch_time)
8559  {
8560  return true;
8561  }
8562  }
8563  }
8564  return false;
8565  }
8566  else
8567  {
8568  return true;
8569  }
8570  }
8571 
8579  function getStartingTimeOfUser($active_id)
8580  {
8581  global $ilDB;
8582 
8583  if ($active_id < 1) return FALSE;
8584  $pass = ($this->getResetProcessingTime()) ? $this->_getPass($active_id) : 0;
8585  $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",
8586  array('integer', 'integer'),
8587  array($active_id, $pass)
8588  );
8589  if ($result->numRows())
8590  {
8591  $row = $ilDB->fetchAssoc($result);
8592  if (preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches))
8593  {
8594  return mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
8595  }
8596  else
8597  {
8598  return mktime();
8599  }
8600  }
8601  else
8602  {
8603  return mktime();
8604  }
8605  }
8606 
8616  {
8617  if ($this->getEnableProcessingTime())
8618  {
8620  $now = mktime();
8621  if ($now > ($starting_time + $processing_time))
8622  {
8623  return TRUE;
8624  }
8625  else
8626  {
8627  return FALSE;
8628  }
8629  }
8630  else
8631  {
8632  return FALSE;
8633  }
8634  }
8635 
8636  function &getTestQuestions()
8637  {
8638  global $ilDB;
8639 
8640  $query = "
8641  SELECT questions.*,
8642  questtypes.type_tag,
8643  tstquest.sequence,
8644  tstquest.obligatory,
8645  origquest.obj_fi orig_obj_fi
8646 
8647  FROM qpl_questions questions
8648 
8649  INNER JOIN qpl_qst_type questtypes
8650  ON questtypes.question_type_id = questions.question_type_fi
8651 
8652  INNER JOIN tst_test_question tstquest
8653  ON tstquest.question_fi = questions.question_id
8654 
8655  LEFT JOIN qpl_questions origquest
8656  ON origquest.question_id = questions.original_id
8657 
8658  WHERE tstquest.test_fi = %s
8659 
8660  ORDER BY tstquest.sequence
8661  ";
8662 
8663  $query_result = $ilDB->queryF(
8664  $query, array('integer'), array($this->getTestId())
8665  );
8666 
8667  $questions = array();
8668 
8669  while ($row = $ilDB->fetchAssoc($query_result))
8670  {
8671  $question = $row;
8672 
8673  $question['obligationPossible'] = self::isQuestionObligationPossible($row['question_id']);
8674 
8675  $questions[] = $question;
8676  }
8677 
8678  return $questions;
8679  }
8680 
8688  {
8689  return ($this->shuffle_questions) ? 1 : 0;
8690  }
8691 
8698  function setShuffleQuestions($a_shuffle)
8699  {
8700  $this->shuffle_questions = ($a_shuffle) ? 1 : 0;
8701  }
8702 
8716  {
8717  return ($this->show_summary) ? $this->show_summary : 0;
8718  }
8719 
8732  function setListOfQuestionsSettings($a_value = 0)
8733  {
8734  $this->show_summary = $a_value;
8735  }
8736 
8744  {
8745  if (($this->show_summary & 1) > 0)
8746  {
8747  return TRUE;
8748  }
8749  else
8750  {
8751  return FALSE;
8752  }
8753  }
8754 
8761  function setListOfQuestions($a_value = TRUE)
8762  {
8763  if ($a_value)
8764  {
8765  $this->show_summary = 1;
8766  }
8767  else
8768  {
8769  $this->show_summary = 0;
8770  }
8771  }
8772 
8780  {
8781  if (($this->show_summary & 2) > 0)
8782  {
8783  return TRUE;
8784  }
8785  else
8786  {
8787  return FALSE;
8788  }
8789  }
8790 
8797  function setListOfQuestionsStart($a_value = TRUE)
8798  {
8799  if ($a_value && $this->getListOfQuestions())
8800  {
8801  $this->show_summary = $this->show_summary | 2;
8802  }
8803  if (!$a_value && $this->getListOfQuestions())
8804  {
8805  if ($this->getListOfQuestionsStart())
8806  {
8807  $this->show_summary = $this->show_summary ^ 2;
8808  }
8809  }
8810  }
8811 
8819  {
8820  if (($this->show_summary & 4) > 0)
8821  {
8822  return TRUE;
8823  }
8824  else
8825  {
8826  return FALSE;
8827  }
8828  }
8829 
8836  function setListOfQuestionsEnd($a_value = TRUE)
8837  {
8838  if ($a_value && $this->getListOfQuestions())
8839  {
8840  $this->show_summary = $this->show_summary | 4;
8841  }
8842  if (!$a_value && $this->getListOfQuestions())
8843  {
8844  if ($this->getListOfQuestionsEnd())
8845  {
8846  $this->show_summary = $this->show_summary ^ 4;
8847  }
8848  }
8849  }
8850 
8858  {
8859  if (($this->show_summary & 8) > 0)
8860  {
8861  return TRUE;
8862  }
8863  else
8864  {
8865  return FALSE;
8866  }
8867  }
8868 
8875  function setListOfQuestionsDescription($a_value = TRUE)
8876  {
8877  if ($a_value && $this->getListOfQuestions())
8878  {
8879  $this->show_summary = $this->show_summary | 8;
8880  }
8881  if (!$a_value && $this->getListOfQuestions())
8882  {
8883  if ($this->getListOfQuestionsDescription())
8884  {
8885  $this->show_summary = $this->show_summary ^ 8;
8886  }
8887  }
8888  }
8889 
8897  {
8898  return ($this->results_presentation) ? $this->results_presentation : 0;
8899  }
8900 
8908  {
8909  if (($this->results_presentation & 1) > 0)
8910  {
8911  return TRUE;
8912  }
8913  else
8914  {
8915  return FALSE;
8916  }
8917  }
8918 
8926  {
8927  if (($this->results_presentation & 2) > 0)
8928  {
8929  return TRUE;
8930  }
8931  else
8932  {
8933  return FALSE;
8934  }
8935  }
8936 
8944  {
8945  if (($this->results_presentation & 4) > 0)
8946  {
8947  return TRUE;
8948  }
8949  else
8950  {
8951  return FALSE;
8952  }
8953  }
8954 
8962  {
8963  if (($this->results_presentation & 8) > 0)
8964  {
8965  return TRUE;
8966  }
8967  else
8968  {
8969  return FALSE;
8970  }
8971  }
8972 
8980  {
8981  if (($this->results_presentation & 16) > 0)
8982  {
8983  return TRUE;
8984  }
8985  else
8986  {
8987  return FALSE;
8988  }
8989  }
8990 
8998  {
8999  if (($this->results_presentation & 32) > 0)
9000  {
9001  return TRUE;
9002  }
9003  else
9004  {
9005  return FALSE;
9006  }
9007  }
9008 
9014  {
9015  if (($this->results_presentation & 64) > 0)
9016  {
9017  return TRUE;
9018  }
9019  else
9020  {
9021  return FALSE;
9022  }
9023  }
9024 
9031  function setResultsPresentation($a_results_presentation = 3)
9032  {
9033  $this->results_presentation = $a_results_presentation;
9034  }
9035 
9044  function setShowPassDetails($a_details = 1)
9045  {
9046  if ($a_details)
9047  {
9048  $this->results_presentation = $this->results_presentation | 1;
9049  }
9050  else
9051  {
9052  if ($this->getShowPassDetails())
9053  {
9054  $this->results_presentation = $this->results_presentation ^ 1;
9055  }
9056  }
9057  }
9058 
9065  function setShowSolutionDetails($a_details = 1)
9066  {
9067  if ($a_details)
9068  {
9069  $this->results_presentation = $this->results_presentation | 2;
9070  }
9071  else
9072  {
9073  if ($this->getShowSolutionDetails())
9074  {
9075  $this->results_presentation = $this->results_presentation ^ 2;
9076  }
9077  }
9078  }
9079 
9086  function canShowSolutionPrintview($user_id = NULL)
9087  {
9088  return $this->getShowSolutionPrintview();
9089  }
9090 
9097  function setShowSolutionPrintview($a_printview = 1)
9098  {
9099  if ($a_printview)
9100  {
9101  $this->results_presentation = $this->results_presentation | 4;
9102  }
9103  else
9104  {
9105  if ($this->getShowSolutionPrintview())
9106  {
9107  $this->results_presentation = $this->results_presentation ^ 4;
9108  }
9109  }
9110  }
9111 
9118  function setShowSolutionFeedback($a_feedback = TRUE)
9119  {
9120  if ($a_feedback)
9121  {
9122  $this->results_presentation = $this->results_presentation | 8;
9123  }
9124  else
9125  {
9126  if ($this->getShowSolutionFeedback())
9127  {
9128  $this->results_presentation = $this->results_presentation ^ 8;
9129  }
9130  }
9131  }
9132 
9139  function setShowSolutionAnswersOnly($a_full = TRUE)
9140  {
9141  if ($a_full)
9142  {
9143  $this->results_presentation = $this->results_presentation | 16;
9144  }
9145  else
9146  {
9147  if ($this->getShowSolutionAnswersOnly())
9148  {
9149  $this->results_presentation = $this->results_presentation ^ 16;
9150  }
9151  }
9152  }
9153 
9160  function setShowSolutionSignature($a_signature = FALSE)
9161  {
9162  if ($a_signature)
9163  {
9164  $this->results_presentation = $this->results_presentation | 32;
9165  }
9166  else
9167  {
9168  if ($this->getShowSolutionSignature())
9169  {
9170  $this->results_presentation = $this->results_presentation ^ 32;
9171  }
9172  }
9173  }
9174 
9181  function setShowSolutionSuggested($a_solution = FALSE)
9182  {
9183  if ($a_solution)
9184  {
9185  $this->results_presentation = $this->results_presentation | 64;
9186  }
9187  else
9188  {
9189  if ($this->getShowSolutionSuggested())
9190  {
9191  $this->results_presentation = $this->results_presentation ^ 64;
9192  }
9193  }
9194  }
9195 
9202  {
9203  // create a 5 character code
9204  $codestring = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
9205  mt_srand();
9206  $code = "";
9207  for ($i = 1; $i <=5; $i++)
9208  {
9209  $index = mt_rand(0, strlen($codestring)-1);
9210  $code .= substr($codestring, $index, 1);
9211  }
9212 
9213  // todo: separate the code "building" from the code "creation", because the following self calling construction
9214  // is not testable and leads btw in non required double queries if a random value is allready used in db anytime
9215 
9216  // verify it against the database
9217  while ($this->isAccessCodeUsed($code))
9218  {
9219  $code = $this->createNewAccessCode();
9220  }
9221  return $code;
9222  }
9223 
9224  function isAccessCodeUsed($code)
9225  {
9226  global $ilDB;
9227 
9228  $result = $ilDB->queryF("SELECT anonymous_id FROM tst_active WHERE test_fi = %s AND anonymous_id = %s",
9229  array('integer', 'text'),
9230  array($this->getTestId(), $code)
9231  );
9232  return ($result->numRows() > 0) ? true : false;
9233  }
9234 
9235  public static function _getUserIdFromActiveId($active_id)
9236  {
9237  global $ilDB;
9238  $result = $ilDB->queryF("SELECT user_fi FROM tst_active WHERE active_id = %s",
9239  array('integer'),
9240  array($active_id)
9241  );
9242  if ($result->numRows())
9243  {
9244  $row = $ilDB->fetchAssoc($result);
9245  return $row["user_fi"];
9246  }
9247  else
9248  {
9249  return -1;
9250  }
9251  }
9252 
9254  {
9255  $id = $this->getTestId();
9256  if (!is_array($_SESSION["tst_access_code"]))
9257  {
9258  return "";
9259  }
9260  else
9261  {
9262  return $_SESSION["tst_access_code"]["$id"];
9263  }
9264  }
9265 
9266  function setAccessCodeSession($access_code)
9267  {
9268  $id = $this->getTestId();
9269  if (!is_array($_SESSION["tst_access_code"]))
9270  {
9271  $_SESSION["tst_access_code"] = array();
9272  }
9273  $_SESSION["tst_access_code"]["$id"] = $access_code;
9274  }
9275 
9277  {
9278  $id = $this->getTestId();
9279  unset($_SESSION["tst_access_code"]["$id"]);
9280  }
9281 
9282  function getAllowedUsers()
9283  {
9284  return ($this->allowedUsers) ? $this->allowedUsers : 0;
9285  }
9286 
9287  function setAllowedUsers($a_allowed_users)
9288  {
9289  $this->allowedUsers = $a_allowed_users;
9290  }
9291 
9293  {
9294  return ($this->allowedUsersTimeGap) ? $this->allowedUsersTimeGap : 0;
9295  }
9296 
9297  function setAllowedUsersTimeGap($a_allowed_users_time_gap)
9298  {
9299  $this->allowedUsersTimeGap = $a_allowed_users_time_gap;
9300  }
9301 
9303  {
9304  global $ilDB;
9305 
9306  $nr_of_users = $this->getAllowedUsers();
9307  $time_gap = ($this->getAllowedUsersTimeGap()) ? $this->getAllowedUsersTimeGap() : 60;
9308  if (($nr_of_users > 0) && ($time_gap > 0))
9309  {
9310  $now = mktime();
9311  $time_border = $now - $time_gap;
9312  $str_time_border = strftime("%Y%m%d%H%M%S", $time_border);
9313  $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",
9314  array('integer', 'integer'),
9315  array($time_border, $this->getTestId())
9316  );
9317  if ($result->numRows() >= $nr_of_users)
9318  {
9319  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
9321  {
9322  $this->logAction($this->lng->txtlng("assessment", "log_could_not_enter_test_due_to_simultaneous_users", ilObjAssessmentFolder::_getLogLanguage()));
9323  }
9324  return FALSE;
9325  }
9326  else
9327  {
9328  return TRUE;
9329  }
9330  }
9331  return TRUE;
9332  }
9333 
9334  function _getLastAccess($active_id)
9335  {
9336  global $ilDB;
9337 
9338  $result = $ilDB->queryF("SELECT finished FROM tst_times WHERE active_fi = %s ORDER BY finished DESC",
9339  array('integer'),
9340  array($active_id)
9341  );
9342  if ($result->numRows())
9343  {
9344  $row = $ilDB->fetchAssoc($result);
9345  return $row["finished"];
9346  }
9347  return "";
9348  }
9349 
9357  function isHTML($a_text)
9358  {
9359  if (preg_match("/<[^>]*?>/", $a_text))
9360  {
9361  return TRUE;
9362  }
9363  else
9364  {
9365  return FALSE;
9366  }
9367  }
9368 
9376  function QTIMaterialToString($a_material)
9377  {
9378  $result = "";
9379  for ($i = 0; $i < $a_material->getMaterialCount(); $i++)
9380  {
9381  $material = $a_material->getMaterial($i);
9382  if (strcmp($material["type"], "mattext") == 0)
9383  {
9384  $result .= $material["material"]->getContent();
9385  }
9386  if (strcmp($material["type"], "matimage") == 0)
9387  {
9388  $matimage = $material["material"];
9389  if (preg_match("/(il_([0-9]+)_mob_([0-9]+))/", $matimage->getLabel(), $matches))
9390  {
9391  // import an mediaobject which was inserted using tiny mce
9392  if (!is_array($_SESSION["import_mob_xhtml"])) $_SESSION["import_mob_xhtml"] = array();
9393  array_push($_SESSION["import_mob_xhtml"], array("mob" => $matimage->getLabel(), "uri" => $matimage->getUri()));
9394  }
9395  }
9396  }
9397  global $ilLog;
9398  $ilLog->write(print_r($_SESSION["import_mob_xhtml"], true));
9399  return $result;
9400  }
9401 
9410  function addQTIMaterial(&$a_xml_writer, $a_material)
9411  {
9412  include_once "./Services/RTE/classes/class.ilRTE.php";
9413  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
9414 
9415  $a_xml_writer->xmlStartTag("material");
9416  $attrs = array(
9417  "texttype" => "text/plain"
9418  );
9419  if ($this->isHTML($a_material))
9420  {
9421  $attrs["texttype"] = "text/xhtml";
9422  }
9423  $a_xml_writer->xmlElement("mattext", $attrs, ilRTE::_replaceMediaObjectImageSrc($a_material, 0));
9424 
9425  $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId());
9426  foreach ($mobs as $mob)
9427  {
9428  $moblabel = "il_" . IL_INST_ID . "_mob_" . $mob;
9429  if (strpos($a_material, "mm_$mob") !== FALSE)
9430  {
9431  if (ilObjMediaObject::_exists($mob))
9432  {
9433  $mob_obj =& new ilObjMediaObject($mob);
9434  $imgattrs = array(
9435  "label" => $moblabel,
9436  "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle()
9437  );
9438  }
9439  $a_xml_writer->xmlElement("matimage", $imgattrs, NULL);
9440  }
9441  }
9442  $a_xml_writer->xmlEndTag("material");
9443  }
9444 
9451  function prepareTextareaOutput($txt_output, $prepare_for_latex_output = FALSE)
9452  {
9453  include_once "./Services/Utilities/classes/class.ilUtil.php";
9454  return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output);
9455  }
9456 
9463  function saveCertificateVisibility($a_value)
9464  {
9465  global $ilDB;
9466 
9467  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET certificate_visibility = %s, tstamp = %s WHERE test_id = %s",
9468  array('text', 'integer', 'integer'),
9469  array($a_value, time(), $this->getTestId())
9470  );
9471  }
9472 
9480  {
9481  return (strlen($this->certificate_visibility)) ? $this->certificate_visibility : 0;
9482  }
9483 
9490  function setCertificateVisibility($a_value)
9491  {
9492  $this->certificate_visibility = $a_value;
9493  }
9494 
9501  function getAnonymity()
9502  {
9503  return ($this->anonymity) ? 1 : 0;
9504  }
9505 
9512  function setAnonymity($a_value = 0)
9513  {
9514  switch ($a_value)
9515  {
9516  case 1:
9517  $this->anonymity = 1;
9518  break;
9519  default:
9520  $this->anonymity = 0;
9521  break;
9522  }
9523  }
9524 
9531  function getShowCancel()
9532  {
9533  return ($this->show_cancel) ? 1 : 0;
9534  }
9535 
9542  function setShowCancel($a_value = 1)
9543  {
9544  switch ($a_value)
9545  {
9546  case 1:
9547  $this->show_cancel = 1;
9548  break;
9549  default:
9550  $this->show_cancel = 0;
9551  break;
9552  }
9553  }
9554 
9561  function getShowMarker()
9562  {
9563  return ($this->show_marker) ? 1 : 0;
9564  }
9565 
9572  function setShowMarker($a_value = 1)
9573  {
9574  switch ($a_value)
9575  {
9576  case 1:
9577  $this->show_marker = 1;
9578  break;
9579  default:
9580  $this->show_marker = 0;
9581  break;
9582  }
9583  }
9584 
9592  {
9593  return ($this->fixed_participants) ? 1 : 0;
9594  }
9595 
9602  function setFixedParticipants($a_value = 1)
9603  {
9604  switch ($a_value)
9605  {
9606  case 1:
9607  $this->fixed_participants = 1;
9608  break;
9609  default:
9610  $this->fixed_participants = 0;
9611  break;
9612  }
9613  }
9614 
9622  function _lookupAnonymity($a_obj_id)
9623  {
9624  global $ilDB;
9625 
9626  $result = $ilDB->queryF("SELECT anonymity FROM tst_tests WHERE obj_fi = %s",
9627  array('integer'),
9628  array($a_obj_id)
9629  );
9630  while($row = $ilDB->fetchAssoc($result))
9631  {
9632  return $row['anonymity'];
9633  }
9634  return 0;
9635  }
9636 
9644  function _lookupRandomTestFromActiveId($active_id)
9645  {
9646  global $ilDB;
9647 
9648  $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",
9649  array('integer'),
9650  array($active_id)
9651  );
9652  while($row = $ilDB->fetchAssoc($result))
9653  {
9654  return $row['random_test'];
9655  }
9656  return 0;
9657  }
9658 
9667  function userLookupFullName($user_id, $overwrite_anonymity = FALSE, $sorted_order = FALSE, $suffix = "")
9668  {
9669  if ($this->getAnonymity() && !$overwrite_anonymity)
9670  {
9671  return $this->lng->txt("unknown") . $suffix;
9672  }
9673  else
9674  {
9675  include_once './Services/User/classes/class.ilObjUser.php';
9676  $uname = ilObjUser::_lookupName($user_id);
9677  if (strlen($uname["firstname"].$uname["lastname"]) == 0) $uname["firstname"] = $this->lng->txt("deleted_user");
9678  if ($sorted_order)
9679  {
9680  return trim($uname["lastname"] . ", " . $uname["firstname"]) . $suffix;
9681  }
9682  else
9683  {
9684  return trim($uname["firstname"] . " " . $uname["lastname"]) . $suffix;
9685  }
9686  }
9687  }
9688 
9696  function getStartTestLabel($active_id)
9697  {
9698  if ($this->getNrOfTries() == 1)
9699  {
9700  return $this->lng->txt("tst_start_test");
9701  }
9702  $active_pass = $this->_getPass($active_id);
9703  $res = $this->getNrOfResultsForPass($active_id, $active_pass);
9704  if ($res == 0)
9705  {
9706  if ($active_pass == 0)
9707  {
9708  return $this->lng->txt("tst_start_test");
9709  }
9710  else
9711  {
9712  return $this->lng->txt("tst_start_new_test_pass");
9713  }
9714  }
9715  else
9716  {
9717  return $this->lng->txt("tst_resume_test");
9718  }
9719  }
9720 
9729  function &getAvailableDefaults($sortby = "name", $sortorder = "asc")
9730  {
9731  global $ilDB;
9732  global $ilUser;
9733 
9734  $result = $ilDB->queryF("SELECT * FROM tst_test_defaults WHERE user_fi = %s ORDER BY $sortby $sortorder",
9735  array('integer'),
9736  array($ilUser->getId())
9737  );
9738  $defaults = array();
9739  while ($row = $ilDB->fetchAssoc($result))
9740  {
9741  $defaults[$row["test_defaults_id"]] = $row;
9742  }
9743  return $defaults;
9744  }
9745 
9753  function &getTestDefaults($test_defaults_id)
9754  {
9755  return self::_getTestDefaults($test_defaults_id);
9756  }
9757 
9758  public static function _getTestDefaults($test_defaults_id)
9759  {
9760  global $ilDB;
9761 
9762  $result = $ilDB->queryF("SELECT * FROM tst_test_defaults WHERE test_defaults_id = %s",
9763  array('integer'),
9764  array($test_defaults_id)
9765  );
9766  if ($result->numRows() == 1)
9767  {
9768  $row = $ilDB->fetchAssoc($result);
9769  return $row;
9770  }
9771  else
9772  {
9773  return NULL;
9774  }
9775  }
9776 
9783  function deleteDefaults($test_default_id)
9784  {
9785  global $ilDB;
9786  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_defaults WHERE test_defaults_id = %s",
9787  array('integer'),
9788  array($test_default_id)
9789  );
9790  }
9791 
9798  function addDefaults($a_name)
9799  {
9800  global $ilDB;
9801  global $ilUser;
9802  $testsettings = array(
9803  "TitleOutput" => $this->getTitleOutput(),
9804  "PassScoring" => $this->getPassScoring(),
9805  "Introduction" => $this->getIntroduction(),
9806  "FinalStatement" => $this->getFinalStatement(),
9807  "ShowInfo" => $this->getShowInfo(),
9808  "ForceJS" => $this->getForceJS(),
9809  "CustomStyle" => $this->getCustomStyle(),
9810  "ShowFinalStatement" => $this->getShowFinalStatement(),
9811  "SequenceSettings" => $this->getSequenceSettings(),
9812  "ScoreReporting" => $this->getScoreReporting(),
9813  "ScoreCutting" => $this->getScoreCutting(),
9814  'SpecificAnswerFeedback' => $this->getSpecificAnswerFeedback(),
9815  'PrintBsWithRes' => (int)$this->isBestSolutionPrintedWithResult(),
9816  "InstantFeedbackSolution" => $this->getInstantFeedbackSolution(),
9817  "AnswerFeedback" => $this->getAnswerFeedback(),
9818  "AnswerFeedbackPoints" => $this->getAnswerFeedbackPoints(),
9819  "ResultsPresentation" => $this->getResultsPresentation(),
9820  "Anonymity" => $this->getAnonymity(),
9821  "ShowCancel" => $this->getShowCancel(),
9822  "ShowMarker" => $this->getShowMarker(),
9823  "ReportingDate" => $this->getReportingDate(),
9824  "NrOfTries" => $this->getNrOfTries(),
9825  "Shuffle" => $this->getShuffleQuestions(),
9826  "Kiosk" => $this->getKiosk(),
9827  "UsePreviousAnswers" => $this->getUsePreviousAnswers(),
9828  "ProcessingTime" => $this->getProcessingTime(),
9829  "EnableProcessingTime" => $this->getEnableProcessingTime(),
9830  "ResetProcessingTime" => $this->getResetProcessingTime(),
9831  "StartingTime" => $this->getStartingTime(),
9832  "EndingTime" => $this->getEndingTime(),
9833  "ECTSOutput" => $this->getECTSOutput(),
9834  "ECTSFX" => $this->getECTSFX(),
9835  "ECTSGrades" => $this->getECTSGrades(),
9836  "isRandomTest" => $this->isRandomTest(),
9837  "RandomQuestionCount" => $this->getRandomQuestionCount(),
9838  "CountSystem" => $this->getCountSystem(),
9839  "MCScoring" => $this->getMCScoring(),
9840  "mailnotification" => $this->getMailNotification(),
9841  "mailnottype" => $this->getMailNotificationType(),
9842  "exportsettings" => $this->getExportSettings(),
9843  "ListOfQuestionsSettings" => $this->getListOfQuestionsSettings(),
9844  'obligations_enabled' => (int)$this->areObligationsEnabled(),
9845  'offer_question_hints' => (int)$this->isOfferingQuestionHintsEnabled(),
9846  'pass_deletion_allowed' => (int)$this->isPassDeletionAllowed(),
9847  'autosave' => (int)$this->getAutosave(),
9848  'autosave_ival' => (int)$this->getAutosaveIval(),
9849  'fixed_participants' => $this->getFixedParticipants(),
9850  'allowedusers' => $this->getAllowedUsers(),
9851  'alloweduserstimegap' => $this->getAllowedUsersTimeGap(),
9852 
9853  'highscore_enabled' => $this->getHighscoreEnabled(),
9854  'highscore_anon' => $this->getHighscoreAnon(),
9855  'highscore_achieved_ts' => $this->getHighscoreAchievedTS(),
9856  'highscore_score' => $this->getHighscoreScore(),
9857  'highscore_percentage' => $this->getHighscorePercentage(),
9858  'highscore_hints' => $this->getHighscoreHints(),
9859  'highscore_wtime' => $this->getHighscoreWTime(),
9860  'highscore_own_table' => $this->getHighscoreOwnTable(),
9861  'highscore_top_table' => $this->getHighscoreTopTable(),
9862  'highscore_top_num' => $this->getHighscoreTopNum(),
9863  'password' => $this->getPassword()
9864  );
9865  $next_id = $ilDB->nextId('tst_test_defaults');
9866  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_defaults (test_defaults_id, name, user_fi, defaults, marks, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
9867  array('integer', 'text', 'integer', 'text', 'text', 'integer'),
9868  array($next_id, $a_name, $ilUser->getId(), serialize($testsettings), serialize($this->mark_schema), time())
9869  );
9870  }
9871 
9879  function applyDefaults($test_defaults)
9880  {
9881  $testsettings = unserialize($test_defaults["defaults"]);
9882  include_once "./Modules/Test/classes/class.assMarkSchema.php";
9883  $this->mark_schema = unserialize($test_defaults["marks"]);
9884 
9885  $this->setTitleOutput($testsettings["TitleOutput"]);
9886  $this->setPassScoring($testsettings["PassScoring"]);
9887  $this->setIntroduction($testsettings["Introduction"]);
9888  $this->setFinalStatement($testsettings["FinalStatement"]);
9889  $this->setShowInfo($testsettings["ShowInfo"]);
9890  $this->setForceJS($testsettings["ForceJS"]);
9891  $this->setCustomStyle($testsettings["CustomStyle"]);
9892  $this->setShowFinalStatement($testsettings["ShowFinalStatement"]);
9893  $this->setSequenceSettings($testsettings["SequenceSettings"]);
9894  $this->setScoreReporting($testsettings["ScoreReporting"]);
9895  $this->setScoreCutting($testsettings['ScoreCutting']);
9896  $this->setSpecificAnswerFeedback($testsettings['SpecificAnswerFeedback']);
9897  $this->setPrintBestSolutionWithResult((bool)$testsettings['PrintBsWithRes']);
9898  $this->setInstantFeedbackSolution($testsettings["InstantFeedbackSolution"]);
9899  $this->setAnswerFeedback($testsettings["AnswerFeedback"]);
9900  $this->setAnswerFeedbackPoints($testsettings["AnswerFeedbackPoints"]);
9901  $this->setResultsPresentation($testsettings["ResultsPresentation"]);
9902  $this->setAnonymity($testsettings["Anonymity"]);
9903  $this->setShowCancel($testsettings["ShowCancel"]);
9904  $this->setShuffleQuestions($testsettings["Shuffle"]);
9905  $this->setShowMarker($testsettings["ShowMarker"]);
9906  $this->setReportingDate($testsettings["ReportingDate"]);
9907  $this->setNrOfTries($testsettings["NrOfTries"]);
9908  $this->setUsePreviousAnswers($testsettings["UsePreviousAnswers"]);
9909  $this->setProcessingTime($testsettings["ProcessingTime"]);
9910  $this->setResetProcessingTime($testsettings["ResetProcessingTime"]);
9911  $this->setEnableProcessingTime($testsettings["EnableProcessingTime"]);
9912  $this->setStartingTime($testsettings["StartingTime"]);
9913  $this->setKiosk($testsettings["Kiosk"]);
9914  $this->setEndingTime($testsettings["EndingTime"]);
9915  $this->setECTSOutput($testsettings["ECTSOutput"]);
9916  $this->setECTSFX($testsettings["ECTSFX"]);
9917  $this->setECTSGrades($testsettings["ECTSGrades"]);
9918  $this->setRandomTest($testsettings["isRandomTest"]);
9919  $this->setRandomQuestionCount($testsettings["RandomQuestionCount"]);
9920  $this->setCountSystem($testsettings["CountSystem"]);
9921  $this->setMCScoring($testsettings["MCScoring"]);
9922  $this->setMailNotification($testsettings["mailnotification"]);
9923  $this->setMailNotificationType($testsettings["mailnottype"]);
9924  $this->setExportSettings($testsettings['exportsettings']);
9925  $this->setListOfQuestionsSettings($testsettings["ListOfQuestionsSettings"]);
9926  $this->setObligationsEnabled($testsettings["obligations_enabled"]);
9927  $this->setOfferingQuestionHintsEnabled($testsettings["offer_question_hints"]);
9928  $this->setHighscoreEnabled($testsettings['highscore_enabled']);
9929  $this->setHighscoreAnon($testsettings['highscore_anon']);
9930  $this->setHighscoreAchievedTS($testsettings['highscore_achieved_ts']);
9931  $this->setHighscoreScore($testsettings['highscore_score']);
9932  $this->setHighscorePercentage($testsettings['highscore_percentage']);
9933  $this->setHighscoreHints($testsettings['highscore_hints']);
9934  $this->setHighscoreWTime($testsettings['highscore_wtime']);
9935  $this->setHighscoreOwnTable($testsettings['highscore_own_table']);
9936  $this->setHighscoreTopTable($testsettings['highscore_top_table']);
9937  $this->setHighscoreTopNum($testsettings['highscore_top_num']);
9938  $this->setPassDeletionAllowed($testsettings['pass_deletion_allowed']);
9939 
9940  $this->setAutosave($testsettings['autosave']);
9941  $this->setAutosaveIval($testsettings['autosave_ival']);
9942  $this->setFixedParticipants($testsettings['fixed_participants'] );
9943  $this->setAllowedUsers($testsettings['allowedusers']);
9944  $this->setAllowedUsersTimeGap($testsettings['alloweduserstimegap']);
9945  $this->setPassword($testsettings['password']);
9946  $this->saveToDb();
9947 
9948  return true;
9949  }
9950 
9958  function processPrintoutput2FO($print_output)
9959  {
9960  if (extension_loaded("tidy"))
9961  {
9962  $config = array(
9963  "indent" => false,
9964  "output-xml" => true,
9965  "numeric-entities" => true
9966  );
9967  $tidy = new tidy();
9968  $tidy->parseString($print_output, $config, 'utf8');
9969  $tidy->cleanRepair();
9970  $print_output = tidy_get_output($tidy);
9971  $print_output = preg_replace("/^.*?(<html)/", "\\1", $print_output);
9972  }
9973  else
9974  {
9975  $print_output = str_replace("&nbsp;", "&#160;", $print_output);
9976  $print_output = str_replace("&otimes;", "X", $print_output);
9977  }
9978  $xsl = file_get_contents("./Modules/Test/xml/question2fo.xsl");
9979 
9980  // additional font support
9981  $xsl = str_replace(
9982  'font-family="Helvetica, unifont"',
9983  'font-family="'.$GLOBALS['ilSetting']->get('rpc_pdf_font','Helvetica, unifont').'"',
9984  $xsl
9985  );
9986 
9987  $args = array( '/_xml' => $print_output, '/_xsl' => $xsl );
9988  $xh = xslt_create();
9989  $params = array();
9990  $output = xslt_process($xh, "arg:/_xml", "arg:/_xsl", NULL, $args, $params);
9991  xslt_error($xh);
9992  xslt_free($xh);
9993  return $output;
9994  }
9995 
10002  public function deliverPDFfromHTML($content, $title = NULL)
10003  {
10004  $content = preg_replace("/href=\".*?\"/", "", $content);
10005  $printbody = new ilTemplate("tpl.il_as_tst_print_body.html", TRUE, TRUE, "Modules/Test");
10006  $printbody->setVariable("TITLE", ilUtil::prepareFormOutput($this->getTitle()));
10007  $printbody->setVariable("ADM_CONTENT", $content);
10008  $printbody->setCurrentBlock("css_file");
10009  $printbody->setVariable("CSS_FILE", $this->getTestStyleLocation("filesystem"));
10010  $printbody->parseCurrentBlock();
10011  $printbody->setCurrentBlock("css_file");
10012  $printbody->setVariable("CSS_FILE", ilUtil::getStyleSheetLocation("filesystem", "delos.css"));
10013  $printbody->parseCurrentBlock();
10014  $printoutput = $printbody->get();
10015  $html = str_replace("href=\"./", "href=\"" . ILIAS_HTTP_PATH . "/", $printoutput);
10016  $html = preg_replace("/<div id=\"dontprint\">.*?<\\/div>/ims", "", $html);
10017  if (extension_loaded("tidy"))
10018  {
10019  $config = array(
10020  "indent" => false,
10021  "output-xml" => true,
10022  "numeric-entities" => true
10023  );
10024  $tidy = new tidy();
10025  $tidy->parseString($html, $config, 'utf8');
10026  $tidy->cleanRepair();
10027  $html = tidy_get_output($tidy);
10028  $html = preg_replace("/^.*?(<html)/", "\\1", $html);
10029  }
10030  else
10031  {
10032  $html = str_replace("&nbsp;", "&#160;", $html);
10033  $html = str_replace("&otimes;", "X", $html);
10034  }
10035  $html = preg_replace("/src=\".\\//ims", "src=\"" . ILIAS_HTTP_PATH . "/", $html);
10036  // #12866
10037  $html = preg_replace('/&(?!([\w]+?;|#[\d]+?;))/', '&amp;', $html);
10038  $fo = $this->processPrintoutput2FO($html);
10039  if( $fo === false )
10040  {
10041  global $ilLog, $ilias;
10042  $ilLog->write(__METHOD__.': could not process HTML to FO: '.$html);
10043  $ilias->raiseError(__METHOD__.': could not process HTML to FO: '.htmlentities($html));
10044  }
10045  $this->deliverPDFfromFO($fo, $title);
10046  }
10047 
10054  public function deliverPDFfromFO($fo, $title = null)
10055  {
10056  global $ilLog;
10057 
10058  include_once "./Services/Utilities/classes/class.ilUtil.php";
10059  $fo_file = ilUtil::ilTempnam() . ".fo";
10060  $fp = fopen($fo_file, "w"); fwrite($fp, $fo); fclose($fp);
10061 
10062  include_once './Services/WebServices/RPC/classes/class.ilRpcClientFactory.php';
10063  try
10064  {
10065  $pdf_base64 = ilRpcClientFactory::factory('RPCTransformationHandler')->ilFO2PDF($fo);
10066  $filename = (strlen($title)) ? $title : $this->getTitle();
10067  ilUtil::deliverData($pdf_base64->scalar, ilUtil::getASCIIFilename($filename) . ".pdf", "application/pdf", false, true);
10068  return true;
10069  }
10070  catch(XML_RPC2_FaultException $e)
10071  {
10072  $ilLog->write(__METHOD__.': '.$e->getMessage());
10073  return false;
10074  }
10075  catch(Exception $e)
10076  {
10077  $ilLog->write(__METHOD__.': '.$e->getMessage());
10078  return false;
10079  }
10080 
10081  /*
10082  include_once "./Services/Transformation/classes/class.ilFO2PDF.php";
10083  $fo2pdf = new ilFO2PDF();
10084  $fo2pdf->setFOString($fo);
10085  $result = $fo2pdf->send();
10086  $filename = (strlen($title)) ? $title : $this->getTitle();
10087  ilUtil::deliverData($result, ilUtil::getASCIIFilename($filename) . ".pdf", "application/pdf", false, true);
10088  */
10089  }
10090 
10100  static function getManualFeedback($active_id, $question_id, $pass)
10101  {
10102  global $ilDB;
10103  $feedback = "";
10104  $result = $ilDB->queryF("SELECT feedback FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
10105  array('integer', 'integer', 'integer'),
10106  array($active_id, $question_id, $pass)
10107  );
10108  if ($result->numRows())
10109  {
10110  $row = $ilDB->fetchAssoc($result);
10111  include_once("./Services/RTE/classes/class.ilRTE.php");
10112  $feedback = ilRTE::_replaceMediaObjectImageSrc($row["feedback"], 1);
10113  }
10114  return $feedback;
10115  }
10116 
10127  function saveManualFeedback($active_id, $question_id, $pass, $feedback)
10128  {
10129  global $ilDB;
10130 
10131  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
10132  array('integer', 'integer', 'integer'),
10133  array($active_id, $question_id, $pass)
10134  );
10135 
10136  if (strlen($feedback))
10137  {
10138  $next_id = $ilDB->nextId('tst_manual_fb');
10139 
10141  $result = $ilDB->insert('tst_manual_fb', array(
10142  'manual_feedback_id' => array( 'integer', $next_id ),
10143  'active_fi' => array( 'integer', $active_id ),
10144  'question_fi' => array( 'integer', $question_id ),
10145  'pass' => array( 'integer', $pass),
10146  'feedback' => array( 'clob', ilRTE::_replaceMediaObjectImageSrc( $feedback, 0) ),
10147  'tstamp' => array( 'integer', time() ),
10148  )
10149  );
10150 
10151  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
10153  {
10154  global $lng, $ilUser;
10155  include_once "./Modules/Test/classes/class.ilObjTestAccess.php";
10156  $username = ilObjTestAccess::_getParticipantData($active_id);
10157  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
10158  $this->logAction(sprintf($lng->txtlng("assessment", "log_manual_feedback", ilObjAssessmentFolder::_getLogLanguage()), $ilUser->getFullname() . " (" . $ilUser->getLogin() . ")", $username, assQuestion::_getQuestionTitle($question_id), $feedback));
10159  }
10160  }
10161  if (PEAR::isError($result))
10162  {
10163  global $ilias;
10164  $ilias->raiseError($result->getMessage());
10165  }
10166  else
10167  {
10168  return TRUE;
10169  }
10170  }
10171 
10180  {
10181  global $ilUser;
10182  if (strcmp($_GET["tst_javascript"], "0") == 0) return FALSE;
10183  if ($this->getForceJS()) return TRUE;
10184  $assessmentSetting = new ilSetting("assessment");
10185  return ($ilUser->getPref("tst_javascript") === FALSE) ? $assessmentSetting->get("use_javascript") : $ilUser->getPref("tst_javascript");
10186  }
10187 
10194  function &createTestSession()
10195  {
10196  global $ilUser;
10197 
10198  include_once "./Modules/Test/classes/class.ilTestSession.php";
10199  $testSession = FALSE;
10200  $testSession = new ilTestSession();
10201  $testSession->setRefId($this->getRefId());
10202  $testSession->setTestId($this->getTestId());
10203  $testSession->setUserId($ilUser->getId());
10204  $testSession->setAnonymousId($_SESSION["tst_access_code"][$this->getTestId()]);
10205  $testSession->saveToDb();
10206  $this->testSession =& $testSession;
10207  return $this->testSession;
10208  }
10209 
10215  public function setTestId($a_id)
10216  {
10217  $this->test_id = $a_id;
10218  }
10219 
10227  function &setTestSession($active_id = "")
10228  {
10229  if (is_object($this->testSession) && ($this->testSession->getActiveId() > 0)) return $this->testSession;
10230 
10231  global $ilUser;
10232 
10233  include_once "./Modules/Test/classes/class.ilTestSession.php";
10234  $testSession = FALSE;
10235  if ($active_id > 0)
10236  {
10237  $testSession = new ilTestSession($active_id);
10238  $testSession->setRefId($this->getRefId());
10239  }
10240  else
10241  {
10242  $testSession = new ilTestSession();
10243  $testSession->setRefId($this->getRefId());
10244  $testSession->loadTestSession($this->getTestId(), $ilUser->getId(), $_SESSION["tst_access_code"][$this->getTestId()]);
10245  }
10246  $this->testSession =& $testSession;
10247  return $this->testSession;
10248  }
10249 
10256  function &getTestSession($active_id = "")
10257  {
10258  if (is_object($this->testSession) && ($this->testSession->getActiveId() > 0)) return $this->testSession;
10259  return $this->setTestSession($active_id);
10260  }
10261 
10262  function &createTestSequence($active_id, $pass, $shuffle)
10263  {
10264  include_once "./Modules/Test/classes/class.ilTestSequence.php";
10265  $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
10266  if (!$this->testSequence->hasSequence())
10267  {
10268  $this->testSequence->createNewSequence($this->getQuestionCount(), $shuffle);
10269  $this->testSequence->saveToDb();
10270  }
10271  }
10272 
10278  function &getTestSequence($active_id = "", $pass = "")
10279  {
10280  if (is_object($this->testSequence) && ($this->testSequence->getActiveId() > 0)) return $this->testSequence;
10281 
10282  include_once "./Modules/Test/classes/class.ilTestSequence.php";
10283  if (($active_id > 0) && (strlen($pass)))
10284  {
10285  $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
10286  }
10287  else
10288  {
10289  $this->testSequence = new ilTestSequence($this->getTestSession()->getActiveId(), $this->getTestSession()->getPass(), $this->isRandomTest());
10290  }
10291  return $this->testSequence;
10292  }
10293 
10295  {
10296  if ($this->getTestSession()->getActiveId() > 0)
10297  {
10298  $result = $this->getTestResult($this->getTestSession()->getActiveId(), $this->getTestSession()->getPass(), TRUE);
10299  foreach ($result as $sequence => $question)
10300  {
10301  if (is_numeric($sequence))
10302  {
10303  if ($question["reached"] == $question["max"])
10304  {
10305  $this->getTestSequence()->hideQuestion($question["qid"]);
10306  }
10307  }
10308  }
10309  $this->getTestSequence()->saveToDb();
10310  }
10311  }
10312 
10321  function getDetailedTestResults($participants)
10322  {
10323  $results = array();
10324  if (count($participants))
10325  {
10326  foreach ($participants as $active_id => $user_rec)
10327  {
10328  $row = array();
10329  $reached_points = 0;
10330  $max_points = 0;
10331  foreach ($this->questions as $value)
10332  {
10333  $question =& ilObjTest::_instanciateQuestion($value);
10334  if (is_object($question))
10335  {
10336  $max_points += $question->getMaximumPoints();
10337  $reached_points += $question->getReachedPoints($active_id);
10338  if ($max_points > 0)
10339  {
10340  $percentvalue = $reached_points / $max_points;
10341  if ($percentvalue < 0) $percentvalue = 0.0;
10342  }
10343  else
10344  {
10345  $percentvalue = 0;
10346  }
10347  if ($this->getAnonymity())
10348  {
10349  $user_rec['firstname'] = "";
10350  $user_rec['lastname'] = $this->lng->txt("unknown");
10351  }
10352  $row = array(
10353  "user_id"=>$user_rec['usr_id'],
10354  "matriculation" => $user_rec['matriculation'],
10355  "lastname" => $user_rec['lastname'],
10356  "firstname" => $user_rec['firstname'],
10357  "login"=>$user_rec['login'],
10358  "question_id" => $question->getId(),
10359  "question_title" => $question->getTitle(),
10360  "reached_points" => $reached_points,
10361  "max_points" => $max_points
10362  );
10363  $results[] = $row;
10364  }
10365  }
10366  }
10367  }
10368  return $results;
10369  }
10370 
10375  {
10376  global $ilDB;
10377 
10378  $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",
10379  array('integer'),
10380  array($a_q_id)
10381  );
10382  $rec = $ilDB->fetchAssoc($result);
10383  return $rec["obj_id"];
10384  }
10385 
10392  function isPluginActive($a_pname)
10393  {
10394  global $ilPluginAdmin;
10395  if ($ilPluginAdmin->isActive(IL_COMP_MODULE, "TestQuestionPool", "qst", $a_pname))
10396  {
10397  return TRUE;
10398  }
10399  else
10400  {
10401  return FALSE;
10402  }
10403  }
10404 
10405  protected function getPassed($active_id)
10406  {
10407  global $ilDB;
10408 
10409  $result = $ilDB->queryF("SELECT passed FROM tst_result_cache WHERE active_fi = %s",
10410  array('integer'),
10411  array($active_id)
10412  );
10413  if ($result->numRows())
10414  {
10415  $row = $ilDB->fetchAssoc($result);
10416  return $row['passed'];
10417  }
10418  else
10419  {
10420  $counted_pass = ilObjTest::_getResultPass($active_id);
10421  $result_array =& $this->getTestResult($active_id, $counted_pass);
10422  return $result_array["test"]["passed"];
10423  }
10424  }
10425 
10431  function canShowCertificate($user_id, $active_id)
10432  {
10433  if ($this->canShowTestResults($user_id))
10434  {
10435  include_once "./Services/Certificate/classes/class.ilCertificate.php";
10436  include_once "./Modules/Test/classes/class.ilTestCertificateAdapter.php";
10437  $cert = new ilCertificate(new ilTestCertificateAdapter($this));
10438  if ($cert->isComplete())
10439  {
10440  $vis = $this->getCertificateVisibility();
10441  $showcert = FALSE;
10442  switch ($vis)
10443  {
10444  case 0:
10445  $showcert = TRUE;
10446  break;
10447  case 1:
10448  if ($this->getPassed($active_id))
10449  {
10450  $showcert = TRUE;
10451  }
10452  break;
10453  case 2:
10454  $showcert = FALSE;
10455  break;
10456  }
10457  if ($showcert)
10458  {
10459  return TRUE;
10460  }
10461  else
10462  {
10463  return FALSE;
10464  }
10465  }
10466  else
10467  {
10468  return FALSE;
10469  }
10470  }
10471  else
10472  {
10473  return FALSE;
10474  }
10475  }
10476 
10483  {
10484  global $ilDB;
10485  $query = "
10486  SELECT tst_test_result.active_fi, tst_test_result.question_fi, tst_test_result.pass
10487  FROM tst_test_result, tst_active, qpl_questions
10488  WHERE tst_active.active_id = tst_test_result.active_fi
10489  AND tst_active.test_fi = %s
10490  AND tst_test_result.question_fi = qpl_questions.question_id
10491  AND tst_test_result.question_fi = %s";
10492 
10493  $result = $ilDB->queryF( $query,
10494  array('integer', 'integer'),
10495  array($test_id, $question_id)
10496  );
10497  $foundusers = array();
10498  while ($row = $ilDB->fetchAssoc($result))
10499  {
10500  if (!array_key_exists($row["active_fi"], $foundusers))
10501  {
10502  $foundusers[$row["active_fi"]] = array();
10503  }
10504  array_push($foundusers[$row["active_fi"]], array("pass" => $row["pass"], "qid" => $row["question_fi"]));
10505  }
10506  return $foundusers;
10507  }
10508 
10514  public function hasPDFProcessing()
10515  {
10516  global $ilias;
10517 
10518  include_once './Services/WebServices/RPC/classes/class.ilRPCServerSettings.php';
10519  if(ilRPCServerSettings::getInstance()->isEnabled())
10520  {
10521  return TRUE;
10522  }
10523  else
10524  {
10525  return FALSE;
10526  }
10527  }
10528 
10534  public function getAggregatedResultsData()
10535  {
10536  $data =& $this->getCompleteEvaluationData();
10537  $foundParticipants =& $data->getParticipants();
10538  $results = array("overview" => array(), "questions" => array());
10539  if (count($foundParticipants))
10540  {
10541  $results["overview"][$this->lng->txt("tst_eval_total_persons")] = count($foundParticipants);
10542  $total_finished = $this->evalTotalFinished();
10543  $results["overview"][$this->lng->txt("tst_eval_total_finished")] = $total_finished;
10544  $average_time = $this->evalTotalStartedAverageTime();
10545  $diff_seconds = $average_time;
10546  $diff_hours = floor($diff_seconds/3600);
10547  $diff_seconds -= $diff_hours * 3600;
10548  $diff_minutes = floor($diff_seconds/60);
10549  $diff_seconds -= $diff_minutes * 60;
10550  $results["overview"][$this->lng->txt("tst_eval_total_finished_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds);
10551  $total_passed = 0;
10552  $total_passed_reached = 0;
10553  $total_passed_max = 0;
10554  $total_passed_time = 0;
10555  foreach ($foundParticipants as $userdata)
10556  {
10557  if ($userdata->getPassed())
10558  {
10559  $total_passed++;
10560  $total_passed_reached += $userdata->getReached();
10561  $total_passed_max += $userdata->getMaxpoints();
10562  $total_passed_time += $userdata->getTimeOfWork();
10563  }
10564  }
10565  $average_passed_reached = $total_passed ? $total_passed_reached / $total_passed : 0;
10566  $average_passed_max = $total_passed ? $total_passed_max / $total_passed : 0;
10567  $average_passed_time = $total_passed ? $total_passed_time / $total_passed : 0;
10568  $results["overview"][$this->lng->txt("tst_eval_total_passed")] = $total_passed;
10569  $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);
10570  $average_time = $average_passed_time;
10571  $diff_seconds = $average_time;
10572  $diff_hours = floor($diff_seconds/3600);
10573  $diff_seconds -= $diff_hours * 3600;
10574  $diff_minutes = floor($diff_seconds/60);
10575  $diff_seconds -= $diff_minutes * 60;
10576  $results["overview"][$this->lng->txt("tst_eval_total_passed_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds);
10577  }
10578 
10579  foreach ($data->getQuestionTitles() as $question_id => $question_title)
10580  {
10581  $answered = 0;
10582  $reached = 0;
10583  $max = 0;
10584  foreach ($foundParticipants as $userdata)
10585  {
10586  for ($i = 0; $i <= $userdata->getLastPass(); $i++)
10587  {
10588  if (is_object($userdata->getPass($i)))
10589  {
10590  $question =& $userdata->getPass($i)->getAnsweredQuestionByQuestionId($question_id);
10591  if (is_array($question))
10592  {
10593  $answered++;
10594  $reached += $question["reached"];
10595  $max += $question["points"];
10596  }
10597  }
10598  }
10599  }
10600  $percent = $max ? $reached/$max * 100.0 : 0;
10601  $counter++;
10602  $results["questions"][$question_id] = array(
10603  $question_title,
10604  sprintf("%.2f", $answered ? $reached / $answered : 0) . " " . strtolower($this->lng->txt("of")) . " " . sprintf("%.2f", $answered ? $max / $answered : 0),
10605  sprintf("%.2f", $percent) . "%",
10606  $answered,
10607  sprintf("%.2f", $answered ? $reached / $answered : 0),
10608  sprintf("%.2f", $answered ? $max / $answered : 0),
10609  $percent / 100.0
10610  );
10611  }
10612  return $results;
10613  }
10614 
10618  function getXMLZip()
10619  {
10620  include_once("./Modules/Test/classes/class.ilTestExport.php");
10621  $test_exp = new ilTestExport($this, "xml");
10622  return $test_exp->buildExportFile();
10623  }
10624 
10628  public function getMailNotification()
10629  {
10630  return $this->mailnotification;
10631  }
10632 
10638  public function setMailNotification($a_notification)
10639  {
10640  $this->mailnotification = $a_notification;
10641  }
10642 
10643  public function sendSimpleNotification($active_id)
10644  {
10645  include_once "./Modules/Test/classes/class.ilTestMailNotification.php";
10646 
10647  $mail = new ilTestMailNotification();
10648  $owner_id = $this->getOwner();
10649  $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id));
10650  $mail->sendSimpleNotification($owner_id, $this->getTitle(), $usr_data);
10651  }
10652 
10659  {
10660  include_once "./Modules/Test/classes/class.ilObjTestGUI.php";
10661  include_once "./Modules/Test/classes/tables/class.ilEvaluationAllTableGUI.php";
10662  $table_gui = new ilEvaluationAllTableGUI(new ilObjTestGUI(), 'outEvaluation', $this->getAnonymity());
10663  return $table_gui->getSelectedColumns();
10664  }
10665 
10666  public function sendAdvancedNotification($active_id)
10667  {
10668  include_once "./Modules/Test/classes/class.ilTestMailNotification.php";
10669 
10670  $mail = new ilTestMailNotification();
10671  $owner_id = $this->getOwner();
10672  $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id));
10673 
10674  include_once "./Modules/Test/classes/class.ilTestExport.php";
10675  $exportObj = new ilTestExport($this, "results");
10676  $file = $exportObj->exportToExcel($deliver = FALSE, 'active_id', $active_id, $passedonly = FALSE);
10677  include_once "./Services/Mail/classes/class.ilFileDataMail.php";
10678  $fd = new ilFileDataMail(ANONYMOUS_USER_ID);
10679  $fd->copyAttachmentFile($file, "result_" . $active_id . ".xls");
10680  $file_names[] = "result_" . $active_id . ".xls";
10681 
10682  $mail->sendAdvancedNotification($owner_id, $this->getTitle(), $usr_data, $file_names);
10683 
10684  if(count($file_names))
10685  {
10686  $fd->unlinkFiles($file_names);
10687  unset($fd);
10688  @unlink($file);
10689  }
10690  }
10691 
10692  function createRandomSolutions($number)
10693  {
10694  global $ilDB;
10695 
10696  // 1. get a user
10697  $query = "SELECT usr_id FROM usr_data";
10698  $result = $ilDB->query($query);
10699  while ($data = $ilDB->fetchAssoc($result))
10700  {
10701  $activequery = sprintf("SELECT user_fi FROM tst_active WHERE test_fi = %s AND user_fi = %s",
10702  $ilDB->quote($this->getTestId()),
10703  $ilDB->quote($data['usr_id'])
10704  );
10705  $activeresult = $ilDB->query($activequery);
10706  if ($activeresult->numRows() == 0)
10707  {
10708  $user_id = $data['usr_id'];
10709  if ($user_id != 13)
10710  {
10711  include_once "./Modules/Test/classes/class.ilTestSession.php";
10712  $testSession = FALSE;
10713  $testSession = new ilTestSession();
10714  $testSession->setRefId($this->getRefId());
10715  $testSession->setTestId($this->getTestId());
10716  $testSession->setUserId($user_id);
10717  $testSession->saveToDb();
10718  $passes = ($this->getNrOfTries()) ? $this->getNrOfTries() : 10;
10719  $nr_of_passes = rand(1, $passes);
10720  $active_id = $testSession->getActiveId();
10721  for ($pass = 0; $pass < $nr_of_passes; $pass++)
10722  {
10723  include_once "./Modules/Test/classes/class.ilTestSequence.php";
10724  $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
10725  if (!$testSequence->hasSequence())
10726  {
10727  $testSequence->createNewSequence($this->getQuestionCount(), $shuffle);
10728  $testSequence->saveToDb();
10729  }
10730  for ($seq = 1; $seq <= count($this->questions); $seq++)
10731  {
10732  $question_id = $testSequence->getQuestionForSequence($seq);
10733  $objQuestion = ilObjTest::_instanciateQuestion($question_id);
10734  $objQuestion->createRandomSolution($testSession->getActiveId(), $pass);
10735  }
10736  if ($pass < $nr_of_passes - 1)
10737  {
10738  $testSession->increasePass();
10739  $testSession->setLastSequence(0);
10740  $testSession->saveToDb();
10741  }
10742  else
10743  {
10744  $this->setActiveTestSubmitted($user_id);
10745  }
10746  }
10747  $number--;
10748  if ($number == 0) return;
10749  }
10750  }
10751  }
10752  }
10753 
10754  public function getResultsForActiveId($active_id)
10755  {
10756  global $ilDB;
10757 
10758  $query = "
10759  SELECT *
10760  FROM tst_result_cache
10761  WHERE active_fi = %s
10762  ";
10763 
10764  $result = $ilDB->queryF(
10765  $query, array('integer'), array($active_id)
10766  );
10767 
10768  if( !$result->numRows() )
10769  {
10770  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
10771 
10773 
10774  $query = "
10775  SELECT *
10776  FROM tst_result_cache
10777  WHERE active_fi = %s
10778  ";
10779 
10780  $result = $ilDB->queryF(
10781  $query, array('integer'), array($active_id)
10782  );
10783  }
10784 
10785  $row = $ilDB->fetchAssoc($result);
10786 
10787  return $row;
10788 
10789  }
10790 
10791  public function getMailNotificationType()
10792  {
10793  if ($this->mailnottype == 1)
10794  {
10795  return $this->mailnottype;
10796  }
10797  else
10798  {
10799  return 0;
10800  }
10801  }
10802 
10803  public function setMailNotificationType($a_type)
10804  {
10805  if ($a_type == 1)
10806  {
10807  $this->mailnottype = 1;
10808  }
10809  else
10810  {
10811  $this->mailnottype = 0;
10812  }
10813  }
10814 
10815  public function getExportSettings()
10816  {
10817  if ($this->exportsettings)
10818  {
10819  return $this->exportsettings;
10820  }
10821  else
10822  {
10823  return 0;
10824  }
10825  }
10826 
10827  public function setExportSettings($a_settings)
10828  {
10829  if ($a_settings)
10830  {
10831  $this->exportsettings = $a_settings;
10832  }
10833  else
10834  {
10835  $this->exportsettings = 0;
10836  }
10837  }
10838 
10840  {
10841  if (($this->exportsettings & 1) > 0)
10842  {
10843  return true;
10844  }
10845  else
10846  {
10847  return false;
10848  }
10849  }
10850 
10851  public function setExportSettingsSingleChoiceShort($a_settings)
10852  {
10853  if ($a_settings)
10854  {
10855  $this->exportsettings = $this->exportsettings | 1;
10856  }
10857  else
10858  {
10860  {
10861  $this->exportsettings = $this->exportsettings ^ 1;
10862  }
10863  }
10864  }
10865 
10866  public function getEnabledViewMode() {
10867  return $this->enabled_view_mode;
10868  }
10869 
10870  public function setEnabledViewMode($mode) {
10871  $this->enabled_view_mode = $mode;
10872  }
10873 
10875  $this->template_id = (int)$template_id;
10876  }
10877 
10878  function getTemplate() {
10879  return $this->template_id;
10880  }
10881 
10882  public function moveQuestionAfterOLD($previous_question_id, $new_question_id) {
10883  $new_array = array();
10884  $position = 1;
10885 
10886  $query = 'SELECT question_fi FROM tst_test_question WHERE test_fi = %s';
10887  $types = array('integer');
10888  $values = array($this->getTestId());
10889 
10890  $new_question_id += 1;
10891 
10892  global $ilDB;
10893  $inserted = false;
10894  $res = $ilDB->queryF($query, $types, $values);
10895  while($row = $ilDB->fetchAssoc($res)) {
10896 
10897  $qid = $row['question_fi'];
10898 
10899  if ($qid == $new_question_id) {
10900  continue;
10901  }
10902  else if ($qid == $previous_question_id) {
10903  $new_array[$position++] = $qid;
10904  $new_array[$position++] = $new_question_id;
10905  $inserted = true;
10906  }
10907  else {
10908  $new_array[$position++] = $qid;
10909  }
10910  }
10911 
10912  $update_query = 'UPDATE tst_test_question SET sequence = %s WHERE test_fi = %s AND question_fi = %s';
10913  $update_types = array('integer', 'integer', 'integer');
10914 
10915  foreach($new_array as $position => $qid) {
10916  $ilDB->manipulateF(
10917  $update_query,
10918  $update_types,
10919  $vals = array(
10920  $position,
10921  $this->getTestId(),
10922  $qid
10923  )
10924  );
10925  }
10926  }
10927 
10928  public function setScoringFeedbackOptionsByArray($options) {
10929  if (is_array($options))
10930  {
10931  $this->setGenericAnswerFeedback( in_array('instant_feedback_generic', $options) ? 1 : 0);
10932  $this->setSpecificAnswerFeedback( in_array('instant_feedback_specific', $options) ? 1 : 0);
10933  $this->setAnswerFeedbackPoints( in_array('instant_feedback_points', $options) ? 1 : 0);
10934  $this->setInstantFeedbackSolution( in_array('instant_feedback_solution', $options) ? 1 : 0);
10935  } else {
10936  $this->setGenericAnswerFeedback(0);
10937  $this->setSpecificAnswerFeedback(0);
10938  $this->setAnswerFeedbackPoints(0);
10939  $this->setInstantFeedbackSolution(0);
10940  }
10941  }
10942 
10943  public function setResultsPresentationOptionsByArray($options) {
10944  $setter = array(
10945  'pass_details' => 'setShowPassDetails',
10946  'solution_details' => 'setShowSolutionDetails',
10947  'solution_printview' => 'setShowSolutionPrintview',
10948  'solution_feedback' => 'setShowSolutionFeedback',
10949  'solution_answers_only' => 'setShowSolutionAnswersOnly',
10950  'solution_signature' => 'setShowSolutionSignature',
10951  'solution_suggested' => 'setShowSolutionSuggested',
10952  );
10953  foreach($setter as $key => $setter) {
10954  if (in_array($key, $options)) {
10955  $this->$setter(1);
10956  }
10957  else {
10958  $this->$setter(0);
10959  }
10960  }
10961  }
10962 
10963  public function getPoolUsage() {
10964  return (boolean) $this->poolUsage;
10965  }
10966 
10967  public function setPoolUsage($usage) {
10968  $this->poolUsage = (boolean)$usage;
10969  }
10970 
10971  public function setQuestionOrderAndObligations($orders, $obligations)
10972  {
10973  global $ilDB;
10974 
10975  asort($orders);
10976 
10977  $i = 0;
10978 
10979  foreach($orders as $id => $position)
10980  {
10981  $i++;
10982 
10983  $obligatory = (
10984  isset($obligations[$id]) && $obligations[$id] ? 1 : 0
10985  );
10986 
10987  $query = "
10988  UPDATE tst_test_question
10989  SET sequence = %s,
10990  obligatory = %s
10991  WHERE question_fi = %s
10992  ";
10993 
10994  $ilDB->manipulateF(
10995  $query, array('integer', 'integer', 'integer'), array($i, $obligatory, $id)
10996  );
10997  }
10998 
10999  $this->loadQuestions();
11000  }
11001 
11002  public function moveQuestionAfter($question_to_move, $question_before) {
11003  global $ilDB;
11004  //var_dump(func_get_args());
11005  if ($question_before) {
11006  $query = 'SELECT sequence, test_fi FROM tst_test_question WHERE question_fi = %s';
11007  $types = array('integer');
11008  $values = array($question_before);
11009  $rset = $ilDB->queryF($query, $types, $values);
11010  }
11011 
11012  if (!$question_before || ($rset && !($row = $ilDB->fetchAssoc($rset)))) {
11013  $row = array(
11014  'sequence' => 0,
11015  'test_fi' => $this->getTestId(),
11016  );
11017  }
11018 
11019  $update = 'UPDATE tst_test_question SET sequence = sequence + 1 WHERE sequence > %s AND test_fi = %s';
11020  $types = array('integer', 'integer');
11021  $values = array($row['sequence'], $row['test_fi']);
11022  $ilDB->manipulateF($update, $types, $values);
11023 
11024  $update = 'UPDATE tst_test_question SET sequence = %s WHERE question_fi = %s';
11025  $types = array('integer', 'integer');
11026  $values = array($row['sequence'] + 1, $question_to_move);
11027  $ilDB->manipulateF($update, $types, $values);
11028 
11029  }
11030 
11032  global $ilDB;
11034  $query = 'SELECT count(question_id) cnt FROM qpl_questions'.
11035  ' INNER JOIN object_data on obj_fi = obj_id'.
11036  ' WHERE type <> '.$ilDB->quote('qpl', 'text').
11037  ' AND '.$ilDB->in('question_id', array_keys($questions), false, 'integer');
11038 
11039  $rset = $ilDB->query($query);
11040 
11041  if ($row = $ilDB->fetchAssoc($rset)) {
11042  return $row['cnt'] > 0;
11043  }
11044  return false;
11045  }
11046 
11053  public static function _lookupFinishedUserTests($a_user_id)
11054  {
11055  global $ilDB;
11056 
11057  $result = $ilDB->queryF("SELECT test_fi,MAX(pass) AS pass FROM tst_active".
11058  " JOIN tst_pass_result ON (tst_pass_result.active_fi = tst_active.active_id)".
11059  " WHERE user_fi=%s".
11060  " GROUP BY test_fi",
11061  array('integer', 'integer'),
11062  array($a_user_id, 1)
11063  );
11064  $all = array();
11065  while($row = $ilDB->fetchAssoc($result))
11066  {
11067  $obj_id = self::_getObjectIDFromTestID($row["test_fi"]);
11068  $all[$obj_id] = (bool)$row["pass"];
11069  }
11070  return $all;
11071  }
11072  public function getQuestions()
11073  {
11074  return $this->questions;
11075  }
11076 
11077  public function isOnline()
11078  {
11079  return $this->online;
11080  }
11081 
11082  public function setOnline($a_online = true)
11083  {
11084  $this->online = (bool)$a_online;
11085  }
11086 
11087  public function setPrintBestSolutionWithResult($status)
11088  {
11089  $this->print_best_solution_with_result = (bool) $status;
11090  }
11091 
11093  {
11095  }
11096 
11103  {
11105  }
11106 
11113  {
11114  $this->offeringQuestionHintsEnabled = (bool)$offeringQuestionHintsEnabled;
11115  }
11116 
11117  function setActivationVisibility($a_value)
11118  {
11119  $this->activation_visibility = (bool) $a_value;
11120  }
11121 
11123  {
11125  }
11126 
11128  {
11129  return (bool)$this->activation_limited;
11130  }
11131 
11132  function setActivationLimited($a_value)
11133  {
11134  $this->activation_limited = (bool)$a_value;
11135  }
11136 
11137  /* GET/SET for highscore feature */
11138 
11144  public function setHighscoreEnabled($a_enabled)
11145  {
11146  $this->_highscore_enabled = (bool)$a_enabled;
11147  }
11148 
11154  public function getHighscoreEnabled()
11155  {
11156  return (bool) $this->_highscore_enabled;
11157  }
11158 
11166  public function setHighscoreAnon($a_anon)
11167  {
11168  $this->_highscore_anon = (bool)$a_anon;
11169  }
11170 
11180  public function getHighscoreAnon()
11181  {
11182  return (bool) $this->_highscore_anon;
11183  }
11184 
11193  public function isHighscoreAnon()
11194  {
11195  if ($this->getAnonymity() == 1)
11196  {
11197  return true;
11198  }
11199  else
11200  {
11201  return (bool)$this->getHighscoreAnon();
11202  }
11203  }
11204 
11210  public function setHighscoreAchievedTS($a_achieved_ts)
11211  {
11212  $this->_highscore_achieved_ts = (bool)$a_achieved_ts;
11213  }
11214 
11220  public function getHighscoreAchievedTS()
11221  {
11222  return (bool) $this->_highscore_achieved_ts;
11223  }
11224 
11230  public function setHighscoreScore($a_score)
11231  {
11232  $this->_highscore_score = (bool)$a_score;
11233  }
11234 
11240  public function getHighscoreScore()
11241  {
11242  return (bool) $this->_highscore_score;
11243  }
11244 
11250  public function setHighscorePercentage($a_percentage)
11251  {
11252  $this->_highscore_percentage = (bool)$a_percentage;
11253  }
11254 
11260  public function getHighscorePercentage()
11261  {
11262  return (bool) $this->_highscore_percentage;
11263  }
11264 
11270  public function setHighscoreHints($a_hints)
11271  {
11272  $this->_highscore_hints = (bool)$a_hints;
11273  }
11274 
11280  public function getHighscoreHints()
11281  {
11282  return (bool) $this->_highscore_hints;
11283  }
11284 
11290  public function setHighscoreWTime($a_wtime)
11291  {
11292  $this->_highscore_wtime = (bool)$a_wtime;
11293  }
11294 
11300  public function getHighscoreWTime()
11301  {
11302  return (bool) $this->_highscore_wtime;
11303  }
11304 
11310  public function setHighscoreOwnTable($a_own_table)
11311  {
11312  $this->_highscore_own_table = (bool)$a_own_table;
11313  }
11314 
11320  public function getHighscoreOwnTable()
11321  {
11322  return (bool) $this->_highscore_own_table;
11323  }
11324 
11330  public function setHighscoreTopTable($a_top_table)
11331  {
11332  $this->_highscore_top_table = (bool)$a_top_table;
11333  }
11334 
11340  public function getHighscoreTopTable()
11341  {
11342  return (bool) $this->_highscore_top_table;
11343  }
11344 
11351  public function setHighscoreTopNum($a_top_num)
11352  {
11353  $this->_highscore_top_num = (int)$a_top_num;
11354  }
11355 
11364  public function getHighscoreTopNum($a_retval = 10)
11365  {
11366  $retval = $a_retval;
11367  if ( (int) $this->_highscore_top_num != 0)
11368  {
11369  $retval = $this->_highscore_top_num;
11370  }
11371 
11372  return $retval;
11373  }
11374  /* End GET/SET for highscore feature*/
11375 
11376  public function setSpecificAnswerFeedback($specific_answer_feedback)
11377  {
11378  switch ($specific_answer_feedback)
11379  {
11380  case 1:
11381  $this->specific_answer_feedback = 1;
11382  break;
11383  default:
11384  $this->specific_answer_feedback = 0;
11385  break;
11386  }
11387  }
11388 
11389  public function getSpecificAnswerFeedback()
11390  {
11391  switch ($this->specific_answer_feedback)
11392  {
11393  case 1:
11394  return 1;
11395  default:
11396  return 0;
11397  }
11398  }
11399 
11406  {
11407  $this->obligationsEnabled = (bool)$obligationsEnabled;
11408  }
11409 
11415  public function areObligationsEnabled()
11416  {
11417  return (bool)$this->obligationsEnabled;
11418  }
11419 
11426  public static function isQuestionObligationPossible($questionId)
11427  {
11428  require_once('Modules/TestQuestionPool/classes/class.assQuestion.php');
11429 
11430  $classConcreteQuestion = assQuestion::_getQuestionType($questionId);
11431 
11432  assQuestion::_includeClass($classConcreteQuestion, 0);
11433 
11434  // static binder is not at work yet (in PHP < 5.3)
11435  //$obligationPossible = $classConcreteQuestion::isObligationPossible();
11436  $obligationPossible = call_user_func(array($classConcreteQuestion, 'isObligationPossible'), $questionId);
11437 
11438  return $obligationPossible;
11439  }
11440 
11447  public static function isQuestionObligatory($question_id)
11448  {
11449  global $ilDB;
11450 
11451  $rset = $ilDB->queryF('SELECT obligatory FROM tst_test_question WHERE question_fi = %s', array('integer'), array($question_id));
11452 
11453  if( $row = $ilDB->fetchAssoc($rset) )
11454  {
11455  return (bool) $row['obligatory'];
11456  }
11457 
11458  return false;
11459  }
11460 
11473  public static function allObligationsAnswered($test_id, $active_id, $pass)
11474  {
11475  global $ilDB;
11476 
11477  $rset = $ilDB->queryF(
11478  'SELECT obligations_answered FROM tst_pass_result WHERE active_fi = %s AND pass = %s',
11479  array('integer', 'integer'),
11480  array($active_id, $pass)
11481  );
11482 
11483  if( $row = $ilDB->fetchAssoc($rset) )
11484  {
11485  return (bool)$row['obligations_answered'];
11486  }
11487 
11488  return !self::hasObligations($test_id);
11489  }
11490 
11499  public static function hasObligations($test_id)
11500  {
11501  global $ilDB;
11502 
11503  $rset = $ilDB->queryF(
11504  'SELECT count(*) cnt FROM tst_test_question WHERE test_fi = %s AND obligatory = 1',
11505  array('integer'), array($test_id)
11506  );
11507 
11508  $row = $ilDB->fetchAssoc($rset);
11509 
11510  return (bool)$row['cnt'] > 0;
11511  }
11512 
11513  public function setAutosave($autosave)
11514  {
11515  $this->autosave = $autosave;
11516  }
11517 
11518  public function getAutosave()
11519  {
11520  return $this->autosave;
11521  }
11522 
11524  {
11525  $this->autosave_ival = $autosave_ival;
11526  }
11527 
11528  public function getAutosaveIval()
11529  {
11530  return $this->autosave_ival;
11531  }
11532 
11539  {
11540  return (
11541  $this->isRandomTest() && count($this->getRandomQuestionpools()) > 0
11542  );
11543  }
11544 
11551  {
11552  return (
11553  !$this->isRandomTest() && count($this->questions) > 0
11554  );
11555  }
11556 
11562  public function isPassDeletionAllowed()
11563  {
11565  }
11566 
11573  {
11574  $this->passDeletionAllowed = (bool)$passDeletionAllowed;
11575  }
11576 
11578  {
11579  $this->activation_starting_time = $starting_time;
11580  }
11581 
11583  {
11584  $this->activation_ending_time = $ending_time;
11585  }
11586 
11588  {
11589  return (strlen($this->activation_starting_time)) ? $this->activation_starting_time : NULL;
11590  }
11591 
11593  {
11594  return (strlen($this->activation_ending_time)) ? $this->activation_ending_time : NULL;
11595  }
11596 }