ILIAS  eassessment Revision 61809
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilObjTest.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
5 include_once "./classes/class.ilObject.php";
6 include_once "./Modules/Test/classes/inc.AssessmentConstants.php";
7 
17 class ilObjTest extends ilObject
18 {
26  protected $_kiosk;
27 
33  var $test_id;
34 
41 
42 
49  var $author;
50 
56  var $metadata;
57 
64 
72 
79 
88 
99 
108 
115 
125 
132 
140 
148 
158 
165 
172 
179 
186 
193 
200 
206  var $ects_fx;
207 
214 
223 
230 
238 
245 
252 
260 
267 
274 
281 
288 
295 
302 
309 
316 
324 
331 
338 
345 
352 
359 
366 
373 
374  /* Change Sn */
381 
388 
394  private $_exam_pdf;
395  /* Change Sn End */
396 
402  private $_showinfo;
403 
409  private $_forcejs;
410 
416  private $_customStyle;
417 
418  protected $mailnotification;
419  protected $mailnottype;
421  protected $exportsettings;
422  protected $show_serial;
423 
428  private $import_dir;
429 
436  function ilObjTest($a_id = 0,$a_call_by_reference = true)
437  {
438  global $ilUser;
439  $this->type = "tst";
440  include_once "./Modules/Test/classes/class.assMarkSchema.php";
441  $this->mark_schema = new ASS_MarkSchema();
442  $this->test_id = -1;
443  $this->author = $ilUser->fullname;
444  $this->introduction = "";
445  $this->questions = array();
446  $this->sequence_settings = TEST_FIXED_SEQUENCE;
447  $this->score_reporting = REPORT_AFTER_TEST;
448  $this->instant_verification = 0;
449  $this->answer_feedback_points = 0;
450  $this->reporting_date = "";
451  $this->nr_of_tries = 0;
452  $this->_kiosk = 0;
453  $this->use_previous_answers = 1;
454  $this->title_output = 0;
455  $this->starting_time = "";
456  $this->ending_time = "";
457  $this->processing_time = "00:00:00";
458  $this->enable_processing_time = "0";
459  $this->reset_processing_time = 0;
460  $this->ects_output = 0;
461  $this->ects_fx = NULL;
462  $this->random_test = 0;
463  $this->shuffle_questions = TRUE;
464  $this->mailnottype = 0;
465  $this->exportsettings = 0;
466  $this->show_summary = 8;
467  $this->random_question_count = "";
468  $this->count_system = COUNT_PARTIAL_SOLUTIONS;
469  $this->mc_scoring = SCORE_ZERO_POINTS_WHEN_UNANSWERED;
470  $this->score_cutting = SCORE_CUT_QUESTION;
471  $this->pass_scoring = SCORE_LAST_PASS;
472  $this->answer_feedback = 0;
473  $this->password = "";
474  $this->certificate_visibility = 0;
475  $this->allowedUsers = "";
476  $this->_showfinalstatement = FALSE;
477  $this->_finalstatement = "";
478  $this->_redirect_after_exam = "";
479  $this->_redirect_only_kiosk_mode = TRUE;
480  $this->_exam_pdf = TRUE;
481  $this->_showinfo = TRUE;
482  $this->_forcejs = FALSE;
483  $this->_customStyle = "";
484  $this->allowedUsersTimeGap = "";
485  $this->anonymity = 0;
486  $this->show_cancel = 1;
487  $this->autosave = 0;
488  $this->show_marker = 0;
489  $this->show_serial = false;
490  $this->fixed_participants = 0;
491  $this->setShowPassDetails(TRUE);
492  $this->setShowSolutionDetails(TRUE);
493  $this->setShowSolutionAnswersOnly(FALSE);
494  $this->setShowSolutionSignature(FALSE);
495  $this->testSession = FALSE;
496  $this->testSequence = FALSE;
497  $this->mailnotification = 0;
498  global $lng;
499  $lng->loadLanguageModule("assessment");
500  $this->mark_schema->createSimpleSchema($lng->txt("failed_short"), $lng->txt("failed_official"), 0, 0, $lng->txt("passed_short"), $lng->txt("passed_official"), 50, 1);
501  $this->ects_grades = array(
502  "A" => 90,
503  "B" => 65,
504  "C" => 35,
505  "D" => 10,
506  "E" => 0
507  );
508  $this->ilObject($a_id, $a_call_by_reference);
509  }
510 
514  function create($a_upload = false)
515  {
516  parent::create();
517 
518  // meta data will be created by
519  // import parser
520  if (!$a_upload)
521  {
522  $this->createMetaData();
523  }
524  }
525 
532  function update()
533  {
534  if (!parent::update())
535  {
536  return false;
537  }
538 
539  // put here object specific stuff
540 
541  return true;
542  }
543 
550  function createReference() {
551  $result = parent::createReference();
552  $this->saveToDb();
553  return $result;
554  }
555 
561  function read($a_force_db = false)
562  {
563  parent::read($a_force_db);
564  $this->loadFromDb();
565  }
566 
567 
574  function delete()
575  {
576  // always call parent delete function first!!
577  if (!parent::delete())
578  {
579  return false;
580  }
581 
582  // delet meta data
583  $this->deleteMetaData();
584 
585  //put here your module specific stuff
586  $this->deleteTest();
587 
588  return true;
589  }
590 
596  function deleteTest()
597  {
598  global $ilDB;
599 
600  // first of all remove all test editings, because the delete statements used for this
601  // contain a subquery for active ids, that are deleted in the next steps
602  $this->removeAllTestEditings();
603 
604  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s",
605  array('integer'),
606  array($this->getTestId())
607  );
608  $active_array = array();
609  while ($row = $ilDB->fetchAssoc($result))
610  {
611  array_push($active_array, $row["active_id"]);
612  }
613 
614  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
615  array('integer'),
616  array($this->getTestId())
617  );
618 
619  if (count($active_array))
620  {
621  foreach ($active_array as $active_id)
622  {
623  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_times WHERE active_fi = %s",
624  array('integer'),
625  array($active_id)
626  );
627 
628  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
629  array('integer'),
630  array($active_id)
631  );
632  }
633  }
634 
635  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_mark WHERE test_fi = %s",
636  array('integer'),
637  array($this->getTestId())
638  );
639 
640  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s",
641  array('integer'),
642  array($this->getTestId())
643  );
644  while ($row = $ilDB->fetchAssoc($result))
645  {
646  $this->removeQuestion($row["question_fi"]);
647  }
648 
649  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_tests WHERE test_id = %s",
650  array('integer'),
651  array($this->getTestId())
652  );
653 
654  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
655  array('integer'),
656  array($this->getTestId())
657  );
658 
659  // this delete is allready done by call to removeAllTestEditings some lines above
660  /*$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)",
661  array('integer'),
662  array($this->getTestId())
663  );*/
664 
665  // moved to top of this method because this method performs delete statements,
666  // that use a subquery for active ids, that are allready deleted some lines above
667  //$this->removeAllTestEditings();
668 
669  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
670  array('integer'),
671  array($this->getTestId())
672  );
673 
674  if ($this->isRandomTest())
675  {
677  }
678 
679 
680  // delete export files
681  include_once "./Services/Utilities/classes/class.ilUtil.php";
682  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
683  $directory = $tst_data_dir."/tst_".$this->getId();
684  if (is_dir($directory))
685  {
686  include_once "./Services/Utilities/classes/class.ilUtil.php";
687  ilUtil::delDir($directory);
688  }
689  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
690  $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId());
691  // remaining usages are not in text anymore -> delete them
692  // and media objects (note: delete method of ilObjMediaObject
693  // checks whether object is used in another context; if yes,
694  // the object is not deleted!)
695  foreach($mobs as $mob)
696  {
697  ilObjMediaObject::_removeUsage($mob, "tst:html", $this->getId());
698  if (ilObjMediaObject::_exists($mob))
699  {
700  $mob_obj =& new ilObjMediaObject($mob);
701  $mob_obj->delete();
702  }
703  }
704  }
705 
715  function initDefaultRoles()
716  {
717  global $rbacadmin;
718  return array();
719  }
720 
734  function notify($a_event,$a_ref_id,$a_parent_non_rbac_id,$a_node_id,$a_params = 0)
735  {
736  global $tree;
737 
738  switch ($a_event)
739  {
740  case "link":
741 
742  //var_dump("<pre>",$a_params,"</pre>");
743  //echo "Module name ".$this->getRefId()." triggered by link event. Objects linked into target object ref_id: ".$a_ref_id;
744  //exit;
745  break;
746 
747  case "cut":
748 
749  //echo "Module name ".$this->getRefId()." triggered by cut event. Objects are removed from target object ref_id: ".$a_ref_id;
750  //exit;
751  break;
752 
753  case "copy":
754 
755  //var_dump("<pre>",$a_params,"</pre>");
756  //echo "Module name ".$this->getRefId()." triggered by copy event. Objects are copied into target object ref_id: ".$a_ref_id;
757  //exit;
758  break;
759 
760  case "paste":
761 
762  //echo "Module name ".$this->getRefId()." triggered by paste (cut) event. Objects are pasted into target object ref_id: ".$a_ref_id;
763  //exit;
764  break;
765 
766  case "new":
767 
768  //echo "Module name ".$this->getRefId()." triggered by paste (new) event. Objects are applied to target object ref_id: ".$a_ref_id;
769  //exit;
770  break;
771  }
772 
773  // At the beginning of the recursive process it avoids second call of the notify function with the same parameter
774  if ($a_node_id==$_GET["ref_id"])
775  {
776  $parent_obj =& $this->ilias->obj_factory->getInstanceByRefId($a_node_id);
777  $parent_type = $parent_obj->getType();
778  if ($parent_type == $this->getType())
779  {
780  $a_node_id = (int) $tree->getParentId($a_node_id);
781  }
782  }
783 
784  parent::notify($a_event,$a_ref_id,$a_parent_non_rbac_id,$a_node_id,$a_params);
785  }
786 
793  {
794  include_once "./Services/Utilities/classes/class.ilUtil.php";
795  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
796  ilUtil::makeDir($tst_data_dir);
797  if (!is_writable($tst_data_dir))
798  {
799  $this->ilias->raiseError("Test Data Directory (".$tst_data_dir
800  .") not writeable.",$this->ilias->error_obj->MESSAGE);
801  }
802 
803  // create learning module directory (data_dir/lm_data/lm_<id>)
804  $tst_dir = $tst_data_dir."/tst_".$this->getId();
805  ilUtil::makeDir($tst_dir);
806  if (!@is_dir($tst_dir))
807  {
808  $this->ilias->raiseError("Creation of Test Directory failed.",$this->ilias->error_obj->MESSAGE);
809  }
810  // create Export subdirectory (data_dir/lm_data/lm_<id>/Export)
811  $export_dir = $tst_dir."/export";
812  ilUtil::makeDir($export_dir);
813  if (!@is_dir($export_dir))
814  {
815  $this->ilias->raiseError("Creation of Export Directory failed.",$this->ilias->error_obj->MESSAGE);
816  }
817  }
818 
825  {
826  include_once "./Services/Utilities/classes/class.ilUtil.php";
827  $export_dir = ilUtil::getDataDir()."/tst_data"."/tst_".$this->getId()."/export";
828  return $export_dir;
829  }
830 
838  {
839  // quit if import dir not available
840  if (!@is_dir($dir) or
841  !is_writeable($dir))
842  {
843  return array();
844  }
845 
846  // open directory
847  $dir = dir($dir);
848 
849  // initialize array
850  $file = array();
851 
852  // get files and save the in the array
853  while ($entry = $dir->read())
854  {
855  if ($entry != "." and
856  $entry != ".." and
857  //substr($entry, -4) == ".zip" and
858  ereg("^[0-9]{10}_{2}[0-9]+_{2}(test(__results)?(__PDF-Export)?__)*[0-9]+\.[a-z]{1,4}\$", $entry))
859  {
860  $file[] = $entry;
861  }
862  }
863 
864  // close import directory
865  $dir->close();
866 
867  // sort files
868  sort ($file);
869  reset ($file);
870 
871  return $file;
872  }
873 
874 
881  {
882  global $ilias;
883 
884  include_once "./Services/Utilities/classes/class.ilUtil.php";
885  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
886  ilUtil::makeDir($tst_data_dir);
887 
888  if (!is_writable($tst_data_dir))
889  {
890  $ilias->raiseError("Test data directory (".$tst_data_dir
891  .") not writeable.",$ilias->error_obj->FATAL);
892  }
893 
894  // create test directory (data_dir/tst_data/tst_import)
895  $tst_dir = $tst_data_dir."/tst_import";
896  ilUtil::makeDir($tst_dir);
897  if (!@is_dir($tst_dir))
898  {
899  $ilias->raiseError("Creation of test import directory failed.",$ilias->error_obj->FATAL);
900  }
901  return $tst_dir;
902  }
903 
907  function _setImportDirectory($a_import_dir = null)
908  {
909  if (strlen($a_import_dir))
910  {
911  $_SESSION["tst_import_dir"] = $a_import_dir;
912  }
913  else
914  {
915  unset($_SESSION["tst_import_dir"]);
916  }
917  }
918 
926  {
927  if (strlen($_SESSION["tst_import_dir"]))
928  {
929  return $_SESSION["tst_import_dir"];
930  }
931  return null;
932  }
933 
940  {
941  include_once "./Services/Utilities/classes/class.ilUtil.php";
942  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
943  ilUtil::makeDir($tst_data_dir);
944 
945  if (!is_writable($tst_data_dir))
946  {
947  $this->ilias->raiseError("Test Data Directory (".$tst_data_dir
948  .") not writeable.",$this->ilias->error_obj->FATAL);
949  }
950 
951  // create test directory (data_dir/tst_data/tst_import)
952  $tst_dir = $tst_data_dir."/tst_import";
953  ilUtil::makeDir($tst_dir);
954  if (!@is_dir($tst_dir))
955  {
956  $ilias->raiseError("Creation of test import directory failed.",$ilias->error_obj->FATAL);
957  }
958  }
959 
967  {
968  if(strlen($this->import_dir))
969  {
970  return $this->import_dir;
971  }
972 
973  include_once "./Services/Utilities/classes/class.ilUtil.php";
974  $import_dir = ilUtil::getDataDir()."/tst_data/tst_import";
975  if (@is_dir($import_dir))
976  {
977  return $import_dir;
978  }
979  else
980  {
981  return false;
982  }
983  }
984 
990  public function setImportDirectory($a_import_dir)
991  {
992  $this->import_dir = $a_import_dir;
993  }
994 
995 
1003  {
1004  global $ilDB;
1005 
1006  $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",
1007  array('integer'),
1008  array($this->getTestId())
1009  );
1010  $hasSC = false;
1011  while ($row = $ilDB->fetchAssoc($result))
1012  {
1013  if (strcmp($row['foundtypes'], 'assSingleChoice') == 0)
1014  {
1015  $hasSC = true;
1016  }
1017  }
1018  return $hasSC;
1019  }
1020 
1028  {
1029  global $ilDB;
1030 
1031  $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",
1032  array('integer'),
1033  array($this->getTestId())
1034  );
1035  if ($result->numRows() == 1)
1036  {
1037  $row = $ilDB->fetchAssoc($result);
1038  if (strcmp($row['foundtypes'], 'assSingleChoice') == 0)
1039  {
1040  return TRUE;
1041  }
1042  else
1043  {
1044  return false;
1045  }
1046  }
1047  return FALSE;
1048  }
1049 
1057  {
1058  global $ilDB;
1059 
1060  if (!$this->hasSingleChoiceQuestions()) return false;
1061 
1062  $result = $ilDB->queryF("SELECT DISTINCT(qpl_qst_sc.shuffle) foundshuffles FROM qpl_questions, qpl_qst_sc, tst_test_result, qpl_qst_type, tst_active WHERE tst_test_result.question_fi = qpl_questions.question_id AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id AND tst_test_result.active_fi = tst_active.active_id AND qpl_questions.question_id = qpl_qst_sc.question_fi AND tst_active.test_fi = %s AND qpl_qst_type.type_tag = %s",
1063  array('integer', 'text'),
1064  array($this->getTestId(), 'assSingleChoice')
1065  );
1066  if ($result->numRows() == 1)
1067  {
1068  $row = $ilDB->fetchAssoc($result);
1069  return ($row['foundshuffles'] == 0);
1070  }
1071  return FALSE;
1072  }
1073 
1080  function isComplete()
1081  {
1082  if ((count($this->mark_schema->mark_steps)) and (count($this->questions)))
1083  {
1084  return 1;
1085  }
1086  else
1087  {
1088  if ($this->isRandomTest())
1089  {
1090  $arr = $this->getRandomQuestionpools();
1091  if (count($arr) && ($this->getRandomQuestionCount() > 0))
1092  {
1093  return 1;
1094  }
1095  $count = 0;
1096  foreach ($arr as $array)
1097  {
1098  $count += $array["count"];
1099  }
1100  if ($count)
1101  {
1102  return 1;
1103  }
1104  }
1105  return 0;
1106  }
1107  return 0;
1108  }
1109 
1116  function _isComplete($obj_id)
1117  {
1118  $test = new ilObjTest($obj_id, false);
1119  $test->loadFromDb();
1120  return $test->isComplete();
1121  }
1122 
1128  function saveECTSStatus($ects_output = 0, $fx_support = "", $ects_a = 90, $ects_b = 65, $ects_c = 35, $ects_d = 10, $ects_e = 0)
1129  {
1130  global $ilDB;
1131  if ($this->test_id > 0)
1132  {
1133  $fx_support = preg_replace("/,/", ".", $fx_support);
1134  if (preg_match("/\d+/", $fx_support))
1135  {
1136  $fx_support = $fx_support;
1137  }
1138  else
1139  {
1140  $fx_support = NULL;
1141  }
1142  $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",
1143  array('text','float','float','float','float','float','float','integer'),
1144  array($ects_output, $ects_a, $ects_b, $ects_c, $ects_d, $ects_e, $fx_support, $this->getTestId())
1145  );
1146  $this->ects_output = $ects_output;
1147  $this->ects_fx = $fx_support;
1148  }
1149  }
1150 
1157  {
1158  global $ilDB;
1159 
1160  $complete = 0;
1161  if ($this->isComplete())
1162  {
1163  $complete = 1;
1164  }
1165  if ($this->test_id > 0)
1166  {
1167  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET complete = %s WHERE test_id = %s",
1168  array('text','integer'),
1169  array($complete, $this->test_id)
1170  );
1171  }
1172  }
1173 
1179  function getAllRTEContent()
1180  {
1181  $result = array();
1182  array_push($result, $this->getIntroduction());
1183  array_push($result, $this->getFinalStatement());
1184  return $result;
1185  }
1186 
1193  {
1194  include_once("./Services/RTE/classes/class.ilRTE.php");
1195  $completecontent = "";
1196  foreach ($this->getAllRTEContent() as $content)
1197  {
1198  $completecontent .= $content;
1199  }
1200  ilRTE::_cleanupMediaObjectUsage($completecontent, $this->getType() . ":html",
1201  $this->getId());
1202  }
1203 
1210  function saveToDb($properties_only = FALSE)
1211  {
1212  global $ilDB, $ilLog;
1213 
1214  // cleanup RTE images
1215  $this->cleanupMediaobjectUsage();
1216  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1217  if ($this->test_id == -1)
1218  {
1219  // Create new dataset
1220  $next_id = $ilDB->nextId('tst_tests');
1221  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_tests (test_id, obj_fi, author, introduction, " .
1222  "finalstatement, redirect_after_exam, redirect_only_kiosk_mode, exam_pdf, showinfo, forcejs, customstyle, showfinalstatement, sequence_settings, " .
1223  "score_reporting, instant_verification, answer_feedback_points, answer_feedback, anonymity, show_cancel, autosave, show_marker, show_serial, " .
1224  "fixed_participants, nr_of_tries, kiosk, use_previous_answers, title_output, processing_time, enable_processing_time, " .
1225  "reset_processing_time, reporting_date, starting_time, ending_time, complete, ects_output, ects_a, ects_b, ects_c, ects_d, " .
1226  "ects_e, ects_fx, random_test, random_question_count, count_system, mc_scoring, score_cutting, pass_scoring, " .
1227  "shuffle_questions, results_presentation, show_summary, password, allowedusers, mailnottype, exportsettings, " .
1228  "alloweduserstimegap, certificate_visibility, mailnotification, created, tstamp) " .
1229  "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, " .
1230  "%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
1231  array(
1232  'integer', 'integer', 'text', 'text',
1233  'text', 'text', 'integer', 'integer', 'integer', 'integer', 'text', 'integer', 'integer',
1234  'integer', 'text', 'text', 'text', 'text', 'text', 'text','integer','text',
1235  'text', 'integer', 'integer', 'text', 'text', 'text', 'text',
1236  'integer', 'text', 'text', 'text', 'text', 'text', 'float', 'float', 'float', 'float',
1237  'float', 'float', 'text', 'integer', 'text', 'text', 'text', 'text',
1238  'text', 'integer', 'integer', 'text', 'integer', 'integer', 'integer',
1239  'integer', 'text', 'integer', 'integer', 'integer'
1240  ),
1241  array(
1242  $next_id,
1243  $this->getId(),
1244  $this->getAuthor(),
1247  $this->getRedirectAfterExam(),
1248  $this->getRedirectOnlyKioskMode(),
1249  $this->getExamPdf(),
1250  $this->getShowInfo(),
1251  $this->getForceJS(),
1252  $this->getCustomStyle(),
1253  $this->getShowFinalStatement(),
1254  $this->getSequenceSettings(),
1255  $this->getScoreReporting(),
1256  $this->getInstantFeedbackSolution(),
1257  $this->getAnswerFeedbackPoints(),
1258  $this->getAnswerFeedback(),
1259  $this->getAnonymity(),
1260  $this->getShowCancel(),
1261  $this->getAutosave(),
1262  $this->getShowMarker(),
1263  $this->getShowSerial(),
1264  $this->getFixedParticipants(),
1265  $this->getNrOfTries(),
1266  $this->getKiosk(),
1267  $this->getUsePreviousAnswers(),
1268  $this->getTitleOutput(),
1269  $this->getProcessingTime(),
1270  $this->getEnableProcessingTime(),
1271  $this->getResetProcessingTime(),
1272  $this->getReportingDate(),
1273  $this->getStartingTime(),
1274  $this->getEndingTime(),
1275  $this->isComplete(),
1276  $this->getECTSOutput(),
1277  strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : NULL,
1278  strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : NULL,
1279  strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : NULL,
1280  strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : NULL,
1281  strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : NULL,
1282  $this->getECTSFX(),
1283  $this->isRandomTest(),
1284  $this->getRandomQuestionCount(),
1285  $this->getCountSystem(),
1286  $this->getMCScoring(),
1287  $this->getScoreCutting(),
1288  $this->getPassScoring(),
1289  $this->getShuffleQuestions(),
1290  $this->getResultsPresentation(),
1291  $this->getListOfQuestionsSettings(),
1292  $this->getPassword(),
1293  $this->getAllowedUsers(),
1294  $this->getMailNotificationType(),
1295  $this->getExportSettings(),
1296  $this->getAllowedUsersTimeGap(),
1297  $this->getCertificateVisibility(),
1298  $this->getMailNotification(),
1299  time(),
1300  time()
1301  )
1302  );
1303  $this->test_id = $next_id;
1304 
1306  {
1307  $this->logAction($this->lng->txtlng("assessment", "log_create_new_test", ilObjAssessmentFolder::_getLogLanguage()));
1308  }
1309  }
1310  else
1311  {
1312  // Modify existing dataset
1313  $oldrow = array();
1315  {
1316  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
1317  array('integer'),
1318  array($this->test_id)
1319  );
1320  if ($result->numRows() == 1)
1321  {
1322  $oldrow = $ilDB->fetchAssoc($result);
1323  }
1324  }
1325  //echo $this->getRedirectOnlyKioskMode();
1326  //echo $this->getExamPdf();
1327  //echo $this->getRedirectAfterExam();
1328  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET author = %s, introduction = %s, " .
1329  "finalstatement = %s, redirect_after_exam = %s, redirect_only_kiosk_mode = %s, exam_pdf = %s, showinfo = %s, forcejs = %s, customstyle = %s, showfinalstatement = %s, sequence_settings = %s, " .
1330  "score_reporting = %s, instant_verification = %s, answer_feedback_points = %s, answer_feedback = %s, anonymity = %s, show_cancel = %s, autosave = %s, show_marker = %s, show_serial = %s, " .
1331  "fixed_participants = %s, nr_of_tries = %s, kiosk = %s, use_previous_answers = %s, title_output = %s, processing_time = %s, enable_processing_time = %s, " .
1332  "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, " .
1333  "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, " .
1334  "shuffle_questions = %s, results_presentation = %s, show_summary = %s, password = %s, allowedusers = %s, mailnottype = %s, exportsettings = %s, " .
1335  "alloweduserstimegap = %s, certificate_visibility = %s, mailnotification = %s, tstamp = %s WHERE test_id = %s",
1336  array(
1337  'text', 'text',
1338  'text', 'text', 'integer', 'integer', 'integer', 'integer', 'text', 'integer', 'integer',
1339  'integer', 'text', 'text', 'text', 'text', 'text', 'text', 'integer','text',
1340  'text', 'integer', 'integer', 'text', 'text', 'text', 'text',
1341  'integer', 'text', 'text', 'text', 'text', 'text', 'float', 'float', 'float', 'float',
1342  'float', 'float', 'text', 'integer', 'text', 'text', 'text', 'text',
1343  'text', 'integer', 'integer', 'text', 'integer','integer', 'integer',
1344  'integer', 'text', 'integer', 'integer', 'integer'
1345  ),
1346  array(
1347  $this->getAuthor(),
1350  $this->getRedirectAfterExam(),
1351  $this->getRedirectOnlyKioskMode(),
1352  $this->getExamPdf(),
1353  $this->getShowInfo(),
1354  $this->getForceJS(),
1355  $this->getCustomStyle(),
1356  $this->getShowFinalStatement(),
1357  $this->getSequenceSettings(),
1358  $this->getScoreReporting(),
1359  $this->getInstantFeedbackSolution(),
1360  $this->getAnswerFeedbackPoints(),
1361  $this->getAnswerFeedback(),
1362  $this->getAnonymity(),
1363  $this->getShowCancel(),
1364  $this->getAutosave(),
1365  $this->getShowMarker(),
1366  $this->getShowSerial(),
1367  $this->getFixedParticipants(),
1368  $this->getNrOfTries(),
1369  $this->getKiosk(),
1370  $this->getUsePreviousAnswers(),
1371  $this->getTitleOutput(),
1372  $this->getProcessingTime(),
1373  $this->getEnableProcessingTime(),
1374  $this->getResetProcessingTime(),
1375  $this->getReportingDate(),
1376  $this->getStartingTime(),
1377  $this->getEndingTime(),
1378  $this->isComplete(),
1379  $this->getECTSOutput(),
1380  strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : NULL,
1381  strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : NULL,
1382  strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : NULL,
1383  strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : NULL,
1384  strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : NULL,
1385  $this->getECTSFX(),
1386  $this->isRandomTest(),
1387  $this->getRandomQuestionCount(),
1388  $this->getCountSystem(),
1389  $this->getMCScoring(),
1390  $this->getScoreCutting(),
1391  $this->getPassScoring(),
1392  $this->getShuffleQuestions(),
1393  $this->getResultsPresentation(),
1394  $this->getListOfQuestionsSettings(),
1395  $this->getPassword(),
1396  $this->getAllowedUsers(),
1397  $this->getMailNotificationType(),
1398  $this->getExportSettings(),
1399  $this->getAllowedUsersTimeGap(),
1400  $this->getCertificateVisibility(),
1401  $this->getMailNotification(),
1402  time(),
1403  $this->getTestId()
1404  )
1405  );
1406 
1407  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1409  {
1410  $logresult = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
1411  array('integer'),
1412  array($this->getTestId())
1413  );
1414  $newrow = array();
1415  if ($logresult->numRows() == 1)
1416  {
1417  $newrow = $ilDB->fetchAssoc($logresult);
1418  }
1419  $changed_fields = array();
1420  foreach ($oldrow as $key => $value)
1421  {
1422  if (strcmp($oldrow[$key], $newrow[$key]) != 0)
1423  {
1424  array_push($changed_fields, "$key: " . $oldrow[$key] . " => " . $newrow[$key]);
1425  }
1426  }
1427  $changes = join($changed_fields, ", ");
1428  if (count($changed_fields) > 0)
1429  {
1430  $this->logAction($this->lng->txtlng("assessment", "log_modified_test", ilObjAssessmentFolder::_getLogLanguage()) . " [".$changes."]");
1431  }
1432  }
1433  if ($this->evalTotalPersons() > 0)
1434  {
1435  // reset the finished status of participants if the nr of test passes did change
1436  if ($this->getNrOfTries() > 0)
1437  {
1438  // set all unfinished tests with nr of passes >= allowed passes finished
1439  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND tries >= %s AND submitted = %s",
1440  array('integer', 'integer', 'integer'),
1441  array($this->getTestId(), $this->getNrOfTries(), 0)
1442  );
1443  while ($row = $ilDB->fetchAssoc($aresult))
1444  {
1445  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1446  array('integer', 'timestamp', 'integer'),
1447  array(1, date('Y-m-d H:i:s'), $row["active_id"])
1448  );
1449  }
1450 
1451  // set all finished tests with nr of passes < allowed passes not finished
1452  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND tries < %s AND submitted = %s",
1453  array('integer', 'integer', 'integer'),
1454  array($this->getTestId(), $this->getNrOfTries()-1, 1)
1455  );
1456  while ($row = $ilDB->fetchAssoc($aresult))
1457  {
1458  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1459  array('integer', 'timestamp', 'integer'),
1460  array(0, NULL, $row["active_id"])
1461  );
1462  }
1463  }
1464  else
1465  {
1466  // set all finished tests with nr of passes >= allowed passes not finished
1467  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND submitted = %s",
1468  array('integer', 'integer'),
1469  array($this->getTestId(), 1)
1470  );
1471  while ($row = $ilDB->fetchAssoc($aresult))
1472  {
1473  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1474  array('integer', 'timestamp', 'integer'),
1475  array(0, NULL, $row["active_id"])
1476  );
1477  }
1478  }
1479  }
1480  }
1481  if (!$this->isRandomTest())
1482  {
1484  }
1485  if (!$properties_only)
1486  {
1487  if (PEAR::isError($result))
1488  {
1489  global $ilias;
1490  $ilias->raiseError($result->getMessage());
1491  }
1492  else
1493  {
1494  if (!$this->isRandomTest())
1495  {
1496  $this->saveQuestionsToDb();
1497  }
1498  $this->mark_schema->saveToDb($this->test_id);
1499  }
1500  }
1501  }
1502 
1510  {
1511  global $ilDB;
1512 
1513  $oldquestions = array();
1514  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
1516  {
1517  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence",
1518  array('integer'),
1519  array($this->getTestId())
1520  );
1521  if ($result->numRows() > 0)
1522  {
1523  while ($row = $ilDB->fetchAssoc($result))
1524  {
1525  array_push($oldquestions, $row["question_fi"]);
1526  }
1527  }
1528  }
1529 
1530  // delete existing category relations
1531  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
1532  array('integer'),
1533  array($this->getTestId())
1534  );
1535  // create new category relations
1536  foreach ($this->questions as $key => $value)
1537  {
1538  $next_id = $ilDB->nextId('tst_test_question');
1539  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
1540  array('integer','integer', 'integer', 'integer', 'integer'),
1541  array($next_id, $this->getTestId(), $value, $key, time())
1542  );
1543  }
1544  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1546  {
1547  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence",
1548  array('integer'),
1549  array($this->getTestId())
1550  );
1551  $newquestions = array();
1552  if ($result->numRows() > 0)
1553  {
1554  while ($row = $ilDB->fetchAssoc($result))
1555  {
1556  array_push($newquestions, $row["question_fi"]);
1557  }
1558  }
1559  foreach ($oldquestions as $index => $question_id)
1560  {
1561  if (strcmp($newquestions[$index], $question_id) != 0)
1562  {
1563  $pos = array_search($question_id, $newquestions);
1564  if ($pos === FALSE)
1565  {
1566  $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id);
1567  }
1568  else
1569  {
1570  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index+1) . " => " . ($pos+1), $question_id);
1571  }
1572  }
1573  }
1574  foreach ($newquestions as $index => $question_id)
1575  {
1576  if (array_search($question_id, $oldquestions) === FALSE)
1577  {
1578  $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index+1), $question_id);
1579  }
1580  }
1581  }
1582  }
1583 
1587  protected function isNewRandomTest()
1588  {
1589  global $ilDB;
1590  $result = $ilDB->queryF('SELECT copy_id FROM tst_rnd_cpy WHERE tst_fi = %s',
1591  array('integer'),
1592  array($this->getTestId())
1593  );
1594  return $result->numRows() > 0;
1595  }
1596 
1603  function saveRandomQuestion($active_id, $question_id, $pass = NULL, $maxcount)
1604  {
1605  global $ilUser;
1606  global $ilDB;
1607 
1608  if (is_null($pass)) $pass = 0;
1609  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s",
1610  array('integer','integer'),
1611  array($active_id, $pass)
1612  );
1613  if ($result->numRows() < $maxcount)
1614  {
1615  $duplicate_id = $question_id;
1616  if (!$this->isNewRandomTest())
1617  {
1618  $duplicate_id = $this->getRandomQuestionDuplicate($question_id, $active_id);
1619  if ($duplicate_id === FALSE)
1620  {
1621  $duplicate_id = $this->duplicateQuestionForTest($question_id);
1622  }
1623  }
1624  $next_id = $ilDB->nextId('tst_test_rnd_qst');
1625  $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)",
1626  array('integer','integer','integer','integer','integer','integer'),
1627  array($next_id,$active_id, $duplicate_id, $result->numRows()+1, $pass, time())
1628  );
1629  }
1630  }
1631 
1641  function getRandomQuestionDuplicate($question_id, $active_id)
1642  {
1643  global $ilDB;
1644 
1645  $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",
1646  array('integer', 'integer'),
1647  array($question_id, $active_id)
1648  );
1649  $num = $result->numRows();
1650  if ($num > 0)
1651  {
1652  $row = $ilDB->fetchAssoc($result);
1653  return $row["question_id"];
1654  }
1655  else
1656  {
1657  return FALSE;
1658  }
1659  }
1660 
1666  function getNrOfResultsForPass($active_id, $pass)
1667  {
1668  global $ilDB;
1669 
1670  $result = $ilDB->queryF("SELECT test_result_id FROM tst_test_result WHERE active_fi = %s AND pass = %s",
1671  array('integer','integer'),
1672  array($active_id, $pass)
1673  );
1674  return $result->numRows();
1675  }
1676 
1685  function hasRandomQuestionsForPass($active_id, $pass)
1686  {
1687  global $ilDB;
1688  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s",
1689  array('integer','integer'),
1690  array($active_id, $pass)
1691  );
1692  return ($result->numRows() > 0) ? true : false;
1693  }
1694 
1701  function generateRandomQuestions($active_id, $pass = NULL)
1702  {
1703  global $ilUser;
1704  global $ilDB;
1705 
1706  if ($active_id > 0)
1707  {
1708  if ($this->hasRandomQuestionsForPass($active_id, $pass) > 0)
1709  {
1710  // Something went wrong. Maybe the user pressed the start button twice
1711  // Questions already exist so there is no need to create new questions
1712  return;
1713  }
1714  if ($pass > 0)
1715  {
1716  if ($this->getNrOfResultsForPass($active_id, $pass - 1) == 0)
1717  {
1718  // This means that someone maybe reloaded the test submission page
1719  // If there are no existing results for the previous test, it makes
1720  // no sense to create a new set of random questions
1721  return;
1722  }
1723  }
1724  }
1725  else
1726  {
1727  // This may not happen! If it happens, raise a fatal error...
1728  global $ilias, $ilErr;
1729  $ilias->raiseError(sprintf($this->lng->txt("error_random_question_generation"), $ilUser->getId(), $this->getTestId()), $ilErr->FATAL);
1730  }
1731 
1732  $num = $this->getRandomQuestionCount();
1733  if ($num > 0)
1734  {
1735  $qpls =& $this->getRandomQuestionpools();
1736  $rndquestions = $this->generateRandomPass($num, $qpls, $pass);
1737  $allquestions = array();
1738  foreach ($rndquestions as $question_id)
1739  {
1740  array_push($allquestions, $question_id);
1741  }
1742  if ($this->getShuffleQuestions())
1743  {
1744  srand ((float)microtime()*1000000);
1745  shuffle($allquestions);
1746  }
1747 
1748  $maxcount = 0;
1749  foreach ($qpls as $data)
1750  {
1751  $maxcount += $data["contains"];
1752  }
1753  if ($num > $maxcount) $num = $maxcount;
1754  foreach ($allquestions as $question_id)
1755  {
1756  $this->saveRandomQuestion($active_id, $question_id, $pass, $num);
1757  }
1758  }
1759  else
1760  {
1761  $qpls =& $this->getRandomQuestionpools();
1762  $allquestions = array();
1763  $maxcount = 0;
1764  foreach ($qpls as $key => $value)
1765  {
1766  if ($value["count"] > 0)
1767  {
1768  $rndquestions = $this->generateRandomPass($value["count"], array($value), $pass);
1769  foreach ($rndquestions as $question_id)
1770  {
1771  array_push($allquestions, $question_id);
1772  }
1773  }
1774  $add = ($value["count"] <= $value["contains"]) ? $value["count"] : $value["contains"];
1775  $maxcount += $add;
1776  }
1777  if ($this->getShuffleQuestions())
1778  {
1779  srand ((float)microtime()*1000000);
1780  shuffle($allquestions);
1781  }
1782  foreach ($allquestions as $question_id)
1783  {
1784  $this->saveRandomQuestion($active_id, $question_id, $pass, $maxcount);
1785  }
1786  }
1787  }
1788 
1795  function saveRandomQuestionCount($total_questions = NULL)
1796  {
1797  global $ilDB;
1798 
1799  if (strlen($total_questions))
1800  {
1801  $this->setRandomQuestionCount($total_questions);
1802  }
1803  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET random_question_count = %s, tstamp = %s WHERE test_id = %s",
1804  array('integer', 'integer', 'integer'),
1805  array($this->getRandomQuestionCount(), time(), $this->getTestId())
1806  );
1807  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1809  {
1810  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_total_amount_of_questions", ilObjAssessmentFolder::_getLogLanguage()), $this->getRandomQuestionCount()));
1811  }
1812  }
1813 
1822  {
1823  global $ilDB;
1824 
1825  $qpls = array();
1826  $counter = 0;
1827  $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",
1828  array("integer"),
1829  array($this->getTestId())
1830  );
1831  if ($result->numRows())
1832  {
1833  while ($row = $ilDB->fetchAssoc($result))
1834  {
1835  $qpls[$counter] = array(
1836  "index" => $counter,
1837  "count" => $row["num_of_q"],
1838  "qpl" => $row["questionpool_fi"],
1839  "contains" => $row["questioncount"]
1840  );
1841  $counter++;
1842  }
1843  }
1844  return $qpls;
1845  }
1846 
1850  public function saveRandomQuestionpools()
1851  {
1852  global $ilDB;
1853 
1854  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1855  // delete existing random questionpools
1856  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
1857  array('integer'),
1858  array($this->getTestId())
1859  );
1861  {
1862  $this->logAction($this->lng->txtlng("assessment", "log_random_question_pool_deleted", ilObjAssessmentFolder::_getLogLanguage()));
1863  }
1864  // delete existing duplicated questions
1866 
1867  // create new random questionpools
1868  foreach ($this->random_questionpool_data as $idx => $data)
1869  {
1870  if ($data->qpl > 0)
1871  {
1872  // save questionpool information
1873  $next_id = $ilDB->nextId('tst_test_random');
1874  $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)",
1875  array('integer','integer', 'integer', 'integer', 'integer', 'integer'),
1876  array($next_id, $this->getTestId(), $data->qpl, $data->count, time(), $idx)
1877  );
1879  {
1880  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_random_question_pool_added", ilObjAssessmentFolder::_getLogLanguage()), $value["title"] . " (" . $value["qpl"] . ")", $value["count"]));
1881  }
1882  // duplicate all questions of the questionpools
1883  $this->duplicateQuestionpoolForTest($data->qpl);
1884  }
1885  }
1886  }
1887 
1891  protected function removeDuplicatedQuestionpools()
1892  {
1893  global $ilDB;
1894 
1895  $result = $ilDB->queryF('SELECT * FROM tst_rnd_cpy WHERE tst_fi = %s',
1896  array('integer'),
1897  array($this->getTestId())
1898  );
1899  while ($row = $ilDB->fetchAssoc($result))
1900  {
1901  $question =& ilObjTest::_instanciateQuestion($row['qst_fi']);
1902  $question->delete($row['qst_fi']);
1903  }
1904 
1905  $affectedRows = $ilDB->manipulateF('DELETE FROM tst_rnd_cpy WHERE tst_fi = %s',
1906  array('integer'),
1907  array($this->getTestId())
1908  );
1909 
1910  $affectedRows = $ilDB->manipulateF('DELETE FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1911  array('integer'),
1912  array($this->getTestId())
1913  );
1914  }
1915 
1919  public function getUsedRandomQuestionpools()
1920  {
1921  global $ilDB;
1922  if ($this->isNewRandomTest())
1923  {
1924  $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',
1925  array('integer'),
1926  array($this->getTestId())
1927  );
1928  $pools = array();
1929  while ($row = $ilDB->fetchAssoc($result))
1930  {
1931  if (is_array($pools[$row['qpl_fi']]))
1932  {
1933  $pools[$row['qpl_fi']]['count']++;
1934  }
1935  else
1936  {
1937  $pools[$row['qpl_fi']]['count'] = 1;
1938  }
1939  $pools[$row['qpl_fi']]['num_of_q'] = $row['num_of_q'];
1940  }
1941  $result = $ilDB->queryF('SELECT * FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1942  array('integer'),
1943  array($this->getTestId())
1944  );
1945 
1946  while ($row = $ilDB->fetchAssoc($result))
1947  {
1948  $pools[$row['qpl_fi']]['title'] = $row['qpl_title'];
1949  }
1950  return $pools;
1951  }
1952  else
1953  {
1954  $result = $ilDB->queryF('SELECT tst_test_random.* FROM tst_test_random WHERE tst_test_random.test_fi = %s ORDER BY sequence, test_random_id',
1955  array('integer'),
1956  array($this->getTestId())
1957  );
1958  $pools = array();
1959  while ($row = $ilDB->fetchAssoc($result))
1960  {
1961  $pools[$row['questionpool_fi']]['count'] = $row['num_of_q'];
1962  $pools[$row['questionpool_fi']]['num_of_q'] = $row['num_of_q'];
1963  }
1964  $result = $ilDB->queryF('SELECT * FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1965  array('integer'),
1966  array($this->getTestId())
1967  );
1968 
1969  while ($row = $ilDB->fetchAssoc($result))
1970  {
1971  $pools[$row['qpl_fi']]['title'] = $row['qpl_title'];
1972  }
1973  return $pools;
1974  }
1975  }
1976 
1980  protected function duplicateQuestionpoolForTest($questionpool_id)
1981  {
1982  global $ilDB;
1983  $result = $ilDB->queryF('SELECT question_id FROM qpl_questions WHERE obj_fi = %s AND complete = %s AND original_id IS NULL',
1984  array('integer', 'text'),
1985  array($questionpool_id, 1)
1986  );
1987  $saved_titles = array();
1988  while ($row = $ilDB->fetchAssoc($result))
1989  {
1990  $question =& ilObjTest::_instanciateQuestion($row['question_id']);
1991  $duplicate_id = $question->duplicate(true);
1992  if ($duplicate_id > 0)
1993  {
1994  $next_id = $ilDB->nextId('tst_rnd_cpy');
1995  $ilDB->manipulateF('INSERT INTO tst_rnd_cpy (copy_id, tst_fi, qst_fi, qpl_fi) VALUES (%s, %s, %s, %s)',
1996  array('integer', 'integer', 'integer', 'integer'),
1997  array($next_id, $this->getTestId(), $duplicate_id, $questionpool_id)
1998  );
1999  if (!array_key_exists($questionpool_id, $saved_titles))
2000  {
2001  $next_id = $ilDB->nextId('tst_rnd_qpl_title');
2002  $ilDB->manipulateF('INSERT INTO tst_rnd_qpl_title (title_id, tst_fi, qpl_fi, qpl_title) VALUES (%s, %s, %s, %s)',
2003  array('integer', 'integer', 'integer', 'text'),
2004  array($next_id, $this->getTestId(), $questionpool_id, ilObject::_lookupTitle($questionpool_id))
2005  );
2006  $saved_titles[$questionpool_id] = 1;
2007  }
2008  }
2009  }
2010  }
2011 
2012  function addRandomQuestionpoolData($count = 0, $qpl = 0, $position)
2013  {
2014  include_once "./Modules/Test/classes/class.ilRandomTestData.php";
2015  if (array_key_exists($position, $this->random_questionpool_data))
2016  {
2017  $newitems = array();
2018  for ($i = 0; $i < $position; $i++)
2019  {
2020  array_push($newitems, $this->random_questionpool_data[$i]);
2021  }
2022  array_push($newitems, new ilRandomTestData($count, $qpl));
2023  for ($i = $position; $i < count($this->random_questionpool_data); $i++)
2024  {
2025  array_push($newitems, $this->random_questionpool_data[$i]);
2026  }
2027  $this->random_questionpool_data = $newitems;
2028  }
2029  else
2030  {
2031  array_push($this->random_questionpool_data, new ilRandomTestData($count, $qpl));
2032  }
2033  }
2034 
2035  function removeRandomQuestionpoolData($position)
2036  {
2037  if (array_key_exists($position, $this->random_questionpool_data))
2038  {
2039  unset($this->random_questionpool_data[$position]);
2040  }
2041  }
2042 
2043  function setRandomQuestionpoolData($a_data)
2044  {
2045  $this->random_questionpool_data = $a_data;
2046  }
2047 
2056  {
2057  if (is_array($this->random_questionpool_data) && count($this->random_questionpool_data)) return $this->random_questionpool_data;
2058 
2059  global $ilDB;
2060 
2061  $qpls = array();
2062  $counter = 0;
2063  $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",
2064  array("integer"),
2065  array($this->getTestId())
2066  );
2067  include_once "./Modules/Test/classes/class.ilRandomTestData.php";
2068  if ($result->numRows())
2069  {
2070  while ($row = $ilDB->fetchAssoc($result))
2071  {
2072  array_push($qpls, new ilRandomTestData($row['num_of_q'], $row['questionpool_fi']));
2073  }
2074  }
2075  return $qpls;
2076  }
2077 
2085  function loadFromDb()
2086  {
2087  global $ilDB;
2088 
2089  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE obj_fi = %s",
2090  array('integer'),
2091  array($this->getId())
2092  );
2093  if ($result->numRows() == 1)
2094  {
2095  $data = $ilDB->fetchObject($result);
2096  $this->setTestId($data->test_id);
2097  if (strlen($this->getAuthor()) == 0)
2098  {
2099  $this->saveAuthorToMetadata($data->author);
2100  }
2101  $this->setAuthor($data->author);
2102  include_once("./Services/RTE/classes/class.ilRTE.php");
2103  $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc($data->introduction, 1));
2104  $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc($data->finalstatement, 1));
2105  /* Change Sn */
2106  $this->setRedirectAfterExam($data->redirect_after_exam);
2107  $this->setRedirectOnlyKioskMode($data->redirect_only_kiosk_mode);
2108  $this->setExamPdf($data->exam_pdf);
2109  /* Change Sn End */
2110  $this->setShowInfo($data->showinfo);
2111  $this->setForceJS($data->forcejs);
2112  $this->setCustomStyle($data->customstyle);
2113  $this->setShowFinalStatement($data->showfinalstatement);
2114  $this->setSequenceSettings($data->sequence_settings);
2115  $this->setScoreReporting($data->score_reporting);
2116  $this->setInstantFeedbackSolution($data->instant_verification);
2117  $this->setAnswerFeedbackPoints($data->answer_feedback_points);
2118  $this->setAnswerFeedback($data->answer_feedback);
2119  $this->setAnonymity($data->anonymity);
2120  $this->setShowCancel($data->show_cancel);
2121  $this->setAutosave($data->autosave);
2122  $this->setShowMarker($data->show_marker);
2123  $this->setShowSerial($data->show_serial);
2124  $this->setFixedParticipants($data->fixed_participants);
2125  $this->setNrOfTries($data->nr_of_tries);
2126  $this->setKiosk($data->kiosk);
2127  $this->setUsePreviousAnswers($data->use_previous_answers);
2128  $this->setTitleOutput($data->title_output);
2129  $this->setProcessingTime($data->processing_time);
2130  $this->setEnableProcessingTime($data->enable_processing_time);
2131  $this->setResetProcessingTime($data->reset_processing_time);
2132  $this->setReportingDate($data->reporting_date);
2133  $this->setShuffleQuestions($data->shuffle_questions);
2134  $this->setResultsPresentation($data->results_presentation);
2135  $this->setListOfQuestionsSettings($data->show_summary);
2136  $this->setStartingTime($data->starting_time);
2137  $this->setEndingTime($data->ending_time);
2138  $this->setECTSOutput($data->ects_output);
2139  $this->setECTSGrades(
2140  array(
2141  "A" => $data->ects_a,
2142  "B" => $data->ects_b,
2143  "C" => $data->ects_c,
2144  "D" => $data->ects_d,
2145  "E" => $data->ects_e
2146  )
2147  );
2148  $this->setECTSFX($data->ects_fx);
2149  $this->setRandomTest($data->random_test);
2150  $this->setRandomQuestionCount($data->random_question_count);
2151  $this->mark_schema->flush();
2152  $this->mark_schema->loadFromDb($this->getTestId());
2153  $this->setCountSystem($data->count_system);
2154  $this->setMCScoring($data->mc_scoring);
2155  $this->setMailNotification($data->mailnotification);
2156  $this->setMailNotificationType($data->mailnottype);
2157  $this->setExportSettings($data->exportsettings);
2158  $this->setScoreCutting($data->score_cutting);
2159  $this->setPassword($data->password);
2160  $this->setAllowedUsers($data->allowedusers);
2161  $this->setAllowedUsersTimeGap($data->alloweduserstimegap);
2162  $this->setPassScoring($data->pass_scoring);
2163  $this->setCertificateVisibility($data->certificate_visibility);
2164  $this->loadQuestions();
2165  }
2166  }
2167 
2174 function loadQuestions($active_id = "", $pass = NULL)
2175 {
2176  global $ilUser;
2177  global $ilDB;
2178 
2179  $this->questions = array();
2180  if (strcmp($active_id, "") == 0)
2181  {
2182  $active_id = $this->getActiveIdOfUser($ilUser->getId());
2183  }
2184  if ($this->isRandomTest())
2185  {
2186  if (is_null($pass))
2187  {
2188  $pass = $this->_getPass($active_id);
2189  }
2190  $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",
2191  array('integer', 'integer'),
2192  array($active_id, $pass)
2193  );
2194  // The following is a fix for random tests prior to ILIAS 3.8. If someone started a random test in ILIAS < 3.8, there
2195  // is only one test pass (pass = 0) in tst_test_rnd_qst while with ILIAS 3.8 there are questions for every test pass.
2196  // To prevent problems with tests started in an older version and continued in ILIAS 3.8, the first pass should be taken if
2197  // no questions are present for a newer pass.
2198  if ($result->numRows() == 0)
2199  {
2200  $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",
2201  array('integer'),
2202  array($active_id)
2203  );
2204  }
2205  }
2206  else
2207  {
2208  $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",
2209  array('integer'),
2210  array($this->test_id)
2211  );
2212  }
2213  $index = 1;
2214  while ($data = $ilDB->fetchAssoc($result))
2215  {
2216  $this->questions[$index++] = $data["question_fi"];
2217  }
2218 }
2219 
2228  {
2229  $this->introduction = $introduction;
2230  }
2231 
2239  public function setFinalStatement($a_statement = "")
2240  {
2241  $this->_finalstatement = $a_statement;
2242  }
2243 
2244  /* Change Sn */
2254  public function setRedirectAfterExam($a_url = "")
2255  {
2256  $this->_redirect_after_exam = $a_url;
2257  }
2258 
2268  public function setRedirectOnlyKioskMode($a_int = 1)
2269  {
2270  $this->_redirect_only_kiosk_mode = ($a_int) ? 1 : 0;
2271  }
2272 
2282  public function setExamPdf($a_int = 0)
2283  {
2284  $this->_exam_pdf = ($a_int) ? 1 : 0;
2285  }
2286  /* Change Sn End */
2294  public function setShowInfo($a_info = 1)
2295  {
2296  $this->_showinfo = ($a_info) ? 1 : 0;
2297  }
2298 
2306  public function setForceJS($a_js = 1)
2307  {
2308  $this->_forcejs = ($a_js) ? 1 : 0;
2309  }
2310 
2318  public function setCustomStyle($a_customStyle = NULL)
2319  {
2320  $this->_customStyle = $a_customStyle;
2321  }
2322 
2330  public function getCustomStyle()
2331  {
2332  return (strlen($this->_customStyle)) ? $this->_customStyle : NULL;
2333  }
2334 
2342  public function getCustomStyles()
2343  {
2344  $css_path = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test");
2345  $css_path = str_replace("ta.css", "customstyles", $css_path) . "/";
2346  $customstyles = array();
2347  if (is_dir($css_path))
2348  {
2349  $results = array();
2350  include_once "./Services/Utilities/classes/class.ilFileUtils.php";
2352  if (is_array($results["file"]))
2353  {
2354  foreach ($results["file"] as $filename)
2355  {
2356  if (strpos($filename, ".css"))
2357  {
2358  array_push($customstyles, $filename);
2359  }
2360  }
2361  }
2362  }
2363  return $customstyles;
2364  }
2365 
2373  public function getTestStyleLocation($mode = "output")
2374  {
2375  if (strlen($this->getCustomStyle()))
2376  {
2377  $default = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test");
2378  $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $default);
2379  if (file_exists($custom))
2380  {
2381  $custom = ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2382  $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $custom);
2383  return $custom;
2384  }
2385  else
2386  {
2387  return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2388  }
2389  }
2390  else
2391  {
2392  return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2393  }
2394  }
2395 
2403  public function setShowFinalStatement($show = 0)
2404  {
2405  $this->_showfinalstatement = ($show) ? 1 : 0;
2406  }
2407 
2408 
2416  function isRandomTest()
2417  {
2418  return ($this->random_test) ? 1 : 0;
2419  }
2420 
2421 
2427  public static function _lookupRandomTest($a_obj_id)
2428  {
2429  global $ilDB;
2430 
2431  $query = "SELECT random_test FROM tst_tests ".
2432  "WHERE obj_fi = ".$ilDB->quote($a_obj_id,'integer');
2433  $res = $ilDB->query($query);
2434  while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
2435  {
2436  return $row->random_test ? true : false;
2437  }
2438  return false;
2439  }
2440 
2449  {
2450  return ($this->random_question_count) ? $this->random_question_count : 0;
2451  }
2452 
2459  public function getIntroduction()
2460  {
2461  return (strlen($this->introduction)) ? $this->introduction : NULL;
2462  }
2463 
2470  public function getFinalStatement()
2471  {
2472  return (strlen($this->_finalstatement)) ? $this->_finalstatement : NULL;
2473  }
2474  /* Change Sn */
2482  public function getRedirectAfterExam()
2483  {
2484  return (strlen($this->_redirect_after_exam)) ? $this->_redirect_after_exam : NULL;
2485  }
2486 
2494  public function getRedirectOnlyKioskMode()
2495  {
2496  return ($this->_redirect_only_kiosk_mode) ? 1 : 0;
2497  }
2498 
2506  public function getExamPdf()
2507  {
2508  return ($this->_exam_pdf) ? 1 : 0;
2509  }
2510  /* Change Sn End */
2518  public function getShowInfo()
2519  {
2520  return ($this->_showinfo) ? 1 : 0;
2521  }
2522 
2530  public function getForceJS()
2531  {
2532  return ($this->_forcejs) ? 1 : 0;
2533  }
2534 
2542  public function getShowFinalStatement()
2543  {
2544  return ($this->_showfinalstatement) ? 1 : 0;
2545  }
2546 
2554  function getTestId()
2555  {
2556  return $this->test_id;
2557  }
2558 
2566  function getECTSOutput()
2567  {
2568  return ($this->ects_output) ? 1 : 0;
2569  }
2570 
2578  function setECTSOutput($a_ects_output)
2579  {
2580  $this->ects_output = $a_ects_output ? 1 : 0;
2581  }
2582 
2590  function getECTSFX()
2591  {
2592  return (strlen($this->ects_fx)) ? $this->ects_fx : NULL;
2593  }
2594 
2602  function setECTSFX($a_ects_fx)
2603  {
2604  $this->ects_fx = $a_ects_fx;
2605  }
2606 
2614  function &getECTSGrades()
2615  {
2616  return $this->ects_grades;
2617  }
2618 
2626  function setECTSGrades($a_ects_grades)
2627  {
2628  if (is_array($a_ects_grades))
2629  {
2630  $this->ects_grades = $a_ects_grades;
2631  }
2632  }
2633 
2642  {
2643  $this->sequence_settings = $sequence_settings;
2644  }
2645 
2654  {
2655  $this->score_reporting = $score_reporting;
2656  }
2657 
2665  function setInstantFeedbackSolution($instant_feedback = 0)
2666  {
2667  switch ($instant_feedback)
2668  {
2669  case 1:
2670  $this->instant_verification = 1;
2671  break;
2672  default:
2673  $this->instant_verification = 0;
2674  break;
2675  }
2676  }
2677 
2686  {
2687  switch ($answer_feedback)
2688  {
2689  case 1:
2690  $this->answer_feedback = 1;
2691  break;
2692  default:
2693  $this->answer_feedback = 0;
2694  break;
2695  }
2696  }
2697 
2706  {
2707  switch ($answer_feedback_points)
2708  {
2709  case 1:
2710  $this->answer_feedback_points = 1;
2711  break;
2712  default:
2713  $this->answer_feedback_points = 0;
2714  break;
2715  }
2716  }
2717 
2725  function setRandomTest($a_random_test = 0)
2726  {
2727  $this->random_test = $a_random_test;
2728  }
2729 
2737  function setRandomQuestionCount($a_random_question_count = "")
2738  {
2739  $this->random_question_count = $a_random_question_count;
2740  }
2741 
2750  {
2751  if (!$reporting_date)
2752  {
2753  $this->reporting_date = "";
2754  $this->ects_output = 0;
2755  }
2756  else
2757  {
2758  $this->reporting_date = $reporting_date;
2759  }
2760  }
2761 
2770  {
2771  return ($this->sequence_settings) ? $this->sequence_settings : 0;
2772  }
2773 
2782  {
2783  return ($this->score_reporting) ? $this->score_reporting : 0;
2784  }
2785 
2794  {
2795  return ($this->instant_verification) ? $this->instant_verification : 0;
2796  }
2797 
2806  {
2807  return ($this->answer_feedback) ? $this->answer_feedback : 0;
2808  }
2809 
2818  {
2819  return ($this->answer_feedback_points) ? $this->answer_feedback_points : 0;
2820  }
2821 
2829  function getCountSystem()
2830  {
2831  return ($this->count_system) ? $this->count_system : 0;
2832  }
2833 
2841  function _getCountSystem($active_id)
2842  {
2843  global $ilDB;
2844  $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",
2845  array('integer'),
2846  array($active_id)
2847  );
2848  if ($result->numRows())
2849  {
2850  $row = $ilDB->fetchAssoc($result);
2851  return $row["count_system"];
2852  }
2853  return FALSE;
2854  }
2855 
2863  function getMCScoring()
2864  {
2865  return ($this->mc_scoring) ? $this->mc_scoring : 0;
2866  }
2867 
2875  function getScoreCutting()
2876  {
2877  return ($this->score_cutting) ? $this->score_cutting : 0;
2878  }
2879 
2887  function getPassword()
2888  {
2889  return (strlen($this->password)) ? $this->password : NULL;
2890  }
2891 
2899  function getPassScoring()
2900  {
2901  return ($this->pass_scoring) ? $this->pass_scoring : 0;
2902  }
2903 
2911  function _getPassScoring($active_id)
2912  {
2913  global $ilDB;
2914  $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",
2915  array('integer'),
2916  array($active_id)
2917  );
2918  if ($result->numRows())
2919  {
2920  $row = $ilDB->fetchAssoc($result);
2921  return $row["pass_scoring"];
2922  }
2923  return 0;
2924  }
2925 
2933  function _getMCScoring($active_id)
2934  {
2935  global $ilDB;
2936  $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",
2937  array('integer'),
2938  array($active_id)
2939  );
2940  if ($result->numRows())
2941  {
2942  $row = $ilDB->fetchAssoc($result);
2943  return $row["mc_scoring"];
2944  }
2945  return FALSE;
2946  }
2947 
2955  function _getScoreCutting($active_id)
2956  {
2957  global $ilDB;
2958  $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",
2959  array('integer'),
2960  array($active_id)
2961  );
2962  if ($result->numRows())
2963  {
2964  $row = $ilDB->fetchAssoc($result);
2965  return $row["score_cutting"];
2966  }
2967  return FALSE;
2968  }
2969 
2977  function getReportingDate()
2978  {
2979  return (strlen($this->reporting_date)) ? $this->reporting_date : NULL;
2980  }
2981 
2989  function getNrOfTries()
2990  {
2991  return ($this->nr_of_tries) ? $this->nr_of_tries : 0;
2992  }
2993 
3001  function getKiosk()
3002  {
3003  return ($this->_kiosk) ? $this->_kiosk : 0;
3004  }
3005 
3006 
3014  function setKiosk($kiosk = 0)
3015  {
3016  $this->_kiosk = $kiosk;
3017  }
3018 
3026  function getKioskMode()
3027  {
3028  if (($this->_kiosk & 1) > 0)
3029  {
3030  return TRUE;
3031  }
3032  else
3033  {
3034  return FALSE;
3035  }
3036  }
3037 
3045  public function setKioskMode($a_kiosk = FALSE)
3046  {
3047  if ($a_kiosk)
3048  {
3049  $this->_kiosk = $this->_kiosk | 1;
3050  }
3051  else
3052  {
3053  if ($this->getKioskMode())
3054  {
3055  $this->_kiosk = $this->_kiosk ^ 1;
3056  }
3057  }
3058  }
3059 
3067  public function getShowKioskModeTitle()
3068  {
3069  if (($this->_kiosk & 2) > 0)
3070  {
3071  return TRUE;
3072  }
3073  else
3074  {
3075  return FALSE;
3076  }
3077  }
3078 
3085  public function setShowKioskModeTitle($a_title = FALSE)
3086  {
3087  if ($a_title)
3088  {
3089  $this->_kiosk = $this->_kiosk | 2;
3090  }
3091  else
3092  {
3093  if ($this->getShowKioskModeTitle())
3094  {
3095  $this->_kiosk = $this->_kiosk ^ 2;
3096  }
3097  }
3098  }
3099 
3108  {
3109  if (($this->_kiosk & 4) > 0)
3110  {
3111  return TRUE;
3112  }
3113  else
3114  {
3115  return FALSE;
3116  }
3117  }
3118 
3125  public function setShowKioskModeParticipant($a_participant = FALSE)
3126  {
3127  if ($a_participant)
3128  {
3129  $this->_kiosk = $this->_kiosk | 4;
3130  }
3131  else
3132  {
3133  if ($this->getShowKioskModeParticipant())
3134  {
3135  $this->_kiosk = $this->_kiosk ^ 4;
3136  }
3137  }
3138  }
3139 
3148  {
3149  return ($this->use_previous_answers) ? $this->use_previous_answers : 0;
3150  }
3151 
3159  function getTitleOutput()
3160  {
3161  return ($this->title_output) ? $this->title_output : 0;
3162  }
3163 
3172  function _getTitleOutput($active_id)
3173  {
3174  global $ilDB;
3175 
3176  $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",
3177  array('integer'),
3178  array($active_id)
3179  );
3180  if ($result->numRows())
3181  {
3182  $row = $ilDB->fetchAssoc($result);
3183  return $row["title_output"];
3184  }
3185  return 0;
3186  }
3187 
3197  function _getUsePreviousAnswers($active_id, $user_active_user_setting = false)
3198  {
3199  global $ilDB;
3200  global $ilUser;
3201 
3203 
3204  $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",
3205  array("integer"),
3206  array($active_id)
3207  );
3208  if ($result->numRows())
3209  {
3210  $row = $ilDB->fetchAssoc($result);
3211  $use_previous_answers = $row["use_previous_answers"];
3212  }
3213 
3214  if ($use_previous_answers == 1)
3215  {
3216  if ($user_active_user_setting)
3217  {
3218  $res = $ilUser->getPref("tst_use_previous_answers");
3219  if ($res !== FALSE)
3220  {
3222  }
3223  }
3224  }
3225  return $use_previous_answers;
3226  }
3227 
3236  {
3237  return (strlen($this->processing_time)) ? $this->processing_time : NULL;
3238  }
3239 
3246  public function getProcessingTimeAsArray()
3247  {
3248  if (strlen($this->processing_time))
3249  {
3250  if (preg_match("/(\d{2}):(\d{2}):(\d{2})/is", $this->processing_time, $matches))
3251  {
3252  if ((int)$matches[1]+(int)$matches[2]+(int)$matches[3] == 0)
3253  {
3254  return $this->getEstimatedWorkingTime();
3255  }
3256  else
3257  {
3258  return array(
3259  'hh' => $matches[1],
3260  'mm' => $matches[2],
3261  'ss' => $matches[3],
3262  );
3263  }
3264  }
3265  }
3266  return $this->getEstimatedWorkingTime();
3267  }
3268 
3276  function getProcessingTimeInSeconds($active_id = "")
3277  {
3278  if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $this->getProcessingTime(), $matches))
3279  {
3280  $extratime = $this->getExtraTime($active_id) * 60;
3281  return ($matches[1] * 3600) + ($matches[2] * 60) + $matches[3] + $extratime;
3282  }
3283  else
3284  {
3285  return 0;
3286  }
3287  }
3288 
3297  {
3298  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndingTime(), $matches))
3299  {
3300  $ending = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
3301  $now = time();
3302  return $ending - $now;
3303  }
3304  else
3305  {
3306  return 0;
3307  }
3308  }
3309 
3318  {
3319  return ($this->enable_processing_time) ? $this->enable_processing_time : 0;
3320  }
3321 
3330  {
3331  return ($this->reset_processing_time) ? $this->reset_processing_time : 0;
3332  }
3333 
3341  function getStartingTime()
3342  {
3343  return (strlen($this->starting_time)) ? $this->starting_time : NULL;
3344  }
3345 
3353  function getEndingTime()
3354  {
3355  return (strlen($this->ending_time)) ? $this->ending_time : NULL;
3356  }
3357 
3366  {
3367  $this->nr_of_tries = $nr_of_tries;
3368  }
3369 
3378  {
3380  {
3381  $this->use_previous_answers = 1;
3382  }
3383  else
3384  {
3385  $this->use_previous_answers = 0;
3386  }
3387  }
3388 
3397  {
3398  switch ($title_output)
3399  {
3400  case 1:
3401  $this->title_output = 1;
3402  break;
3403  case 2:
3404  $this->title_output = 2;
3405  break;
3406  default:
3407  $this->title_output = 0;
3408  break;
3409  }
3410  }
3411 
3419  function setProcessingTime($processing_time = "00:00:00")
3420  {
3421  $this->processing_time = $processing_time;
3422  }
3423 
3431  function setEnableProcessingTime($enable = 0)
3432  {
3433  if ($enable) {
3434  $this->enable_processing_time = "1";
3435  } else {
3436  $this->enable_processing_time = "0";
3437  }
3438  }
3439 
3447  function setResetProcessingTime($reset = 0)
3448  {
3449  if ($reset)
3450  {
3451  $this->reset_processing_time = 1;
3452  }
3453  else
3454  {
3455  $this->reset_processing_time = 0;
3456  }
3457  }
3458 
3467  {
3468  $this->starting_time = $starting_time;
3469  }
3470 
3478  function setEndingTime($ending_time = NULL)
3479  {
3480  $this->ending_time = $ending_time;
3481  }
3482 
3490  function setCountSystem($a_count_system = COUNT_PARTIAL_SOLUTIONS)
3491  {
3492  $this->count_system = $a_count_system;
3493  }
3494 
3502  function setPassword($a_password = NULL)
3503  {
3504  $this->password = $a_password;
3505  }
3506 
3514  function setScoreCutting($a_score_cutting = SCORE_CUT_QUESTION)
3515  {
3516  $this->score_cutting = $a_score_cutting;
3517  }
3518 
3527  {
3528  $this->mc_scoring = $a_mc_scoring;
3529  }
3530 
3538  function setPassScoring($a_pass_scoring = SCORE_LAST_PASS)
3539  {
3540  switch ($a_pass_scoring)
3541  {
3542  case SCORE_BEST_PASS:
3543  $this->pass_scoring = SCORE_BEST_PASS;
3544  break;
3545  default:
3546  $this->pass_scoring = SCORE_LAST_PASS;
3547  break;
3548  }
3549  }
3550 
3558  function removeQuestion($question_id)
3559  {
3560  $question =& ilObjTest::_instanciateQuestion($question_id);
3561  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3563  {
3564  $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id);
3565  }
3566  $question->delete($question_id);
3567  $this->removeAllTestEditings($question_id);
3568  $this->loadQuestions();
3569  $this->saveQuestionsToDb();
3570  }
3571 
3579  public function removeAllTestEditings($question_id = "")
3580  {
3581  global $ilDB;
3582  // remove the question from tst_solutions
3583  if ($question_id)
3584  {
3585  $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",
3586  array('integer','integer'),
3587  array($this->getTestId(), $question_id)
3588  );
3589  $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",
3590  array('integer','integer'),
3591  array($this->getTestId(), $question_id)
3592  );
3593  $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",
3594  array('integer','integer'),
3595  array($this->getTestId(), $question_id)
3596  );
3597  $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)",
3598  array('integer'),
3599  array($this->getTestId())
3600  );
3601  }
3602  else
3603  {
3604  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE tst_solutions.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3605  array('integer'),
3606  array($this->getTestId())
3607  );
3608  $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)",
3609  array('integer'),
3610  array($this->getTestId())
3611  );
3612  $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)",
3613  array('integer'),
3614  array($this->getTestId())
3615  );
3616  $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)",
3617  array('integer'),
3618  array($this->getTestId())
3619  );
3620  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3622  {
3623  $this->logAction($this->lng->txtlng("assessment", "log_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()));
3624  }
3625  }
3626  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE tst_sequence.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3627  array('integer'),
3628  array($this->getTestId())
3629  );
3630 
3631  if ($this->isRandomTest())
3632  {
3633  $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)",
3634  array('integer'),
3635  array($this->getTestId())
3636  );
3637  }
3638 
3639  // remove test_active entries, because test has changed
3640  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
3641  array('integer'),
3642  array($this->getTestId())
3643  );
3644 
3645  // remove saved user passwords
3646  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE keyword = %s",
3647  array('text'),
3648  array("tst_password_".$this->getTestId())
3649  );
3650 
3651  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3652  // remove file uploads
3653  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId()))
3654  {
3655  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId());
3656  }
3657  }
3658 
3659  function removeSelectedTestResults($active_ids)
3660  {
3661  global $ilDB;
3662 
3663  // remove the question from tst_solutions
3664  foreach ($active_ids as $active_id)
3665  {
3666  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s",
3667  array('integer'),
3668  array($active_id)
3669  );
3670  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s",
3671  array('integer'),
3672  array($active_id)
3673  );
3674  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE active_fi = %s",
3675  array('integer'),
3676  array($active_id)
3677  );
3678  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE active_fi = %s",
3679  array('integer'),
3680  array($active_id)
3681  );
3682 
3683  if ($this->isRandomTest())
3684  {
3685  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE active_fi = %s",
3686  array('integer'),
3687  array($active_id)
3688  );
3689  }
3690 
3691  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3693  {
3694  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id))));
3695  }
3696  }
3697 
3698  // remove test_active entries of selected users
3699  foreach ($active_ids as $active_id)
3700  {
3701  $usr_id = $this->_getUserIdFromActiveId($active_id);
3702 
3703  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE active_id = %s",
3704  array('integer'),
3705  array($active_id)
3706  );
3707 
3708  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
3709  array('integer'),
3710  array($active_id)
3711  );
3712 
3713  // remove saved user password
3714  if ($usr_id > 0)
3715  {
3716  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE usr_id = %s AND keyword = %s",
3717  array('integer', 'text'),
3718  array($usr_id, "tst_password_".$this->getTestId())
3719  );
3720  }
3721 
3722  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3723  // remove file uploads
3724  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"))
3725  {
3726  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id");
3727  }
3728  }
3729  }
3730 
3731  function removeTestResultsForUser($user_id)
3732  {
3733  global $ilDB;
3734 
3735  $active_id = $this->getActiveIdOfUser($user_id);
3736 
3737  // remove the question from tst_solutions
3738  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s",
3739  array('integer'),
3740  array($active_id)
3741  );
3742  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s",
3743  array('integer'),
3744  array($active_id)
3745  );
3746  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE active_fi = %s",
3747  array('integer'),
3748  array($active_id)
3749  );
3750  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE active_fi = %s",
3751  array('integer'),
3752  array($active_id)
3753  );
3754 
3755  if ($this->isRandomTest())
3756  {
3757  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE active_fi = %s",
3758  array('integer'),
3759  array($active_id)
3760  );
3761  }
3762 
3763  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3765  {
3766  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id))));
3767  }
3768 
3769  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
3770  array('integer'),
3771  array($active_id)
3772  );
3773 
3774  // remove test_active entry
3775  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE active_id = %s",
3776  array('integer'),
3777  array($active_id)
3778  );
3779 
3780  // remove saved user password
3781  if ($user_id > 0)
3782  {
3783  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE usr_id = %s AND keyword = %s",
3784  array('integer', 'text'),
3785  array($user_id, "tst_password_".$this->getTestId())
3786  );
3787  }
3788 
3789  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3790  // remove file uploads
3791  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"))
3792  {
3793  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id");
3794  }
3795  }
3796 
3804  function questionMoveUp($question_id)
3805  {
3806  global $ilDB;
3807 
3808  // Move a question up in sequence
3809  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
3810  array('integer', 'integer'),
3811  array($this->getTestId(), $question_id)
3812  );
3813  $data = $ilDB->fetchObject($result);
3814  if ($data->sequence > 1)
3815  {
3816  // OK, it's not the top question, so move it up
3817  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
3818  array('integer','integer'),
3819  array($this->getTestId(), $data->sequence - 1)
3820  );
3821  $data_previous = $ilDB->fetchObject($result);
3822  // change previous dataset
3823  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3824  array('integer','integer'),
3825  array($data->sequence, $data_previous->test_question_id)
3826  );
3827  // move actual dataset up
3828  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3829  array('integer','integer'),
3830  array($data->sequence - 1, $data->test_question_id)
3831  );
3832  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3834  {
3835  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence-1), $question_id);
3836  }
3837  }
3838  $this->loadQuestions();
3839  }
3840 
3848  function questionMoveDown($question_id)
3849  {
3850  global $ilDB;
3851 
3852  // Move a question down in sequence
3853  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
3854  array('integer','integer'),
3855  array($this->getTestId(), $question_id)
3856  );
3857  $data = $ilDB->fetchObject($result);
3858  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
3859  array('integer','integer'),
3860  array($this->getTestId(), $data->sequence + 1)
3861  );
3862  if ($result->numRows() == 1)
3863  {
3864  // OK, it's not the last question, so move it down
3865  $data_next = $ilDB->fetchObject($result);
3866  // change next dataset
3867  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3868  array('integer','integer'),
3869  array($data->sequence, $data_next->test_question_id)
3870  );
3871  // move actual dataset down
3872  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3873  array('integer','integer'),
3874  array($data->sequence + 1, $data->test_question_id)
3875  );
3876  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3878  {
3879  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence+1), $question_id);
3880  }
3881  }
3882  $this->loadQuestions();
3883  }
3884 
3892  function duplicateQuestionForTest($question_id)
3893  {
3894  global $ilUser;
3895  $question =& ilObjTest::_instanciateQuestion($question_id);
3896  $duplicate_id = $question->duplicate(true);
3897 
3898  return $duplicate_id;
3899  }
3900 
3907  function insertQuestion($question_id)
3908  {
3909  global $ilDB;
3910 
3911  $duplicate_id = $this->duplicateQuestionForTest($question_id);
3912 
3913  // get maximum sequence index in test
3914  $result = $ilDB->queryF("SELECT MAX(sequence) seq FROM tst_test_question WHERE test_fi=%s",
3915  array('integer'),
3916  array($this->getTestId())
3917  );
3918  $sequence = 1;
3919 
3920  if ($result->numRows() == 1)
3921  {
3922  $data = $ilDB->fetchObject($result);
3923  $sequence = $data->seq + 1;
3924  }
3925 
3926  $next_id = $ilDB->nextId('tst_test_question');
3927  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
3928  array('integer', 'integer','integer','integer','integer'),
3929  array($next_id, $this->getTestId(), $duplicate_id, $sequence, time())
3930  );
3931  if ($affectedRows == 1)
3932  {
3933  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3935  {
3936  $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . $sequence, $duplicate_id);
3937  }
3938  }
3939  // remove test_active entries, because test has changed
3940  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
3941  array('integer'),
3942  array($this->getTestId())
3943  );
3944  $this->loadQuestions();
3945  $this->saveCompleteStatus();
3946  }
3947 
3955  function &getQuestionTitles()
3956  {
3957  $titles = array();
3958  if (!$this->isRandomTest())
3959  {
3960  global $ilDB;
3961  $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",
3962  array('integer'),
3963  array($this->getTestId())
3964  );
3965  while ($row = $ilDB->fetchAssoc($result))
3966  {
3967  array_push($titles, $row["title"]);
3968  }
3969  }
3970  return $titles;
3971  }
3972 
3981  {
3982  $titles = array();
3983  if (!$this->isRandomTest())
3984  {
3985  global $ilDB;
3986  $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",
3987  array('integer'),
3988  array($this->getTestId())
3989  );
3990  while ($row = $ilDB->fetchAssoc($result))
3991  {
3992  $titles[$row['question_id']] = $row["title"];
3993  }
3994  }
3995  return $titles;
3996  }
3997 
3999  {
4000  $oids = array();
4001  if (!$this->isRandomTest())
4002  {
4003  global $ilDB;
4004  $query = sprintf("SELECT qpl_questions.question_id, qpl_questions.original_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",
4005  $ilDB->quote($this->getTestId() . "")
4006  );
4007  $result = $ilDB->query($query);
4008  while ($row = $ilDB->fetchAssoc($result))
4009  {
4010  $oids[$row['question_id']] = $row["original_id"];
4011  }
4012  }
4013  return $oids;
4014  }
4015 
4025  {
4026  if ($this->getTitleOutput() == 2)
4027  {
4028  return $this->lng->txt("ass_question");
4029  }
4030  else
4031  {
4032  return $title;
4033  }
4034  }
4035 
4044  function getQuestionDataset($question_id)
4045  {
4046  global $ilDB;
4047 
4048  $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",
4049  array('integer'),
4050  array($question_id)
4051  );
4052  $row = $ilDB->fetchObject($result);
4053  return $row;
4054  }
4055 
4062  function &getExistingQuestions($pass = NULL)
4063  {
4064  global $ilUser;
4065  global $ilDB;
4066 
4067  $existing_questions = array();
4068  $active_id = $this->getActiveIdOfUser($ilUser->getId());
4069  if ($this->isRandomTest())
4070  {
4071  if (is_null($pass)) $pass = 0;
4072  $result = $ilDB->queryF("SELECT qpl_questions.original_id FROM qpl_questions, tst_test_rnd_qst WHERE tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.question_fi = qpl_questions.question_id AND tst_test_rnd_qst.pass = %s",
4073  array('integer','integer'),
4074  array($active_id, $pass)
4075  );
4076  }
4077  else
4078  {
4079  $result = $ilDB->queryF("SELECT qpl_questions.original_id FROM qpl_questions, tst_test_question WHERE tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id",
4080  array('integer'),
4081  array($this->getTestId())
4082  );
4083  }
4084  while ($data = $ilDB->fetchObject($result))
4085  {
4086  array_push($existing_questions, $data->original_id);
4087  }
4088  return $existing_questions;
4089  }
4090 
4098  function getQuestionType($question_id)
4099  {
4100  global $ilDB;
4101 
4102  if ($question_id < 1) return -1;
4103  $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",
4104  array('integer'),
4105  array($question_id)
4106  );
4107  if ($result->numRows() == 1)
4108  {
4109  $data = $ilDB->fetchObject($result);
4110  return $data->type_tag;
4111  }
4112  else
4113  {
4114  return "";
4115  }
4116  }
4117 
4124  function startWorkingTime($active_id, $question_id, $pass)
4125  {
4126  global $ilDB;
4127 
4128  $next_id = $ilDB->nextId('tst_times');
4129  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_times (times_id, active_fi, started, finished, pass, question_fi, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s)",
4130  array('integer', 'integer', 'timestamp', 'timestamp', 'integer', 'integer', 'integer'),
4131  array($next_id, $active_id, strftime("%Y-%m-%d %H:%M:%S"), strftime("%Y-%m-%d %H:%M:%S"), $pass, $question_id, time())
4132  );
4133  return $next_id;
4134  }
4135 
4142  function updateWorkingTime($times_id)
4143  {
4144  global $ilDB;
4145 
4146  $affectedRows = $ilDB->manipulateF("UPDATE tst_times SET finished = %s, tstamp = %s WHERE times_id = %s",
4147  array('timestamp', 'integer', 'integer'),
4148  array(strftime("%Y-%m-%d %H:%M:%S"), time(), $times_id)
4149  );
4150  }
4151 
4158  function &getWorkedQuestions($active_id, $pass = NULL)
4159  {
4160  global $ilUser;
4161  global $ilDB;
4162 
4163  if (is_null($pass))
4164  {
4165  $result = $ilDB->queryF("SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
4166  array('integer','integer'),
4167  array($active_id, 0)
4168  );
4169  }
4170  else
4171  {
4172  $result = $ilDB->queryF("SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
4173  array('integer','integer'),
4174  array($active_id, $pass)
4175  );
4176  }
4177  $result_array = array();
4178  while ($row = $ilDB->fetchAssoc($result))
4179  {
4180  array_push($result_array, $row["question_fi"]);
4181  }
4182  return $result_array;
4183  }
4184 
4193  function isTestFinishedToViewResults($active_id, $currentpass)
4194  {
4195  $num = $this->getPassFinishDate($active_id, $currentpass);
4196  return ((($currentpass > 0) && ($num == 0)) || $this->isTestFinished($active_id)) ? true : false;
4197  }
4198 
4205  function &getAllQuestions($pass = NULL)
4206  {
4207  global $ilUser;
4208  global $ilDB;
4209 
4210  $result_array = array();
4211  if ($this->isRandomTest())
4212  {
4213  $active_id = $this->getActiveIdOfUser($ilUser->getId());
4214  $this->loadQuestions($active_id, $pass);
4215  if (count($this->questions) == 0) return $result_array;
4216  if (is_null($pass))
4217  {
4218  $pass = $this->_getPass($active_id);
4219  }
4220  $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'),
4221  array('integer','integer'),
4222  array($active_id, $pass)
4223  );
4224  }
4225  else
4226  {
4227  if (count($this->questions) == 0) return $result_array;
4228  $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'));
4229  }
4230  while ($row = $ilDB->fetchAssoc($result))
4231  {
4232  $result_array[$row["question_id"]] = $row;
4233  }
4234  return $result_array;
4235  }
4236 
4245  function getActiveIdOfUser($user_id = "", $anonymous_id = "")
4246  {
4247  global $ilDB;
4248  global $ilUser;
4249 
4250  if (!$user_id) $user_id = $ilUser->getId();
4251  if (($_SESSION["AccountId"] == ANONYMOUS_USER_ID) && (strlen($_SESSION["tst_access_code"][$this->getTestId()])))
4252  {
4253  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
4254  array('integer','integer','text'),
4255  array($user_id, $this->test_id, $_SESSION["tst_access_code"][$this->getTestId()])
4256  );
4257  }
4258  else if (strlen($anonymous_id))
4259  {
4260  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
4261  array('integer','integer','text'),
4262  array($user_id, $this->test_id, $anonymous_id)
4263  );
4264  }
4265  else
4266  {
4267  if ($_SESSION["AccountId"] == ANONYMOUS_USER_ID)
4268  {
4269  return NULL;
4270  }
4271  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
4272  array('integer','integer'),
4273  array($user_id, $this->test_id)
4274  );
4275  }
4276  if ($result->numRows())
4277  {
4278  $row = $ilDB->fetchAssoc($result);
4279  return $row["active_id"];
4280  }
4281  else
4282  {
4283  return 0;
4284  }
4285  }
4286 
4295  function _getActiveIdOfUser($user_id = "", $test_id = "")
4296  {
4297  global $ilDB;
4298  global $ilUser;
4299 
4300  if (!$user_id) {
4301  $user_id = $ilUser->id;
4302  }
4303  if (!$test_id)
4304  {
4305  return "";
4306  }
4307  $result = $ilDB->queryF("SELECT tst_active.active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
4308  array('integer', 'integer'),
4309  array($user_id, $test_id)
4310  );
4311  if ($result->numRows())
4312  {
4313  $row = $ilDB->fetchAssoc($result);
4314  return $row["active_id"];
4315  }
4316  else
4317  {
4318  return "";
4319  }
4320  }
4321 
4328  function pcArrayShuffle($array)
4329  {
4330  $keys = array_keys($array);
4331  shuffle($keys);
4332  $result = array();
4333  foreach ($keys as $key)
4334  {
4335  $result[$key] = $array[$key];
4336  }
4337  return $result;
4338  }
4339 
4350  function &getTestResult($active_id, $pass = NULL, $ordered_sequence = FALSE)
4351  {
4352  global $ilDB;
4353 
4354  $results = $this->getResultsForActiveId($active_id);
4355  if (is_null($pass))
4356  {
4357  $pass = $results['pass'];
4358  }
4359  include_once "./Modules/Test/classes/class.ilTestSequence.php";
4360  $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
4361  $sequence = array();
4362  if ($ordered_sequence)
4363  {
4364  $sequence = $testSequence->getOrderedSequenceQuestions();
4365  }
4366  else
4367  {
4368  $sequence = $testSequence->getUserSequenceQuestions();
4369  }
4370  $arrResults = array();
4371  $solutionresult = $ilDB->queryF("SELECT tst_test_result.question_fi, tst_test_result.points reached, tst_solutions.solution_id workedthru FROM tst_test_result LEFT JOIN tst_solutions ON tst_solutions.active_fi = tst_test_result.active_fi AND tst_solutions.question_fi = tst_test_result.question_fi WHERE tst_test_result.active_fi = %s AND tst_test_result.pass = %s",
4372  array('integer', 'integer'),
4373  array($active_id, $pass)
4374  );
4375  while ($row = $ilDB->fetchAssoc($solutionresult))
4376  {
4377  $arrResults[$row['question_fi']] = $row;
4378  }
4379 
4380  require_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
4381  $result = $ilDB->query("SELECT qpl_questions.*, qpl_qst_type.type_tag, qpl_sol_sug.question_fi has_sug_sol FROM qpl_qst_type, qpl_questions LEFT JOIN qpl_sol_sug ON qpl_sol_sug.question_fi = qpl_questions.question_id WHERE qpl_qst_type.question_type_id = qpl_questions.question_type_fi AND " . $ilDB->in('qpl_questions.question_id', $sequence, false, 'integer'));
4382  $found = array();
4383  $unordered = array();
4384  $key = 1;
4385  $pass_max = 0;
4386  $pass_reached = 0;
4387  while ($row = $ilDB->fetchAssoc($result))
4388  {
4389  $percentvalue = ($row['points']) ? $arrResults[$row['question_id']]['reached'] / $row['points'] : 0;
4390  if ($percentvalue < 0) $percentvalue = 0.0;
4391  $data = array(
4392  "nr" => "$key",
4393  "title" => ilUtil::prepareFormOutput($row['title']),
4394  "max" => round($row['points'], 2),
4395  "reached" => round($arrResults[$row['question_id']]['reached'],2),
4396  "percent" => sprintf("%2.2f ", ($percentvalue) * 100) . "%",
4397  "solution" => ($row['has_sug_sol']) ? assQuestion::_getSuggestedSolutionOutput($row['question_id']) : '',
4398  "type" => $row["type_tag"],
4399  "qid" => $row['question_id'],
4400  "original_id" => $row["original_id"],
4401  "workedthrough" => ($arrResults[$row['question_id']]['workedthru']) ? 1 : 0
4402  );
4403  $pass_max += round($row['points'], 2);
4404  $pass_reached += round($arrResults[$row['question_id']]['reached'], 2);
4405  $unordered[$row['question_id']] = $data;
4406  $key++;
4407  }
4408  $key = 1;
4409  foreach ($sequence as $qid)
4410  {
4411  $unordered[$qid]['nr'] = $key;
4412  array_push($found, $unordered[$qid]);
4413  $key++;
4414  }
4415  $unordered = null;
4416  if ($this->getScoreCutting() == 1)
4417  {
4418  if ($results['reached_points'] < 0)
4419  {
4420  $results['reached_points'] = 0;
4421  }
4422  if ($pass_reached < 0) $pass_reached = 0;
4423  }
4424  $found['pass']['total_max_points'] = $pass_max;
4425  $found['pass']['total_reached_points'] = $pass_reached;
4426  $found['pass']['percent'] = ($pass_max > 0) ? $pass_reached / $pass_max : 0;
4427  $found["test"]["total_max_points"] = $results['max_points'];
4428  $found["test"]["total_reached_points"] = $results['reached_points'];
4429  $found["test"]["result_pass"] = $results['pass'];
4430  if ((!$total_reached_points) or (!$total_max_points))
4431  {
4432  $percentage = 0.0;
4433  }
4434  else
4435  {
4436  $percentage = ($total_reached_points / $total_max_points) * 100.0;
4437  if ($percentage < 0) $percentage = 0.0;
4438  }
4439  $found["test"]["passed"] = $results['passed'];
4440  return $found;
4441  }
4442 
4449  function evalTotalPersons()
4450  {
4451  global $ilDB;
4452 
4453  $result = $ilDB->queryF("SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s",
4454  array('integer'),
4455  array($this->getTestId())
4456  );
4457  $row = $ilDB->fetchAssoc($result);
4458  return $row["total"];
4459  }
4460 
4467  function getCompleteWorkingTime($user_id)
4468  {
4469  global $ilDB;
4470 
4471  $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",
4472  array('integer','integer'),
4473  array($this->getTestId(), $user_id)
4474  );
4475  $time = 0;
4476  while ($row = $ilDB->fetchAssoc($result))
4477  {
4478  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4479  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4480  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4481  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4482  $time += ($epoch_2 - $epoch_1);
4483  }
4484  return $time;
4485  }
4486 
4494  {
4495  return $this->_getCompleteWorkingTimeOfParticipants($this->getTestId());
4496  }
4497 
4506  {
4507  global $ilDB;
4508 
4509  $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",
4510  array('integer'),
4511  array($test_id)
4512  );
4513  $time = 0;
4514  $times = array();
4515  while ($row = $ilDB->fetchAssoc($result))
4516  {
4517  if (!array_key_exists($row["active_fi"], $times))
4518  {
4519  $times[$row["active_fi"]] = 0;
4520  }
4521  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4522  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4523  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4524  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4525  $times[$row["active_fi"]] += ($epoch_2 - $epoch_1);
4526  }
4527  return $times;
4528  }
4529 
4537  {
4538  global $ilDB;
4539 
4540  $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",
4541  array('integer','integer'),
4542  array($this->getTestId(), $active_id)
4543  );
4544  $time = 0;
4545  while ($row = $ilDB->fetchAssoc($result))
4546  {
4547  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4548  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4549  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4550  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4551  $time += ($epoch_2 - $epoch_1);
4552  }
4553  return $time;
4554  }
4555 
4563  {
4564  global $ilDB;
4565 
4566  $result = $ilDB->queryF("SELECT * FROM tst_times WHERE active_fi = %s AND pass = %s ORDER BY started",
4567  array('integer','integer'),
4568  array($active_id, $pass)
4569  );
4570  $time = 0;
4571  while ($row = $ilDB->fetchAssoc($result))
4572  {
4573  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4574  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4575  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4576  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4577  $time += ($epoch_2 - $epoch_1);
4578  }
4579  return $time;
4580  }
4581 
4589  function getVisitTimeOfParticipant($active_id)
4590  {
4591  return ilObjTest::_getVisitTimeOfParticipant($this->getTestId(), $active_id);
4592  }
4593 
4602  function _getVisitTimeOfParticipant($test_id, $active_id)
4603  {
4604  global $ilDB;
4605 
4606  $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",
4607  array('integer','integer'),
4608  array($test_id, $active_id)
4609  );
4610  $firstvisit = 0;
4611  $lastvisit = 0;
4612  while ($row = $ilDB->fetchAssoc($result))
4613  {
4614  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4615  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4616  if ($firstvisit == 0 || $epoch_1 < $firstvisit) $firstvisit = $epoch_1;
4617  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4618  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4619  if ($epoch_2 > $lastvisit) $lastvisit = $epoch_2;
4620  }
4621  return array("firstvisit" => $firstvisit, "lastvisit" => $lastvisit);
4622  }
4623 
4630  function &evalStatistical($active_id)
4631  {
4632  global $ilDB;
4633 // global $ilBench;
4634  $pass = ilObjTest::_getResultPass($active_id);
4635  $test_result =& $this->getTestResult($active_id, $pass);
4636  $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",
4637  array('integer'),
4638  array($active_id)
4639  );
4640  $times = array();
4641  $first_visit = 0;
4642  $last_visit = 0;
4643  while ($row = $ilDB->fetchObject($result))
4644  {
4645  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
4646  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4647  if (!$first_visit) {
4648  $first_visit = $epoch_1;
4649  }
4650  if ($epoch_1 < $first_visit) {
4651  $first_visit = $epoch_1;
4652  }
4653  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
4654  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4655  if (!$last_visit) {
4656  $last_visit = $epoch_2;
4657  }
4658  if ($epoch_2 > $last_visit) {
4659  $last_visit = $epoch_2;
4660  }
4661  $times[$row->active_fi] += ($epoch_2 - $epoch_1);
4662  }
4663  $max_time = 0;
4664  foreach ($times as $key => $value) {
4665  $max_time += $value;
4666  }
4667  if ((!$test_result["test"]["total_reached_points"]) or (!$test_result["test"]["total_max_points"]))
4668  {
4669  $percentage = 0.0;
4670  }
4671  else
4672  {
4673  $percentage = ($test_result["test"]["total_reached_points"] / $test_result["test"]["total_max_points"]) * 100.0;
4674  if ($percentage < 0) $percentage = 0.0;
4675  }
4676  $mark_obj = $this->mark_schema->getMatchingMark($percentage);
4677  $first_date = getdate($first_visit);
4678  $last_date = getdate($last_visit);
4679  $qworkedthrough = 0;
4680  foreach ($test_result as $key => $value)
4681  {
4682  if (preg_match("/\d+/", $key))
4683  {
4684  $qworkedthrough += $value["workedthrough"];
4685  }
4686  }
4687  if (!$qworkedthrough)
4688  {
4689  $atimeofwork = 0;
4690  }
4691  else
4692  {
4693  $atimeofwork = $max_time / $qworkedthrough;
4694  }
4695  $result_mark = "";
4696  $passed = "";
4697  if ($mark_obj)
4698  {
4699  $result_mark = $mark_obj->getShortName();
4700  if ($mark_obj->getPassed())
4701  {
4702  $passed = 1;
4703  }
4704  else
4705  {
4706  $passed = 0;
4707  }
4708  }
4709  $percent_worked_through = 0;
4710  if (count($this->questions))
4711  {
4712  $percent_worked_through = $qworkedthrough / count($this->questions);
4713  }
4714  $result_array = array(
4715  "qworkedthrough" => $qworkedthrough,
4716  "qmax" => count($this->questions),
4717  "pworkedthrough" => $percent_worked_through,
4718  "timeofwork" => $max_time,
4719  "atimeofwork" => $atimeofwork,
4720  "firstvisit" => $first_date,
4721  "lastvisit" => $last_date,
4722  "resultspoints" => $test_result["test"]["total_reached_points"],
4723  "maxpoints" => $test_result["test"]["total_max_points"],
4724  "resultsmarks" => $result_mark,
4725  "passed" => $passed,
4726  "distancemedian" => "0"
4727  );
4728  foreach ($test_result as $key => $value)
4729  {
4730  if (preg_match("/\d+/", $key))
4731  {
4732  $result_array[$key] = $value;
4733  }
4734  }
4735  return $result_array;
4736  }
4737 
4746  {
4747  $totalpoints_array = array();
4748  $all_users =& $this->evalTotalParticipantsArray();
4749  foreach ($all_users as $active_id => $user_name)
4750  {
4751  $test_result =& $this->getTestResult($active_id);
4752  $reached = $test_result["test"]["total_reached_points"];
4753  $total = $test_result["test"]["total_max_points"];
4754  $percentage = $total != 0 ? $reached/$total : 0;
4755  $mark = $this->mark_schema->getMatchingMark($percentage*100.0);
4756  if ($mark)
4757  {
4758  if ($mark->getPassed())
4759  {
4760  array_push($totalpoints_array, $test_result["test"]["total_reached_points"]);
4761  }
4762  }
4763  }
4764  return $totalpoints_array;
4765  }
4766 
4773  function &getParticipants()
4774  {
4775  global $ilDB;
4776  $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",
4777  array('integer'),
4778  array($this->getTestId())
4779  );
4780  $persons_array = array();
4781  while ($row = $ilDB->fetchAssoc($result))
4782  {
4783  $name = $this->lng->txt("unknown");
4784  $fullname = $this->lng->txt("unknown");
4785  $login = "";
4786  if (!$this->getAnonymity())
4787  {
4788  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4789  {
4790  $name = $this->lng->txt("deleted_user");
4791  $fullname = $this->lng->txt("deleted_user");
4792  $login = $this->lng->txt("unknown");
4793  }
4794  else
4795  {
4796  $login = $row["login"];
4797  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4798  {
4799  $name = $this->lng->txt("unknown");
4800  $fullname = $this->lng->txt("unknown");
4801  }
4802  else
4803  {
4804  $name = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]);
4805  $fullname = trim($row["title"] . " " . $row["firstname"] . " " . $row["lastname"]);
4806  }
4807  }
4808  }
4809  $persons_array[$row["active_id"]] = array(
4810  "name" => $name,
4811  "fullname" => $fullname,
4812  "login" => $login
4813  );
4814  }
4815  return $persons_array;
4816  }
4817 
4824  function &evalTotalPersonsArray($name_sort_order = "asc")
4825  {
4826  global $ilDB;
4827  $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),
4828  array('integer'),
4829  array($this->getTestId())
4830  );
4831  $persons_array = array();
4832  while ($row = $ilDB->fetchAssoc($result))
4833  {
4834  if ($this->getAnonymity())
4835  {
4836  $persons_array[$row["active_id"]] = $this->lng->txt("unknown");
4837  }
4838  else
4839  {
4840  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4841  {
4842  $persons_array[$row["active_id"]] = $this->lng->txt("deleted_user");
4843  }
4844  else
4845  {
4846  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4847  {
4848  $persons_array[$row["active_id"]] = $row["lastname"];
4849  }
4850  else
4851  {
4852  $persons_array[$row["active_id"]] = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]);
4853  }
4854  }
4855  }
4856  }
4857  return $persons_array;
4858  }
4859 
4866  function &evalTotalParticipantsArray($name_sort_order = "asc")
4867  {
4868  global $ilDB;
4869  $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),
4870  array('integer'),
4871  array($this->getTestId())
4872  );
4873  $persons_array = array();
4874  while ($row = $ilDB->fetchAssoc($result))
4875  {
4876  if ($this->getAnonymity())
4877  {
4878  $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("unknown"));
4879  }
4880  else
4881  {
4882  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4883  {
4884  $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("deleted_user"));
4885  }
4886  else
4887  {
4888  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4889  {
4890  $persons_array[$row["active_id"]] = array("name" => $row["lastname"]);
4891  }
4892  else
4893  {
4894  $persons_array[$row["active_id"]] = array("name" => trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]), "login" => $row["login"]);
4895  }
4896  }
4897  }
4898  }
4899  return $persons_array;
4900  }
4901 
4909  {
4910  global $ilDB;
4911 
4912  $result = $ilDB->queryF("SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s AND submitted = %s",
4913  array('integer', 'integer'),
4914  array($this->getTestId(), 1)
4915  );
4916  $row = $ilDB->fetchAssoc($result);
4917  return $row["total"];
4918  }
4919 
4926  function &getQuestionsOfTest($active_id)
4927  {
4928  global $ilDB;
4929  if ($this->isRandomTest())
4930  {
4931  $ilDB->setLimit($this->getQuestionCount(), 0);
4932  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " .
4933  "tst_test_rnd_qst.pass, qpl_questions.points " .
4934  "FROM tst_test_rnd_qst, qpl_questions " .
4935  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4936  "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence",
4937  array('integer'),
4938  array($active_id)
4939  );
4940  }
4941  else
4942  {
4943  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4944  "qpl_questions.points " .
4945  "FROM tst_test_question, tst_active, qpl_questions " .
4946  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4947  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi",
4948  array('integer'),
4949  array($active_id)
4950  );
4951  }
4952  $qtest = array();
4953  if ($result->numRows())
4954  {
4955  while ($row = $ilDB->fetchAssoc($result))
4956  {
4957  array_push($qtest, $row);
4958  }
4959  }
4960  return $qtest;
4961  }
4962 
4969  function &getQuestionsOfPass($active_id, $pass)
4970  {
4971  global $ilDB;
4972  if ($this->isRandomTest())
4973  {
4974  $ilDB->setLimit($this->getQuestionCount(), 0);
4975  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " .
4976  "qpl_questions.points " .
4977  "FROM tst_test_rnd_qst, qpl_questions " .
4978  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4979  "AND tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.pass = %s " .
4980  "ORDER BY tst_test_rnd_qst.sequence",
4981  array('integer', 'integer'),
4982  array($active_id, $pass)
4983  );
4984  }
4985  else
4986  {
4987  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4988  "qpl_questions.points " .
4989  "FROM tst_test_question, tst_active, qpl_questions " .
4990  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4991  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi",
4992  array('integer'),
4993  array($active_id)
4994  );
4995  }
4996  $qpass = array();
4997  if ($result->numRows())
4998  {
4999  while ($row = $ilDB->fetchAssoc($result))
5000  {
5001  array_push($qpass, $row);
5002  }
5003  }
5004  return $qpass;
5005  }
5006 
5008  {
5009  global $ilDB;
5010  include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php";
5011  include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php";
5012  include_once "./Modules/Test/classes/class.ilTestEvaluationData.php";
5013  $data = new ilTestEvaluationData($this);
5014  $result = $ilDB->queryF("SELECT tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
5015  "qpl_questions.points maxpoints " .
5016  "FROM tst_test_result, qpl_questions, tst_active " .
5017  "WHERE tst_active.active_id = tst_test_result.active_fi " .
5018  "AND qpl_questions.question_id = tst_test_result.question_fi " .
5019  "AND tst_active.test_fi = %s " .
5020  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
5021  array('integer'),
5022  array($this->getTestId())
5023  );
5024  $pass = NULL;
5025  $checked = array();
5026  $datasets = 0;
5027  while ($row = $ilDB->fetchAssoc($result))
5028  {
5029  $data->getParticipant($row["active_fi"])->getPass($row["pass"])->addAnsweredQuestion($row["question_fi"], $row["maxpoints"], $row["points"]);
5030  }
5031 
5032  foreach (array_keys($data->getParticipants()) as $active_id)
5033  {
5034  if ($this->isRandomTest())
5035  {
5036  for ($testpass = 0; $testpass <= $data->getParticipant($active_id)->getLastPass(); $testpass++)
5037  {
5038  $ilDB->setLimit($this->getQuestionCount(), 0);
5039  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, qpl_questions.original_id, " .
5040  "tst_test_rnd_qst.pass, qpl_questions.points, qpl_questions.title " .
5041  "FROM tst_test_rnd_qst, qpl_questions " .
5042  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
5043  "AND tst_test_rnd_qst.pass = %s " .
5044  "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence",
5045  array('integer','integer'),
5046  array($testpass, $active_id)
5047  );
5048  if ($result->numRows())
5049  {
5050  while ($row = $ilDB->fetchAssoc($result))
5051  {
5052  $tpass = array_key_exists("pass", $row) ? $row["pass"] : 0;
5053  $data->getParticipant($active_id)->addQuestion($row["original_id"] ? $row["original_id"] : $row["question_fi"], $row["question_fi"], $row["points"], $row["sequence"], $tpass);
5054  $data->addQuestionTitle($row["question_fi"], $row["title"]);
5055  }
5056  }
5057  }
5058  }
5059  else
5060  {
5061  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
5062  "qpl_questions.points, qpl_questions.title, qpl_questions.original_id " .
5063  "FROM tst_test_question, tst_active, qpl_questions " .
5064  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
5065  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi ORDER BY tst_test_question.sequence",
5066  array('integer'),
5067  array($active_id)
5068  );
5069  if ($result->numRows())
5070  {
5071  $questionsbysequence = array();
5072  while ($row = $ilDB->fetchAssoc($result))
5073  {
5074  $questionsbysequence[$row["sequence"]] = $row;
5075  }
5076  $seqresult = $ilDB->queryF("SELECT * FROM tst_sequence WHERE active_fi = %s",
5077  array('integer'),
5078  array($active_id)
5079  );
5080  while ($seqrow = $ilDB->fetchAssoc($seqresult))
5081  {
5082  $questionsequence = unserialize($seqrow["sequence"]);
5083  foreach ($questionsequence as $sidx => $seq)
5084  {
5085  $qsid = $questionsbysequence[$seq]["original_id"] ? $questionsbysequence[$seq]["original_id"] : $questionsbysequence[$seq]["question_fi"];
5086  $data->getParticipant($active_id)->addQuestion($qsid, $questionsbysequence[$seq]["question_fi"], $questionsbysequence[$seq]["points"], $sidx + 1, $seqrow["pass"]);
5087  $data->addQuestionTitle($questionsbysequence[$seq]["question_fi"], $questionsbysequence[$seq]["title"]);
5088  }
5089  }
5090  }
5091  }
5092  }
5093 
5094  if ($this->ects_output)
5095  {
5096  $passed_array =& $this->getTotalPointsPassedArray();
5097  }
5098  foreach (array_keys($data->getParticipants()) as $active_id)
5099  {
5100  $percentage = $data->getParticipant($active_id)->getReachedPointsInPercent();
5101  $mark = $this->mark_schema->getMatchingMark($percentage);
5102  if (is_object($mark))
5103  {
5104  $data->getParticipant($active_id)->setMark($mark->getShortName());
5105  $data->getParticipant($active_id)->setMarkOfficial($mark->getOfficialName());
5106  $data->getParticipant($active_id)->setPassed($mark->getPassed());
5107  }
5108  if ($this->ects_output)
5109  {
5110  $ects_mark = $this->getECTSGrade($passed_array, $data->getParticipant($active_id)->getReached(), $data->getParticipant($active_id)->getMaxPoints());
5111  $data->getParticipant($active_id)->setECTSMark($ects_mark);
5112  }
5113  $visitingTime =& $this->getVisitTimeOfParticipant($active_id);
5114  $data->getParticipant($active_id)->setFirstVisit($visitingTime["firstvisit"]);
5115  $data->getParticipant($active_id)->setLastVisit($visitingTime["lastvisit"]);
5116  }
5117  return $data;
5118  }
5119 
5121  {
5122  global $ilDB;
5123  $random = ilObjTest::_lookupRandomTestFromActiveId($active_id);
5124  if ($random)
5125  {
5126  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.pass, COUNT(tst_test_rnd_qst.question_fi) qcount, " .
5127  "SUM(qpl_questions.points) qsum FROM tst_test_rnd_qst, qpl_questions " .
5128  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id AND " .
5129  "tst_test_rnd_qst.active_fi = %s and pass = %s GROUP BY tst_test_rnd_qst.active_fi, " .
5130  "tst_test_rnd_qst.pass",
5131  array('integer', 'integer'),
5132  array($active_id, $pass)
5133  );
5134  }
5135  else
5136  {
5137  $result = $ilDB->queryF("SELECT COUNT(tst_test_question.question_fi) qcount, " .
5138  "SUM(qpl_questions.points) qsum FROM tst_test_question, qpl_questions, tst_active " .
5139  "WHERE tst_test_question.question_fi = qpl_questions.question_id AND tst_test_question.test_fi = tst_active.test_fi AND " .
5140  "tst_active.active_id = %s GROUP BY tst_test_question.test_fi",
5141  array('integer'),
5142  array($active_id)
5143  );
5144  }
5145  if ($result->numRows())
5146  {
5147  $row = $ilDB->fetchAssoc($result);
5148  return array("count" => $row["qcount"], "points" => $row["qsum"]);
5149  }
5150  else
5151  {
5152  return array("count" => 0, "points" => 0);
5153  }
5154  }
5155 
5156  function &getCompleteEvaluationData($withStatistics = TRUE, $filterby = "", $filtertext = "")
5157  {
5158  include_once "./Modules/Test/classes/class.ilTestEvaluationData.php";
5159  include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php";
5160  include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php";
5162  if ($withStatistics)
5163  {
5164  $data->calculateStatistics();
5165  }
5166  $data->setFilter($filterby, $filtertext);
5167  return $data;
5168  }
5169 
5177  {
5178  return $this->_evalResultsOverview($this->getTestId());
5179  }
5180 
5188  {
5189  global $ilDB;
5190 
5191  $result = $ilDB->queryF("SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " .
5192  "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
5193  "qpl_questions.points maxpoints " .
5194  "FROM tst_test_result, qpl_questions, tst_active " .
5195  "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " .
5196  "WHERE tst_active.active_id = tst_test_result.active_fi " .
5197  "AND qpl_questions.question_id = tst_test_result.question_fi " .
5198  "AND tst_active.test_fi = %s " .
5199  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
5200  array('integer'),
5201  array($test_id)
5202  );
5203  $overview = array();
5204  while ($row = $ilDB->fetchAssoc($result))
5205  {
5206  if (!array_key_exists($row["active_fi"], $overview))
5207  {
5208  $overview[$row["active_fi"]] = array();
5209  $overview[$row["active_fi"]]["firstname"] = $row["firstname"];
5210  $overview[$row["active_fi"]]["lastname"] = $row["lastname"];
5211  $overview[$row["active_fi"]]["title"] = $row["title"];
5212  $overview[$row["active_fi"]]["login"] = $row["login"];
5213  $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"];
5214  $overview[$row["active_fi"]]["started"] = $row["started"];
5215  $overview[$row["active_fi"]]["finished"] = $row["finished"];
5216  }
5217  if (!array_key_exists($row["pass"], $overview[$row["active_fi"]]))
5218  {
5219  $overview[$row["active_fi"]][$row["pass"]] = array();
5220  $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0;
5221  $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"];
5222  }
5223  array_push($overview[$row["active_fi"]][$row["pass"]], $row);
5224  $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"];
5225  }
5226  return $overview;
5227  }
5228 
5236  function &evalResultsOverviewOfParticipant($active_id)
5237  {
5238  global $ilDB;
5239 
5240  $result = $ilDB->queryF("SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " .
5241  "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
5242  "qpl_questions.points maxpoints " .
5243  "FROM tst_test_result, qpl_questions, tst_active " .
5244  "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " .
5245  "WHERE tst_active.active_id = tst_test_result.active_fi " .
5246  "AND qpl_questions.question_id = tst_test_result.question_fi " .
5247  "AND tst_active.test_fi = %s AND tst_active.active_id = %s" .
5248  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
5249  array('integer', 'integer'),
5250  array($this->getTestId(), $active_id)
5251  );
5252  $overview = array();
5253  while ($row = $ilDB->fetchAssoc($result))
5254  {
5255  if (!array_key_exists($row["active_fi"], $overview))
5256  {
5257  $overview[$row["active_fi"]] = array();
5258  $overview[$row["active_fi"]]["firstname"] = $row["firstname"];
5259  $overview[$row["active_fi"]]["lastname"] = $row["lastname"];
5260  $overview[$row["active_fi"]]["title"] = $row["title"];
5261  $overview[$row["active_fi"]]["login"] = $row["login"];
5262  $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"];
5263  $overview[$row["active_fi"]]["started"] = $row["started"];
5264  $overview[$row["active_fi"]]["finished"] = $row["finished"];
5265  }
5266  if (!array_key_exists($row["pass"], $overview[$row["active_fi"]]))
5267  {
5268  $overview[$row["active_fi"]][$row["pass"]] = array();
5269  $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0;
5270  $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"];
5271  }
5272  array_push($overview[$row["active_fi"]][$row["pass"]], $row);
5273  $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"];
5274  }
5275  return $overview;
5276  }
5277 
5289  function buildName($user_id, $firstname, $lastname, $title)
5290  {
5291  global $lng;
5292 
5293  $name = "";
5294  if (strlen($firstname.$lastname.$title) == 0)
5295  {
5296  $name = $lng->txt("deleted_user");
5297  }
5298  else
5299  {
5300  if ($user_id == ANONYMOUS_USER_ID)
5301  {
5302  $name = $lastname;
5303  }
5304  else
5305  {
5306  $name = trim($lastname . ", " . $firstname . " " . $title);
5307  }
5308  if ($this->getAnonymity())
5309  {
5310  $name = $lng->txt("anonymous");
5311  }
5312  }
5313  return $name;
5314  }
5315 
5328  function _buildName($is_anonymous, $user_id, $firstname, $lastname, $title)
5329  {
5330  global $lng;
5331  $name = "";
5332  if (strlen($firstname.$lastname.$title) == 0)
5333  {
5334  $name = $lng->txt("deleted_user");
5335  }
5336  else
5337  {
5338  if ($user_id == ANONYMOUS_USER_ID)
5339  {
5340  $name = $lastname;
5341  }
5342  else
5343  {
5344  $name = trim($lastname . ", " . $firstname . " " . $title);
5345  }
5346  if ($is_anonymous)
5347  {
5348  $name = $lng->txt("anonymous");
5349  }
5350  }
5351  return $name;
5352  }
5353 
5361  {
5362  global $ilDB;
5363 
5364  $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",
5365  array('integer'),
5366  array($this->getTestId())
5367  );
5368  $times = array();
5369  while ($row = $ilDB->fetchObject($result))
5370  {
5371  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
5372  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5373  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
5374  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5375  $times[$row->active_fi] += ($epoch_2 - $epoch_1);
5376  }
5377  $max_time = 0;
5378  $counter = 0;
5379  foreach ($times as $key => $value)
5380  {
5381  $max_time += $value;
5382  $counter++;
5383  }
5384  if ($counter)
5385  {
5386  $average_time = round($max_time / $counter);
5387  }
5388  else
5389  {
5390  $average_time = 0;
5391  }
5392  return $average_time;
5393  }
5394 
5401  function &getAvailableQuestionpools($use_object_id = false, $equal_points = false, $could_be_offline = false, $show_path = FALSE, $with_questioncount = FALSE, $permission = "read")
5402  {
5403  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5404  return ilObjQuestionPool::_getAvailableQuestionpools($use_object_id, $equal_points, $could_be_offline, $show_path, $with_questioncount, $permission);
5405  }
5406 
5414  {
5415  $time_in_seconds = 0;
5416  foreach ($this->questions as $question_id)
5417  {
5418  $question =& ilObjTest::_instanciateQuestion($question_id);
5419  $est_time = $question->getEstimatedWorkingTime();
5420  $time_in_seconds += $est_time["h"] * 3600 + $est_time["m"] * 60 + $est_time["s"];
5421  }
5422  $hours = (int)($time_in_seconds / 3600) ;
5423  $time_in_seconds = $time_in_seconds - ($hours * 3600);
5424  $minutes = (int)($time_in_seconds / 60);
5425  $time_in_seconds = $time_in_seconds - ($minutes * 60);
5426  $result = array("hh" => $hours, "mm" => $minutes, "ss" => $time_in_seconds);
5427  return $result;
5428  }
5429 
5438  public function generateRandomPass($nr, $qpls, $pass = NULL)
5439  {
5440  global $ilDB;
5441  $qplids = array();
5442  foreach ($qpls as $arr) array_push($qplids, $arr['qpl']);
5443  $result = $ilDB->queryF('SELECT * FROM tst_rnd_cpy WHERE tst_fi = %s AND ' . $ilDB->in('qpl_fi', $qplids, false, 'integer'),
5444  array('integer'),
5445  array($this->getTestId())
5446  );
5447  if ($result->numRows())
5448  {
5449  $ids = array();
5450  while ($row = $ilDB->fetchAssoc($result)) array_push($ids, $row['qst_fi']);
5451  $nr = ($nr > count($ids)) ? count($ids) : $nr;
5452  if ($nr == 0) return array();
5453  $rand_keys = array_rand($ids, $nr);
5454  $selection = array();
5455  if (is_array($rand_keys))
5456  {
5457  foreach ($rand_keys as $key)
5458  {
5459  $selection[$ids[$key]] = $ids[$key];
5460  }
5461  }
5462  else
5463  {
5464  $selection[$ids[$rand_keys]] = $ids[$rand_keys];
5465  }
5466  return $selection;
5467  }
5468  else
5469  {
5470  // old style random questions
5471  return $this->randomSelectQuestions($nr, 0, 1, $qplids, $pass);
5472  }
5473  }
5474 
5485  function randomSelectQuestions($nr_of_questions, $questionpool, $use_obj_id = 0, $qpls = "", $pass = NULL)
5486  {
5487  global $rbacsystem;
5488  global $ilDB;
5489 
5490  // retrieve object id instead of ref id if necessary
5491  if (($questionpool != 0) && (!$use_obj_id)) $questionpool = ilObject::_lookupObjId($questionpool);
5492 
5493  // get original ids of all existing questions in the test
5494  $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",
5495  array("integer"),
5496  array($this->getTestId())
5497  );
5498  $original_ids = array();
5499  $paramtypes = array();
5500  $paramvalues = array();
5501  while ($row = $ilDB->fetchAssoc($result))
5502  {
5503  array_push($original_ids, $row['original_id']);
5504  }
5505 
5506  $available = "";
5507  // get a list of all available questionpools
5508  if (($questionpool == 0) && (!is_array($qpls)))
5509  {
5510  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5511  $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())));
5512  if (count($available_pools))
5513  {
5514  $available = " AND " . $ilDB->in('obj_fi', $available_pools, false, 'integer');
5515  }
5516  else
5517  {
5518  return array();
5519  }
5520  }
5521 
5522  $constraint_qpls = "";
5523  $result_array = array();
5524  if ($questionpool == 0)
5525  {
5526  if (is_array($qpls))
5527  {
5528  if (count($qpls) > 0)
5529  {
5530  $constraint_qpls = " AND " . $ilDB->in('obj_fi', $qpls, false, 'integer');
5531  }
5532  }
5533  }
5534 
5535  $original_clause = "";
5536  if (count($original_ids))
5537  {
5538  $original_clause = " AND " . $ilDB->in('question_id', $original_ids, true, 'integer');
5539  }
5540 
5541  if ($questionpool == 0)
5542  {
5543  $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",
5544  array('integer', 'text'),
5545  array(0, "1")
5546  );
5547  }
5548  else
5549  {
5550  $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",
5551  array('integer','integer', 'text'),
5552  array($questionpool, 0, "1")
5553  );
5554  }
5555  $found_ids = array();
5556  while ($row = $ilDB->fetchAssoc($result))
5557  {
5558  array_push($found_ids, $row['question_id']);
5559  }
5560  $nr_of_questions = ($nr_of_questions > count($found_ids)) ? count($found_ids) : $nr_of_questions;
5561  if ($nr_of_questions == 0) return array();
5562  $rand_keys = array_rand($found_ids, $nr_of_questions);
5563  $result = array();
5564  if (is_array($rand_keys))
5565  {
5566  foreach ($rand_keys as $key)
5567  {
5568  $result[$found_ids[$key]] = $found_ids[$key];
5569  }
5570  }
5571  else
5572  {
5573  $result[$found_ids[$rand_keys]] = $found_ids[$rand_keys];
5574  }
5575  return $result;
5576  }
5577 
5584  function getImagePath()
5585  {
5586  return CLIENT_WEB_DIR . "/assessment/" . $this->getId() . "/images/";
5587  }
5588 
5595  function getImagePathWeb()
5596  {
5597  include_once "./Services/Utilities/classes/class.ilUtil.php";
5598  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/" . $this->getId() . "/images/";
5600  }
5601 
5610  function &createQuestionGUI($question_type, $question_id = -1)
5611  {
5612  if ((!$question_type) and ($question_id > 0))
5613  {
5614  $question_type = $this->getQuestionType($question_id);
5615  }
5616  if (!strlen($question_type)) return null;
5617  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
5618  assQuestion::_includeClass($question_type, 1);
5619  $question_type_gui = $question_type . "GUI";
5620  $question = new $question_type_gui();
5621  if ($question_id > 0)
5622  {
5623  $question->object->loadFromDb($question_id);
5624  }
5625  return $question;
5626  }
5627 
5635  function &_instanciateQuestion($question_id)
5636  {
5637  if (strcmp($question_id, "") != 0)
5638  {
5639  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
5640  return assQuestion::_instanciateQuestion($question_id);
5641  }
5642  }
5643 
5652  function moveQuestions($move_questions, $target_index, $insert_mode)
5653  {
5654  $this->questions = array_values($this->questions);
5655  $array_pos = array_search($target_index, $this->questions);
5656  if ($insert_mode == 0)
5657  {
5658  $part1 = array_slice($this->questions, 0, $array_pos);
5659  $part2 = array_slice($this->questions, $array_pos);
5660  }
5661  else if ($insert_mode == 1)
5662  {
5663  $part1 = array_slice($this->questions, 0, $array_pos + 1);
5664  $part2 = array_slice($this->questions, $array_pos + 1);
5665  }
5666  foreach ($move_questions as $question_id)
5667  {
5668  if (!(array_search($question_id, $part1) === FALSE))
5669  {
5670  unset($part1[array_search($question_id, $part1)]);
5671  }
5672  if (!(array_search($question_id, $part2) === FALSE))
5673  {
5674  unset($part2[array_search($question_id, $part2)]);
5675  }
5676  }
5677  $part1 = array_values($part1);
5678  $part2 = array_values($part2);
5679  $new_array = array_values(array_merge($part1, $move_questions, $part2));
5680  $this->questions = array();
5681  $counter = 1;
5682  foreach ($new_array as $question_id)
5683  {
5684  $this->questions[$counter] = $question_id;
5685  $counter++;
5686  }
5687  $this->saveQuestionsToDb();
5688  }
5689 
5690 
5699  {
5700  if ($this->getStartingTime())
5701  {
5702  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getStartingTime(), $matches))
5703  {
5704  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5705  $now = mktime();
5706  if ($now < $epoch_time)
5707  {
5708  // starting time not reached
5709  return false;
5710  }
5711  }
5712  }
5713  return true;
5714  }
5715 
5724  {
5725  if ($this->getEndingTime())
5726  {
5727  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndingTime(), $matches))
5728  {
5729  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5730  $now = mktime();
5731  if ($now > $epoch_time)
5732  {
5733  // ending time reached
5734  return true;
5735  }
5736  }
5737  }
5738  return false;
5739  }
5740 
5746  function getAvailableQuestions($arrFilter, $completeonly = 0)
5747  {
5748  global $ilUser;
5749  global $ilDB;
5750 
5751  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5752  $available_pools = array_keys(ilObjQuestionPool::_getAvailableQuestionpools($use_object_id = TRUE, $equal_points = FALSE, $could_be_offline = FALSE, $showPath = FALSE, $with_questioncount = FALSE));
5753  $available = "";
5754  if (count($available_pools))
5755  {
5756  $available = " AND " . $ilDB->in('qpl_questions.obj_fi', $available_pools, false, 'integer');
5757  }
5758  else
5759  {
5760  return array();
5761  }
5762  if ($completeonly)
5763  {
5764  $available .= " AND qpl_questions.complete = " . $ilDB->quote("1", 'text');
5765  }
5766 
5767  $where = "";
5768  if (is_array($arrFilter))
5769  {
5770  if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title']))
5771  {
5772  $where .= " AND " . $ilDB->like('qpl_questions.title', 'text', "%%" . $arrFilter['title'] . "%%");
5773  }
5774  if (array_key_exists('description', $arrFilter) && strlen($arrFilter['description']))
5775  {
5776  $where .= " AND " . $ilDB->like('qpl_questions.description', 'text', "%%" . $arrFilter['description'] . "%%");
5777  }
5778  if (array_key_exists('author', $arrFilter) && strlen($arrFilter['author']))
5779  {
5780  $where .= " AND " . $ilDB->like('qpl_questions.author', 'text', "%%" . $arrFilter['author'] . "%%");
5781  }
5782  if (array_key_exists('type', $arrFilter) && strlen($arrFilter['type']))
5783  {
5784  $where .= " AND qpl_qst_type.type_tag = " . $ilDB->quote($arrFilter['type'], 'text');
5785  }
5786  if (array_key_exists('qpl', $arrFilter) && strlen($arrFilter['qpl']))
5787  {
5788  $where .= " AND " . $ilDB->like('object_data.title', 'text', "%%" . $arrFilter['qpl'] . "%%");
5789  }
5790  }
5791 
5792  $original_ids =& $this->getExistingQuestions();
5793  $original_clause = " qpl_questions.original_id IS NULL";
5794  if (count($original_ids))
5795  {
5796  $original_clause = " qpl_questions.original_id IS NULL AND " . $ilDB->in('qpl_questions.question_id', $original_ids, true, 'integer');
5797  }
5798 
5799  $query_result = $ilDB->query(
5800  "SELECT qpl_questions.*, qpl_questions.tstamp, qpl_qst_type.type_tag, qpl_qst_type.plugin, object_data.title qpl " .
5801  "FROM qpl_questions, qpl_qst_type, object_data WHERE $original_clause $available AND " .
5802  "object_data.obj_id = qpl_questions.obj_fi AND qpl_questions.tstamp > 0 AND " .
5803  "qpl_questions.question_type_fi = qpl_qst_type.question_type_id$where");
5804  $rows = array();
5805  $types = $this->getQuestionTypeTranslations();
5806  if ($query_result->numRows())
5807  {
5808  while ($row = $ilDB->fetchAssoc($query_result))
5809  {
5810  $row['ttype'] = $types[$row['type_tag']];
5811  if ($row["plugin"])
5812  {
5813  if ($this->isPluginActive($row["type_tag"]))
5814  {
5815  array_push($rows, $row);
5816  }
5817  }
5818  else
5819  {
5820  array_push($rows, $row);
5821  }
5822  }
5823  }
5824  return $rows;
5825  }
5826 
5827  public function &getQuestionTypeTranslations()
5828  {
5829  global $ilDB;
5830  global $lng;
5831  global $ilLog;
5832  global $ilPluginAdmin;
5833 
5834  $lng->loadLanguageModule("assessment");
5835  $result = $ilDB->query("SELECT * FROM qpl_qst_type");
5836  $types = array();
5837  while ($row = $ilDB->fetchAssoc($result))
5838  {
5839  if ($row["plugin"] == 0)
5840  {
5841  $types[$row['type_tag']] = $lng->txt($row["type_tag"]);
5842  }
5843  else
5844  {
5845  $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
5846  foreach ($pl_names as $pl_name)
5847  {
5848  $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
5849  if (strcmp($pl->getQuestionType(), $row["type_tag"]) == 0)
5850  {
5851  $types[$row['type_tag']] = $pl->getQuestionTypeTranslation();
5852  }
5853  }
5854  }
5855  }
5856  ksort($types);
5857  return $types;
5858  }
5859 
5866  function fromXML(&$assessment)
5867  {
5868  unset($_SESSION["import_mob_xhtml"]);
5869 
5870  $this->setDescription($assessment->getComment());
5871  $this->setTitle($assessment->getTitle());
5872 
5873  foreach ($assessment->objectives as $objectives)
5874  {
5875  foreach ($objectives->materials as $material)
5876  {
5877  $this->setIntroduction($this->QTIMaterialToString($material));
5878  }
5879  }
5880  if ($assessment->getPresentationMaterial())
5881  {
5882  $this->setFinalStatement($this->QTIMaterialToString($assessment->getPresentationMaterial()));
5883  }
5884 
5885  foreach ($assessment->assessmentcontrol as $assessmentcontrol)
5886  {
5887  switch ($assessmentcontrol->getSolutionswitch())
5888  {
5889  case "Yes":
5890  $this->setInstantFeedbackSolution(1);
5891  break;
5892  default:
5893  $this->setInstantFeedbackSolution(0);
5894  break;
5895  }
5896  }
5897 
5898  foreach ($assessment->qtimetadata as $metadata)
5899  {
5900  switch ($metadata["label"])
5901  {
5902  case "test_type":
5903  // for old tests with a test type
5904  $type = $metadata["entry"];
5905  switch ($type)
5906  {
5907  case 1:
5908  // assessment
5909  $this->setAnonymity(1);
5910  break;
5911  case 2:
5912  // self assessment
5913  break;
5914  case 4:
5915  // online exam
5916  $this->setFixedParticipants(1);
5917  $this->setListOfQuestionsSettings(7);
5918  $this->setShowSolutionPrintview(1);
5919  break;
5920  case 5:
5921  // varying random test
5922  break;
5923  }
5924  break;
5925  case "sequence_settings":
5926  $this->setSequenceSettings($metadata["entry"]);
5927  break;
5928  case "author":
5929  $this->setAuthor($metadata["entry"]);
5930  break;
5931  case "nr_of_tries":
5932  $this->setNrOfTries($metadata["entry"]);
5933  break;
5934  case "kiosk":
5935  $this->setKiosk($metadata["entry"]);
5936  break;
5937  case "showfinalstatement":
5938  $this->setShowFinalStatement($metadata["entry"]);
5939  break;
5940  case "redirect_after_exam":
5941  $this->setRedirectAfterExam($metadata["entry"]);
5942  break;
5943  case "redirect_only_kiosk_mode":
5944  $this->setRedirectOnlyKioskMode($metadata["entry"]);
5945  break;
5946  case "exam_pdf":
5947  $this->setExamPdf($metadata["entry"]);
5948  break;
5949  case "showinfo":
5950  $this->setShowInfo($metadata["entry"]);
5951  break;
5952  case "forcejs":
5953  $this->setForceJS($metadata["entry"]);
5954  break;
5955  case "customstyle":
5956  $this->setCustomStyle($metadata["entry"]);
5957  break;
5958  case "hide_previous_results":
5959  if ($metadata["entry"] == 0)
5960  {
5961  $this->setUsePreviousAnswers(1);
5962  }
5963  else
5964  {
5965  $this->setUsePreviousAnswers(0);
5966  }
5967  break;
5968  case "use_previous_answers":
5969  $this->setUsePreviousAnswers($metadata["entry"]);
5970  break;
5971  case "answer_feedback":
5972  $this->setAnswerFeedback($metadata["entry"]);
5973  break;
5974  case "hide_title_points":
5975  $this->setTitleOutput($metadata["entry"]);
5976  break;
5977  case "title_output":
5978  $this->setTitleOutput($metadata["entry"]);
5979  break;
5980  case "random_test":
5981  $this->setRandomTest($metadata["entry"]);
5982  break;
5983  case "random_question_count":
5984  $this->setRandomQuestionCount($metadata["entry"]);
5985  break;
5986  case "results_presentation":
5987  $this->setResultsPresentation($metadata["entry"]);
5988  break;
5989  case "reset_processing_time":
5990  $this->setResetProcessingTime($metadata["entry"]);
5991  break;
5992  case "instant_verification":
5993  $this->setInstantFeedbackSolution($metadata["entry"]);
5994  break;
5995  case "answer_feedback_points":
5996  $this->setAnswerFeedbackPoints($metadata["entry"]);
5997  break;
5998  case "anonymity":
5999  $this->setAnonymity($metadata["entry"]);
6000  break;
6001  case "show_cancel":
6002  $this->setShowCancel($metadata["entry"]);
6003  break;
6004  case "autosave":
6005  $this->setAutosave($metadata["entry"]);
6006  break;
6007  case "show_marker":
6008  $this->setShowMarker($metadata["entry"]);
6009  break;
6010  case "show_serial":
6011  $this->setShowSerial($metadata["entry"]);
6012  break;
6013  case "fixed_participants":
6014  $this->setFixedParticipants($metadata["entry"]);
6015  break;
6016  case "score_reporting":
6017  $this->setScoreReporting($metadata["entry"]);
6018  break;
6019  case "shuffle_questions":
6020  $this->setShuffleQuestions($metadata["entry"]);
6021  break;
6022  case "count_system":
6023  $this->setCountSystem($metadata["entry"]);
6024  break;
6025  case "mc_scoring":
6026  $this->setMCScoring($metadata["entry"]);
6027  break;
6028  case "mailnotification":
6029  $this->setMailNotification($metadata["entry"]);
6030  break;
6031  case "mailnottype":
6032  $this->setMailNotificationType($metadata["entry"]);
6033  break;
6034  case "exportsettings":
6035  $this->setExportSettings($metadata['exportsettings']);
6036  break;
6037  case "score_cutting":
6038  $this->setScoreCutting($metadata["entry"]);
6039  break;
6040  case "password":
6041  $this->setPassword($metadata["entry"]);
6042  break;
6043  case "allowedUsers":
6044  $this->setAllowedUsers($metadata["entry"]);
6045  break;
6046  case "allowedUsersTimeGap":
6047  $this->setAllowedUsersTimeGap($metadata["entry"]);
6048  break;
6049  case "pass_scoring":
6050  $this->setPassScoring($metadata["entry"]);
6051  break;
6052  case "show_summary":
6053  $this->setListOfQuestionsSettings($metadata["entry"]);
6054  break;
6055  case "reporting_date":
6056  $iso8601period = $metadata["entry"];
6057  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
6058  {
6059  $this->setReportingDate(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
6060  }
6061  break;
6062  case "processing_time":
6063  $this->setProcessingTime($metadata['entry']);
6064  break;
6065  case "enable_processing_time":
6066  $this->setEnableProcessingTime($metadata['entry']);
6067  break;
6068  case "starting_time":
6069  $iso8601period = $metadata["entry"];
6070  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
6071  {
6072  $this->setStartingTime(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
6073  }
6074  break;
6075  case "ending_time":
6076  $iso8601period = $metadata["entry"];
6077  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
6078  {
6079  $this->setEndingTime(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
6080  }
6081  break;
6082  }
6083  if (preg_match("/mark_step_\d+/", $metadata["label"]))
6084  {
6085  $xmlmark = $metadata["entry"];
6086  preg_match("/<short>(.*?)<\/short>/", $xmlmark, $matches);
6087  $mark_short = $matches[1];
6088  preg_match("/<official>(.*?)<\/official>/", $xmlmark, $matches);
6089  $mark_official = $matches[1];
6090  preg_match("/<percentage>(.*?)<\/percentage>/", $xmlmark, $matches);
6091  $mark_percentage = $matches[1];
6092  preg_match("/<passed>(.*?)<\/passed>/", $xmlmark, $matches);
6093  $mark_passed = $matches[1];
6094  $this->mark_schema->addMarkStep($mark_short, $mark_official, $mark_percentage, $mark_passed);
6095  }
6096  }
6097  // handle the import of media objects in XHTML code
6098  if (is_array($_SESSION["import_mob_xhtml"]))
6099  {
6100  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
6101  include_once "./Services/RTE/classes/class.ilRTE.php";
6102  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
6103  foreach ($_SESSION["import_mob_xhtml"] as $mob)
6104  {
6105  $importfile = $this->getImportDirectory() . "/" . $_SESSION["tst_import_subdir"] . "/" . $mob["uri"];
6106  if (file_exists($importfile))
6107  {
6108  $media_object =& ilObjMediaObject::_saveTempFileAsMediaObject(basename($importfile), $importfile, FALSE);
6109  ilObjMediaObject::_saveUsage($media_object->getId(), "tst:html", $this->getId());
6110  $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getIntroduction()), 1));
6111  $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getFinalStatement()), 1));
6112  }
6113  else
6114  {
6115  global $ilLog;
6116  $ilLog->write("Error: Could not open XHTML mob file for test introduction during test import. File $importfile does not exist!");
6117  }
6118  }
6119  $this->saveToDb();
6120  }
6121  }
6122 
6129  function exportPagesXML(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
6130  {
6131  global $ilBench;
6132 
6133  $this->mob_ids = array();
6134  $this->file_ids = array();
6135 
6136  $attrs = array();
6137  $attrs["Type"] = "Test";
6138  $a_xml_writer->xmlStartTag("ContentObject", $attrs);
6139 
6140  // MetaData
6141  $this->exportXMLMetaData($a_xml_writer);
6142 
6143  // PageObjects
6144  $expLog->write(date("[y-m-d H:i:s] ")."Start Export Page Objects");
6145  $ilBench->start("ContentObjectExport", "exportPageObjects");
6146  $this->exportXMLPageObjects($a_xml_writer, $a_inst, $expLog);
6147  $ilBench->stop("ContentObjectExport", "exportPageObjects");
6148  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export Page Objects");
6149 
6150  // MediaObjects
6151  $expLog->write(date("[y-m-d H:i:s] ")."Start Export Media Objects");
6152  $ilBench->start("ContentObjectExport", "exportMediaObjects");
6153  $this->exportXMLMediaObjects($a_xml_writer, $a_inst, $a_target_dir, $expLog);
6154  $ilBench->stop("ContentObjectExport", "exportMediaObjects");
6155  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export Media Objects");
6156 
6157  // FileItems
6158  $expLog->write(date("[y-m-d H:i:s] ")."Start Export File Items");
6159  $ilBench->start("ContentObjectExport", "exportFileItems");
6160  $this->exportFileItems($a_target_dir, $expLog);
6161  $ilBench->stop("ContentObjectExport", "exportFileItems");
6162  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export File Items");
6163 
6164  $a_xml_writer->xmlEndTag("ContentObject");
6165  }
6166 
6173  function exportXMLMetaData(&$a_xml_writer)
6174  {
6175  include_once "./Services/MetaData/classes/class.ilMD2XML.php";
6176  $md2xml = new ilMD2XML($this->getId(), 0, $this->getType());
6177  $md2xml->setExportMode(true);
6178  $md2xml->startExport();
6179  $a_xml_writer->appendXML($md2xml->getXML());
6180  }
6181 
6187  function modifyExportIdentifier($a_tag, $a_param, $a_value)
6188  {
6189  if ($a_tag == "Identifier" && $a_param == "Entry")
6190  {
6191  include_once "./Services/Utilities/classes/class.ilUtil.php";
6192  $a_value = ilUtil::insertInstIntoID($a_value);
6193  }
6194 
6195  return $a_value;
6196  }
6197 
6198 
6205  function exportXMLPageObjects(&$a_xml_writer, $a_inst, &$expLog)
6206  {
6207  global $ilBench;
6208 
6209  include_once "./Modules/LearningModule/classes/class.ilLMPageObject.php";
6210 
6211  foreach ($this->questions as $question_id)
6212  {
6213  $ilBench->start("ContentObjectExport", "exportPageObject");
6214  $expLog->write(date("[y-m-d H:i:s] ")."Page Object ".$question_id);
6215 
6216  $attrs = array();
6217  $a_xml_writer->xmlStartTag("PageObject", $attrs);
6218 
6219 
6220  // export xml to writer object
6221  $ilBench->start("ContentObjectExport", "exportPageObject_XML");
6222  $page_object = new ilPageObject("qpl", $question_id);
6223  $page_object->buildDom();
6224  $page_object->insertInstIntoIDs($a_inst);
6225  $mob_ids = $page_object->collectMediaObjects(false);
6226  $file_ids = $page_object->collectFileItems();
6227  $xml = $page_object->getXMLFromDom(false, false, false, "", true);
6228  $xml = str_replace("&","&amp;", $xml);
6229  $a_xml_writer->appendXML($xml);
6230  $page_object->freeDom();
6231  unset ($page_object);
6232 
6233  $ilBench->stop("ContentObjectExport", "exportPageObject_XML");
6234 
6235  // collect media objects
6236  $ilBench->start("ContentObjectExport", "exportPageObject_CollectMedia");
6237  //$mob_ids = $page_obj->getMediaObjectIDs();
6238  foreach($mob_ids as $mob_id)
6239  {
6240  $this->mob_ids[$mob_id] = $mob_id;
6241  }
6242  $ilBench->stop("ContentObjectExport", "exportPageObject_CollectMedia");
6243 
6244  // collect all file items
6245  $ilBench->start("ContentObjectExport", "exportPageObject_CollectFileItems");
6246  //$file_ids = $page_obj->getFileItemIds();
6247  foreach($file_ids as $file_id)
6248  {
6249  $this->file_ids[$file_id] = $file_id;
6250  }
6251  $ilBench->stop("ContentObjectExport", "exportPageObject_CollectFileItems");
6252 
6253  $a_xml_writer->xmlEndTag("PageObject");
6254  //unset($page_obj);
6255 
6256  $ilBench->stop("ContentObjectExport", "exportPageObject");
6257 
6258 
6259  }
6260  }
6261 
6268  function exportXMLMediaObjects(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
6269  {
6270  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
6271 
6272  foreach ($this->mob_ids as $mob_id)
6273  {
6274  $expLog->write(date("[y-m-d H:i:s] ")."Media Object ".$mob_id);
6275  if (ilObjMediaObject::_exists($mob_id))
6276  {
6277  $media_obj = new ilObjMediaObject($mob_id);
6278  $media_obj->exportXML($a_xml_writer, $a_inst);
6279  $media_obj->exportFiles($a_target_dir);
6280  unset($media_obj);
6281  }
6282  }
6283  }
6284 
6289  function exportFileItems($a_target_dir, &$expLog)
6290  {
6291  include_once "./Modules/File/classes/class.ilObjFile.php";
6292 
6293  foreach ($this->file_ids as $file_id)
6294  {
6295  $expLog->write(date("[y-m-d H:i:s] ")."File Item ".$file_id);
6296  $file_obj = new ilObjFile($file_id, false);
6297  $file_obj->export($a_target_dir);
6298  unset($file_obj);
6299  }
6300  }
6301 
6306  function getImportMapping()
6307  {
6308  if (!is_array($this->import_mapping))
6309  {
6310  return array();
6311  }
6312  else
6313  {
6314  return $this->import_mapping;
6315  }
6316  }
6317 
6327  function getECTSGrade($passed_array, $reached_points, $max_points)
6328  {
6329  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);
6330  }
6331 
6340  function _getECTSGrade($points_passed, $reached_points, $max_points, $a, $b, $c, $d, $e, $fx)
6341  {
6342  include_once "./classes/class.ilStatistics.php";
6343  // calculate the median
6344  $passed_statistics = new ilStatistics();
6345  $passed_statistics->setData($points_passed);
6346  $ects_percentiles = array
6347  (
6348  "A" => $passed_statistics->quantile($a),
6349  "B" => $passed_statistics->quantile($b),
6350  "C" => $passed_statistics->quantile($c),
6351  "D" => $passed_statistics->quantile($d),
6352  "E" => $passed_statistics->quantile($e)
6353  );
6354  if (count($points_passed) && ($reached_points >= $ects_percentiles["A"]))
6355  {
6356  return "A";
6357  }
6358  else if (count($points_passed) && ($reached_points >= $ects_percentiles["B"]))
6359  {
6360  return "B";
6361  }
6362  else if (count($points_passed) && ($reached_points >= $ects_percentiles["C"]))
6363  {
6364  return "C";
6365  }
6366  else if (count($points_passed) && ($reached_points >= $ects_percentiles["D"]))
6367  {
6368  return "D";
6369  }
6370  else if (count($points_passed) && ($reached_points >= $ects_percentiles["E"]))
6371  {
6372  return "E";
6373  }
6374  else if (strcmp($fx, "") != 0)
6375  {
6376  if ($max_points > 0)
6377  {
6378  $percentage = ($reached_points / $max_points) * 100.0;
6379  if ($percentage < 0) $percentage = 0.0;
6380  }
6381  else
6382  {
6383  $percentage = 0.0;
6384  }
6385  if ($percentage >= $fx)
6386  {
6387  return "FX";
6388  }
6389  else
6390  {
6391  return "F";
6392  }
6393  }
6394  else
6395  {
6396  return "F";
6397  }
6398  }
6399 
6400  function checkMarks()
6401  {
6402  return $this->mark_schema->checkMarks();
6403  }
6404 
6405  function getMarkSchema()
6406  {
6407  return $this->mark_schema;
6408  }
6409 
6417  function setAuthor($author = "")
6418  {
6419  $this->author = $author;
6420  }
6421 
6431  function saveAuthorToMetadata($a_author = "")
6432  {
6433  $md =& new ilMD($this->getId(), 0, $this->getType());
6434  $md_life =& $md->getLifecycle();
6435  if (!$md_life)
6436  {
6437  if (strlen($a_author) == 0)
6438  {
6439  global $ilUser;
6440  $a_author = $ilUser->getFullname();
6441  }
6442 
6443  $md_life =& $md->addLifecycle();
6444  $md_life->save();
6445  $con =& $md_life->addContribute();
6446  $con->setRole("Author");
6447  $con->save();
6448  $ent =& $con->addEntity();
6449  $ent->setEntity($a_author);
6450  $ent->save();
6451  }
6452  }
6453 
6459  function createMetaData()
6460  {
6462  $this->saveAuthorToMetadata();
6463  }
6464 
6472  function getAuthor()
6473  {
6474  $author = array();
6475  include_once "./Services/MetaData/classes/class.ilMD.php";
6476  $md =& new ilMD($this->getId(), 0, $this->getType());
6477  $md_life =& $md->getLifecycle();
6478  if ($md_life)
6479  {
6480  $ids =& $md_life->getContributeIds();
6481  foreach ($ids as $id)
6482  {
6483  $md_cont =& $md_life->getContribute($id);
6484  if (strcmp($md_cont->getRole(), "Author") == 0)
6485  {
6486  $entids =& $md_cont->getEntityIds();
6487  foreach ($entids as $entid)
6488  {
6489  $md_ent =& $md_cont->getEntity($entid);
6490  array_push($author, $md_ent->getEntity());
6491  }
6492  }
6493  }
6494  }
6495  return join($author, ",");
6496  }
6497 
6505  function _lookupAuthor($obj_id)
6506  {
6507  $author = array();
6508  include_once "./Services/MetaData/classes/class.ilMD.php";
6509  $md =& new ilMD($obj_id, 0, "tst");
6510  $md_life =& $md->getLifecycle();
6511  if ($md_life)
6512  {
6513  $ids =& $md_life->getContributeIds();
6514  foreach ($ids as $id)
6515  {
6516  $md_cont =& $md_life->getContribute($id);
6517  if (strcmp($md_cont->getRole(), "Author") == 0)
6518  {
6519  $entids =& $md_cont->getEntityIds();
6520  foreach ($entids as $entid)
6521  {
6522  $md_ent =& $md_cont->getEntity($entid);
6523  array_push($author, $md_ent->getEntity());
6524  }
6525  }
6526  }
6527  }
6528  return join($author, ",");
6529  }
6530 
6537  function &_getAvailableTests($use_object_id = FALSE)
6538  {
6539  global $ilUser;
6540  global $ilDB;
6541 
6542  $result_array = array();
6543  $tests = ilUtil::_getObjectsByOperations("tst","write", $ilUser->getId(), -1);
6544  if (count($tests))
6545  {
6546  $titles = ilObject::_prepareCloneSelection($tests, "tst");
6547  foreach ($tests as $ref_id)
6548  {
6549  if ($use_object_id)
6550  {
6551  $obj_id = ilObject::_lookupObjId($ref_id);
6552  $result_array[$obj_id] = $titles[$ref_id];
6553  }
6554  else
6555  {
6556  $result_array[$ref_id] = $titles[$ref_id];
6557  }
6558  }
6559  }
6560  return $result_array;
6561  }
6562 
6569  function cloneRandomQuestions($new_id)
6570  {
6571  global $ilDB;
6572 
6573  if ($new_id > 0)
6574  {
6575  $result = $ilDB->queryF("SELECT * FROM tst_test_random WHERE test_fi = %s ORDER BY sequence, test_random_id",
6576  array('integer'),
6577  array($this->getTestId())
6578  );
6579  if ($result->numRows())
6580  {
6581  while ($row = $ilDB->fetchAssoc($result))
6582  {
6583  $next_id = $ilDB->nextId('tst_test_random');
6584  $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)",
6585  array('integer', 'integer', 'integer', 'integer', 'integer', 'integer'),
6586  array($next_id, $new_id, $row["questionpool_fi"], $row["num_of_q"], time(), $row['sequence'])
6587  );
6588  }
6589  }
6590  }
6591  }
6592 
6593 
6602  public function cloneObject($a_target_id,$a_copy_id = 0)
6603  {
6604  global $ilDB,$ilLog;
6605 
6606  $this->loadFromDb();
6607 
6608  // Copy settings
6609  $newObj = parent::cloneObject($a_target_id,$a_copy_id);
6610  $this->cloneMetaData($newObj);
6611  $newObj->setAnonymity($this->getAnonymity());
6612  $newObj->setAnswerFeedback($this->getAnswerFeedback());
6613  $newObj->setAnswerFeedbackPoints($this->getAnswerFeedbackPoints());
6614  $newObj->setAuthor($this->getAuthor());
6615  $newObj->setCountSystem($this->getCountSystem());
6616  $newObj->setECTSFX($this->getECTSFX());
6617  $newObj->setECTSGrades($this->getECTSGrades());
6618  $newObj->setECTSOutput($this->getECTSOutput());
6619  $newObj->setEnableProcessingTime($this->getEnableProcessingTime());
6620  $newObj->setEndingTime($this->getEndingTime());
6621  $newObj->setFixedParticipants($this->getFixedParticipants());
6622  $newObj->setInstantFeedbackSolution($this->getInstantFeedbackSolution());
6623  $newObj->setIntroduction($this->getIntroduction());
6624  $newObj->setFinalStatement($this->getFinalStatement());
6625  /* Change Sn */
6626  $newObj->setRedirectAfterExam($this->getRedirectAfterExam());
6627  $newObj->setRedirectOnlyKioskMode($this->getRedirectOnlyKioskMode());
6628  $newObj->setExamPdf($this->getExamPdf());
6629  /* Change Sn End */
6630  $newObj->setShowInfo($this->getShowInfo());
6631  $newObj->setForceJS($this->getForceJS());
6632  $newObj->setCustomStyle($this->getCustomStyle());
6633  $newObj->setShowFinalStatement($this->getShowFinalStatement());
6634  $newObj->setListOfQuestionsSettings($this->getListOfQuestionsSettings());
6635  $newObj->setKiosk($this->getKiosk());
6636  $newObj->setMCScoring($this->getMCScoring());
6637  $newObj->setMailNotification($this->getMailNotification());
6638  $newObj->setMailNotificationType($this->getMailNotificationType());
6639  $newObj->setNrOfTries($this->getNrOfTries());
6640  $newObj->setPassScoring($this->getPassScoring());
6641  $newObj->setPassword($this->getPassword());
6642  $newObj->setProcessingTime($this->getProcessingTime());
6643  $newObj->setRandomQuestionCount($this->getRandomQuestionCount());
6644  $newObj->setRandomTest($this->isRandomTest());
6645  $newObj->setReportingDate($this->getReportingDate());
6646  $newObj->setResetProcessingTime($this->getResetProcessingTime());
6647  $newObj->setResultsPresentation($this->getResultsPresentation());
6648  $newObj->setScoreCutting($this->getScoreCutting());
6649  $newObj->setScoreReporting($this->getScoreReporting());
6650  $newObj->setSequenceSettings($this->getSequenceSettings());
6651  $newObj->setShowCancel($this->getShowCancel());
6652  $newObj->setAutosave($this->getAutosave());
6653  $newObj->setShowMarker($this->getShowMarker());
6654  $newObj->setShuffleQuestions($this->getShuffleQuestions());
6655  $newObj->setStartingTime($this->getStartingTime());
6656  $newObj->setTitleOutput($this->getTitleOutput());
6657  $newObj->setUsePreviousAnswers($this->getUsePreviousAnswers());
6658  $newObj->setCertificateVisibility($this->getCertificateVisibility());
6659  $newObj->mark_schema = clone $this->mark_schema;
6660  $newObj->saveToDb();
6661 
6662  // clone certificate
6663  include_once "./Services/Certificate/classes/class.ilCertificate.php";
6664  include_once "./Modules/Test/classes/class.ilTestCertificateAdapter.php";
6665  $cert = new ilCertificate(new ilTestCertificateAdapter($this));
6666  $newcert = new ilCertificate(new ilTestCertificateAdapter($newObj));
6667  $cert->cloneCertificate($newcert);
6668 
6669  if ($this->isRandomTest())
6670  {
6671  $newObj->saveRandomQuestionCount($newObj->getRandomQuestionCount());
6672  $this->cloneRandomQuestions($newObj->getTestId());
6673  }
6674  else
6675  {
6676  include_once("./Services/CopyWizard/classes/class.ilCopyWizardOptions.php");
6677  $cwo = ilCopyWizardOptions::_getInstance($a_copy_id);
6678 
6679  // clone the questions
6680  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
6681  foreach ($this->questions as $key => $question_id)
6682  {
6683  $question = ilObjTest::_instanciateQuestion($question_id);
6684  $newObj->questions[$key] = $question->duplicate();
6685  $original_id = assQuestion::_getOriginalId($question_id);
6686  $question = ilObjTest::_instanciateQuestion($newObj->questions[$key]);
6687  $question->saveToDb($original_id);
6688 
6689  // Save the mapping of old question id <-> new question id
6690  // This will be used in class.ilObjCourse::cloneDependencies to copy learning objectives
6691  $cwo->appendMapping($this->getRefId().'_'.$question_id,$newObj->getRefId().'_'.$newObj->questions[$key]);
6692  $ilLog->write(__METHOD__.': Added mapping '.$this->getRefId().'_'.$question_id.' <-> ' .
6693  $newObj->getRefId().'_'.$newObj->questions[$key]);
6694  }
6695  }
6696  $newObj->saveToDb();
6697  return $newObj;
6698  }
6699 
6706  function getQuestionCount()
6707  {
6708  $num = 0;
6709 
6710  if ($this->isRandomTest())
6711  {
6712  if ($this->getRandomQuestionCount())
6713  {
6714  $num = $this->getRandomQuestionCount();
6715  $qpls =& $this->getRandomQuestionpools();
6716  $maxcount = 0;
6717  foreach ($qpls as $data)
6718  {
6719  $maxcount += $data["contains"];
6720  }
6721  if ($num > $maxcount) $num = $maxcount;
6722  }
6723  else
6724  {
6725  $qpls =& $this->getRandomQuestionpools();
6726  foreach ($qpls as $data)
6727  {
6728  $add = ($data["count"] <= $data["contains"]) ? $data["count"] : $data["contains"];
6729  $num += $add;
6730  }
6731  }
6732  }
6733  else
6734  {
6735  $num = count($this->questions);
6736  }
6737  return $num;
6738  }
6739 
6747  {
6748  global $ilDB;
6749 
6750  $num = 0;
6751 
6752  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
6753  array('integer'),
6754  array($test_id)
6755  );
6756  if (!$result->numRows())
6757  {
6758  return 0;
6759  }
6760  $test = $ilDB->fetchAssoc($result);
6761 
6762  if ($test["random_test"] == 1)
6763  {
6764  $qpls = array();
6765  $counter = 0;
6766  $result = $ilDB->queryF("SELECT * FROM tst_test_random WHERE test_fi = %s ORDER BY sequence, test_random_id",
6767  array('integer'),
6768  array($test_id)
6769  );
6770  if ($result->numRows())
6771  {
6772  while ($row = $ilDB->fetchAssoc($result))
6773  {
6774  $countresult = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE obj_fi = %s AND qpl_questions.tstamp > 0 AND original_id IS NULL",
6775  array('integer'),
6776  $row["questionpool_fi"]
6777  );
6778  $contains = $countresult->numRows();
6779  $qpls[$counter] = array(
6780  "index" => $counter,
6781  "count" => $row["num_of_q"],
6782  "qpl" => $row["questionpool_fi"],
6783  "contains" => $contains
6784  );
6785  $counter++;
6786  }
6787  }
6788  if ($test["random_question_count"] > 0)
6789  {
6790  $num = $test["random_question_count"];
6791  $maxcount = 0;
6792  foreach ($qpls as $data)
6793  {
6794  $maxcount += $data["contains"];
6795  }
6796  if ($num > $maxcount) $num = $maxcount;
6797  }
6798  else
6799  {
6800  $num = 0;
6801  foreach ($qpls as $data)
6802  {
6803  $add = ($data["count"] <= $data["contains"]) ? $data["count"] : $data["contains"];
6804  $num += $add;
6805  }
6806  }
6807  }
6808  else
6809  {
6810  $result = $ilDB->queryF("SELECT test_question_id FROM tst_test_question WHERE test_fi = %s",
6811  array('integer'),
6812  array($test_id)
6813  );
6814  $num = $result->numRows();
6815  }
6816  return $num;
6817  }
6818 
6825  {
6826  global $ilDB;
6827 
6828  // delete eventually set questions of a previous non-random test
6829  $this->removeAllTestEditings();
6830  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
6831  array('integer'),
6832  array($this->getTestId())
6833  );
6834  $this->questions = array();
6835  $this->saveCompleteStatus();
6836  }
6837 
6844  {
6845  global $ilDB;
6846  // delete eventually set random question pools of a previous random test
6847  $this->removeAllTestEditings();
6848  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
6849  array('integer'),
6850  array($this->getTestId())
6851  );
6852  $this->questions = array();
6853  $this->saveCompleteStatus();
6854  }
6855 
6863  function logAction($logtext = "", $question_id = "")
6864  {
6865  global $ilUser;
6866 
6867  $original_id = "";
6868  if (strcmp($question_id, "") != 0)
6869  {
6870  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
6871  $original_id = assQuestion::_getOriginalId($question_id);
6872  }
6873  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
6874  ilObjAssessmentFolder::_addLog($ilUser->getId(), $this->getId(), $logtext, $question_id, $original_id, TRUE, $this->getRefId());
6875  }
6876 
6885  {
6886  global $ilDB;
6887  $object_id = FALSE;
6888  $result = $ilDB->queryF("SELECT obj_fi FROM tst_tests WHERE test_id = %s",
6889  array('integer'),
6890  array($test_id)
6891  );
6892  if ($result->numRows())
6893  {
6894  $row = $ilDB->fetchAssoc($result);
6895  $object_id = $row["obj_fi"];
6896  }
6897  return $object_id;
6898  }
6899 
6907  function _getObjectIDFromActiveID($active_id)
6908  {
6909  global $ilDB;
6910  $object_id = FALSE;
6911  $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",
6912  array('integer'),
6913  array($active_id)
6914  );
6915  if ($result->numRows())
6916  {
6917  $row = $ilDB->fetchAssoc($result);
6918  $object_id = $row["obj_fi"];
6919  }
6920  return $object_id;
6921  }
6922 
6930  function _getTestIDFromObjectID($object_id)
6931  {
6932  global $ilDB;
6933  $test_id = FALSE;
6934  $result = $ilDB->queryF("SELECT test_id FROM tst_tests WHERE obj_fi = %s",
6935  array('integer'),
6936  array($object_id)
6937  );
6938  if ($result->numRows())
6939  {
6940  $row = $ilDB->fetchAssoc($result);
6941  $test_id = $row["test_id"];
6942  }
6943  return $test_id;
6944  }
6945 
6954  function getTextAnswer($active_id, $question_id, $pass = NULL)
6955  {
6956  global $ilDB;
6957 
6958  $res = "";
6959  if (($active_id) && ($question_id))
6960  {
6961  if (is_null($pass))
6962  {
6963  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
6964  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
6965  }
6966  $result = $ilDB->queryF("SELECT value1 FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
6967  array('integer', 'integer', 'integer'),
6968  array($active_id, $question_id, $pass)
6969  );
6970  if ($result->numRows() == 1)
6971  {
6972  $row = $ilDB->fetchAssoc($result);
6973  $res = $row["value1"];
6974  }
6975  }
6976  return $res;
6977  }
6978 
6986  function getQuestiontext($question_id)
6987  {
6988  global $ilDB;
6989 
6990  $res = "";
6991  if ($question_id)
6992  {
6993  $result = $ilDB->queryF("SELECT question_text FROM qpl_questions WHERE question_id = %s",
6994  array('integer'),
6995  array($question_id)
6996  );
6997  if ($result->numRows() == 1)
6998  {
6999  $row = $ilDB->fetchAssoc($result);
7000  $res = $row["question_text"];
7001  }
7002  }
7003  return $res;
7004  }
7005 
7012  function &getInvitedUsers($user_id="", $order="login, lastname, firstname")
7013  {
7014  global $ilDB;
7015 
7016  $result_array = array();
7017 
7018  if ($this->getAnonymity())
7019  {
7020  if (is_numeric($user_id))
7021  {
7022  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " .
7023  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7024  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7025  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
7026  "ORDER BY $order",
7027  array('text', 'text', 'text', 'integer', 'integer'),
7028  array("", $this->lng->txt("unknown"), "", $this->getTestId(), $user_id)
7029  );
7030  }
7031  else
7032  {
7033  $result = $ilDB->queryF("SELECT tst_active.active_id, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " .
7034  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7035  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7036  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
7037  "ORDER BY $order",
7038  array('text', 'text', 'text', 'integer'),
7039  array("", $this->lng->txt("unknown"), "", $this->getTestId())
7040  );
7041  }
7042  }
7043  else
7044  {
7045  if (is_numeric($user_id))
7046  {
7047  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " .
7048  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7049  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi " .
7050  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
7051  "ORDER BY $order",
7052  array('integer', 'integer'),
7053  array($this->getTestId(), $user_id)
7054  );
7055  }
7056  else
7057  {
7058  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " .
7059  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7060  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi " .
7061  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
7062  "ORDER BY $order",
7063  array('integer'),
7064  array($this->getTestId())
7065  );
7066  }
7067  }
7068  $result_array = array();
7069  while ($row = $ilDB->fetchAssoc($result))
7070  {
7071  $result_array[$row['usr_id']]= $row;
7072  }
7073  return $result_array;
7074  }
7075 
7083  {
7084  global $ilDB;
7085 
7086  if ($this->getAnonymity())
7087  {
7088  $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 ".
7089  "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),
7090  array('text', 'text', 'text', 'integer'),
7091  array("", $this->lng->txt("unknown"), "", $this->getTestId())
7092  );
7093  }
7094  else
7095  {
7096  $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 ".
7097  "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),
7098  array('integer'),
7099  array($this->getTestId())
7100  );
7101  }
7102  $data = array();
7103  while ($row = $ilDB->fetchAssoc($result))
7104  {
7105  $data[$row['active_id']] = $row;
7106  }
7107  foreach ($data as $index => $participant)
7108  {
7109  if (strlen(trim($participant["firstname"].$participant["lastname"])) == 0)
7110  {
7111  $data[$index]["lastname"] = $this->lng->txt("deleted_user");
7112  }
7113  }
7114  return $data;
7115  }
7116 
7117  public function getTestParticipantsForManualScoring($filter = NULL)
7118  {
7119  global $ilDB;
7120 
7121  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
7123  if (count($scoring) == 0) return array();
7124 
7125  $participants =& $this->getTestParticipants();
7126  $filtered_participants = array();
7127  foreach ($participants as $active_id => $participant)
7128  {
7129  $result = $ilDB->queryF("SELECT tst_test_result.manual FROM tst_test_result,qpl_questions WHERE tst_test_result.question_fi = qpl_questions.question_id AND " . $ilDB->in('qpl_questions.question_type_fi', $scoring, false, 'integer') . " AND tst_test_result.active_fi = %s",
7130  array("integer"),
7131  array($active_id)
7132  );
7133  $count = $result->numRows();
7134  if ($count > 0)
7135  {
7136  switch ($filter)
7137  {
7138  case 1: // only active users
7139  if ($participant->active) $filtered_participants[$active_id] = $participant;
7140  break;
7141  case 2: // only inactive users
7142  if (!$participant->active) $filtered_participants[$active_id] = $participant;
7143  break;
7144  case 3: // all users
7145  $filtered_participants[$active_id] = $participant;
7146  break;
7147  case 4:
7148  // already scored participants
7149  //$found = 0;
7150  //while ($row = $ilDB->fetchAssoc($result))
7151  //{
7152  // if ($row["manual"]) $found++;
7153  //}
7154  //if ($found == $count)
7155  //{
7156  //$filtered_participants[$active_id] = $participant;
7157  //}
7158  //else
7159  //{
7160  $assessmentSetting = new ilSetting("assessment");
7161  $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id);
7162  if ($manscoring_done) $filtered_participants[$active_id] = $participant;
7163  //}
7164  break;
7165  case 5:
7166  // unscored participants
7167  //$found = 0;
7168  //while ($row = $ilDB->fetchAssoc($result))
7169  //{
7170  // if ($row["manual"]) $found++;
7171  //}
7172  //if ($found == 0)
7173  //{
7174  $assessmentSetting = new ilSetting("assessment");
7175  $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id);
7176  if (!$manscoring_done) $filtered_participants[$active_id] = $participant;
7177  //}
7178  break;
7179  case 6:
7180  // partially scored participants
7181  $found = 0;
7182  while ($row = $ilDB->fetchAssoc($result))
7183  {
7184  if ($row["manual"]) $found++;
7185  }
7186  if (($found > 0) && ($found < $count)) $filtered_participants[$active_id] = $participant;
7187  break;
7188  default:
7189  $filtered_participants[$active_id] = $participant;
7190  break;
7191  }
7192  }
7193  }
7194  return $filtered_participants;
7195  }
7196 
7204  function &getUserData($ids)
7205  {
7206  global $ilDB;
7207  global $lng;
7208 
7209  if (!is_array($ids) || count($ids) ==0) return array();
7210 
7211  if ($this->getAnonymity())
7212  {
7213  $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",
7214  array('text', 'text', 'text'),
7215  array("", $lng->txt("unknown"), "")
7216  );
7217  }
7218  else
7219  {
7220  $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");
7221  }
7222 
7223  $result_array = array();
7224  while ($row = $ilDB->fetchAssoc($result))
7225  {
7226  $result_array[$row["usr_id"]]= $row;
7227  }
7228  return $result_array;
7229  }
7230 
7231  function &getGroupData($ids)
7232  {
7233  if (!is_array($ids) || count($ids) ==0) return array();
7234  $result = array();
7235  foreach ($ids as $ref_id)
7236  {
7237  $obj_id = ilObject::_lookupObjId($ref_id);
7238  $result[$ref_id] = array("ref_id" => $ref_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id));
7239  }
7240  return $result;
7241  }
7242 
7243  function &getRoleData($ids)
7244  {
7245  if (!is_array($ids) || count($ids) ==0) return array();
7246  $result = array();
7247  foreach ($ids as $obj_id)
7248  {
7249  $result[$obj_id] = array("obj_id" => $obj_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id));
7250  }
7251  return $result;
7252  }
7253 
7254 
7261  function inviteGroup($group_id)
7262  {
7263  include_once "./Modules/Group/classes/class.ilObjGroup.php";
7264  $group = new ilObjGroup($group_id);
7265  $members = $group->getGroupMemberIds();
7266  include_once './Services/User/classes/class.ilObjUser.php';
7267  foreach ($members as $user_id)
7268  {
7269  $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id));
7270  }
7271  }
7272 
7279  function inviteRole($role_id)
7280  {
7281  global $rbacreview;
7282  $members = $rbacreview->assignedUsers($role_id,"usr_id");
7283  include_once './Services/User/classes/class.ilObjUser.php';
7284  foreach ($members as $user_id)
7285  {
7286  $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id));
7287  }
7288  }
7289 
7290 
7291 
7298  function disinviteUser($user_id)
7299  {
7300  global $ilDB;
7301 
7302  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
7303  array('integer', 'integer'),
7304  array($this->getTestId(), $user_id)
7305  );
7306  }
7307 
7314  function inviteUser($user_id, $client_ip="")
7315  {
7316  global $ilDB;
7317 
7318  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
7319  array('integer', 'integer'),
7320  array($this->getTestId(), $user_id)
7321  );
7322  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_invited_user (test_fi, user_fi, clientip, tstamp) VALUES (%s, %s, %s, %s)",
7323  array('integer', 'integer', 'text', 'integer'),
7324  array($this->getTestId(), $user_id, (strlen($client_ip)) ? $client_ip : NULL, time())
7325  );
7326  }
7327 
7328 
7329  function setClientIP($user_id, $client_ip)
7330  {
7331  global $ilDB;
7332 
7333  $affectedRows = $ilDB->manipulateF("UPDATE tst_invited_user SET clientip = %s, tstamp = %s WHERE test_fi=%s and user_fi=%s",
7334  array('text', 'integer', 'integer', 'integer'),
7335  array((strlen($client_ip)) ? $client_ip : NULL, time(), $this->getTestId(), $user_id)
7336  );
7337  }
7338 
7344  function _getSolvedQuestions($active_id, $question_fi = null)
7345  {
7346  global $ilDB;
7347  if (is_numeric($question_fi))
7348  {
7349  $result = $ilDB->queryF("SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s AND question_fi=%s",
7350  array('integer', 'integer'),
7351  array($active_id, $question_fi)
7352  );
7353  }
7354  else
7355  {
7356  $result = $ilDB->queryF("SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s",
7357  array('integer'),
7358  array($active_id)
7359  );
7360  }
7361  $result_array = array();
7362  while ($row = $ilDB->fetchAssoc($result))
7363  {
7364  $result_array[$row["question_fi"]]= $row;
7365  }
7366  return $result_array;
7367  }
7368 
7369 
7373  function setQuestionSetSolved($value, $question_id, $user_id)
7374  {
7375  global $ilDB;
7376 
7377  $active_id = $this->getActiveIdOfUser($user_id);
7378  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s AND question_fi = %s",
7379  array('integer', 'integer'),
7380  array($active_id, $question_id)
7381  );
7382  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_qst_solved (solved, question_fi, active_fi) VALUES (%s, %s, %s)",
7383  array('integer', 'integer', 'integer'),
7384  array($value, $question_id, $active_id)
7385  );
7386  }
7387 
7388 
7392  function setActiveTestSubmitted($user_id)
7393  {
7394  global $ilDB, $ilLog;
7395 
7396  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s, tstamp = %s WHERE test_fi = %s AND user_fi = %s",
7397  array('integer', 'timestamp', 'integer', 'integer', 'integer'),
7398  array(1, date('Y-m-d H:i:s'), time(), $this->getTestId(), $user_id)
7399  );
7400  $this->testSession = NULL;
7401  }
7402 
7406  function isTestFinished($active_id)
7407  {
7408  global $ilDB;
7409 
7410  $result = $ilDB->queryF("SELECT submitted FROM tst_active WHERE active_id=%s AND submitted=%s",
7411  array('integer', 'integer'),
7412  array($active_id, 1)
7413  );
7414  return $result->numRows() == 1;
7415  }
7416 
7420  function isActiveTestSubmitted($user_id = null)
7421  {
7422  global $ilUser;
7423  global $ilDB;
7424 
7425  if (!is_numeric($user_id))
7426  $user_id = $ilUser->getId();
7427 
7428  $result = $ilDB->queryF("SELECT submitted FROM tst_active WHERE test_fi=%s AND user_fi=%s AND submitted=%s",
7429  array('integer', 'integer', 'integer'),
7430  array($this->getTestId(), $user_id, 1)
7431  );
7432  return $result->numRows() == 1;
7433  }
7434 
7439  {
7440  return $this->getNrOfTries() != 0;
7441  }
7442 
7443 
7448  function isNrOfTriesReached($tries)
7449  {
7450  return $tries >= (int) $this->getNrOfTries();
7451  }
7452 
7453 
7462  function getAllTestResults($participants, $prepareForCSV = true)
7463  {
7464  global $lng;
7465 
7466  $results = array();
7467  $row = array(
7468  "user_id" => $lng->txt("user_id"),
7469  "matriculation" => $lng->txt("matriculation"),
7470  "lastname" => $lng->txt("lastname"),
7471  "firstname" => $lng->txt("firstname"),
7472  "login" =>$lng->txt("login"),
7473  "reached_points" => $lng->txt("tst_reached_points"),
7474  "max_points" => $lng->txt("tst_maximum_points"),
7475  "percent_value" => $lng->txt("tst_percent_solved"),
7476  "mark" => $lng->txt("tst_mark"),
7477  "ects" => $lng->txt("ects_grade")
7478  );
7479  $results[] = $row;
7480  if (count($participants))
7481  {
7482  if ($this->ects_output)
7483  {
7484  $passed_array =& $this->getTotalPointsPassedArray();
7485  }
7486  foreach ($participants as $active_id => $user_rec)
7487  {
7488  $row = array();
7489  $reached_points = 0;
7490  $max_points = 0;
7491  foreach ($this->questions as $value)
7492  {
7493  $question =& ilObjTest::_instanciateQuestion($value);
7494  if (is_object($question))
7495  {
7496  $max_points += $question->getMaximumPoints();
7497  $reached_points += $question->getReachedPoints($active_id);
7498  }
7499  }
7500  if ($max_points > 0)
7501  {
7502  $percentvalue = $reached_points / $max_points;
7503  if ($percentvalue < 0) $percentvalue = 0.0;
7504  }
7505  else
7506  {
7507  $percentvalue = 0;
7508  }
7509  $mark_obj = $this->mark_schema->getMatchingMark($percentvalue * 100);
7510  $passed = "";
7511  if ($mark_obj)
7512  {
7513  $mark = $mark_obj->getOfficialName();
7514  $ects_mark = $this->getECTSGrade($passed_array, $reached_points, $max_points);
7515  }
7516  if ($this->getAnonymity())
7517  {
7518  $user_rec['firstname'] = "";
7519  $user_rec['lastname'] = $lng->txt("unknown");
7520  }
7521  $row = array(
7522  "user_id"=>$user_rec['usr_id'],
7523  "matriculation" => $user_rec['matriculation'],
7524  "lastname" => $user_rec['lastname'],
7525  "firstname" => $user_rec['firstname'],
7526  "login"=>$user_rec['login'],
7527  "reached_points" => $reached_points,
7528  "max_points" => $max_points,
7529  "percent_value" => $percentvalue,
7530  "mark" => $mark,
7531  "ects" => $ects_mark
7532  );
7533  $results[] = $prepareForCSV ? $this->processCSVRow ($row, true) : $row;
7534  }
7535  }
7536  return $results;
7537  }
7538 
7549  function &processCSVRow($row, $quoteAll = FALSE, $separator = ";")
7550  {
7551  $resultarray = array();
7552  foreach ($row as $rowindex => $entry)
7553  {
7554  $surround = FALSE;
7555  if ($quoteAll)
7556  {
7557  $surround = TRUE;
7558  }
7559  if (strpos($entry, "\"") !== FALSE)
7560  {
7561  $entry = str_replace("\"", "\"\"", $entry);
7562  $surround = TRUE;
7563  }
7564  if (strpos($entry, $separator) !== FALSE)
7565  {
7566  $surround = TRUE;
7567  }
7568  // replace all CR LF with LF (for Excel for Windows compatibility
7569  $entry = str_replace(chr(13).chr(10), chr(10), $entry);
7570  if ($surround)
7571  {
7572  $resultarray[$rowindex] = utf8_decode("\"" . $entry . "\"");
7573  }
7574  else
7575  {
7576  $resultarray[$rowindex] = utf8_decode($entry);
7577  }
7578  }
7579  return $resultarray;
7580  }
7581 
7590  function _getPass($active_id)
7591  {
7592  global $ilDB;
7593  $result = $ilDB->queryF("SELECT tries FROM tst_active WHERE active_id = %s",
7594  array('integer'),
7595  array($active_id)
7596  );
7597  if ($result->numRows())
7598  {
7599  $row = $ilDB->fetchAssoc($result);
7600  return $row["tries"];
7601  }
7602  else
7603  {
7604  return 0;
7605  }
7606  }
7607 
7617  function _getMaxPass($active_id)
7618  {
7619  global $ilDB;
7620  $result = $ilDB->queryF("SELECT MAX(pass) maxpass FROM tst_test_result WHERE active_fi = %s",
7621  array('integer'),
7622  array($active_id)
7623  );
7624  if ($result->numRows())
7625  {
7626  $row = $ilDB->fetchAssoc($result);
7627  $max = $row["maxpass"];
7628  }
7629  else
7630  {
7631  $max = NULL;
7632  }
7633  return $max;
7634  }
7635 
7644  function _getBestPass($active_id)
7645  {
7646  global $ilDB;
7647 
7648  $result = $ilDB->queryF("SELECT * FROM tst_pass_result WHERE active_fi = %s",
7649  array('integer'),
7650  array($active_id)
7651  );
7652  if ($result->numRows())
7653  {
7654  $bestrow = null;
7655  $bestpoints = -1;
7656  while ($row = $ilDB->fetchAssoc($result))
7657  {
7658  if ($row["points"] > $bestpoints)
7659  {
7660  $bestrow = $row;
7661  $bestpoints = $row["points"];
7662  }
7663  }
7664  if (is_array($bestrow))
7665  {
7666  return $bestrow["pass"];
7667  }
7668  else
7669  {
7670  return 0;
7671  }
7672  }
7673  else
7674  {
7675  return 0;
7676  }
7677  }
7678 
7687  function _getResultPass($active_id)
7688  {
7689  $counted_pass = NULL;
7690  if (ilObjTest::_getPassScoring($active_id) == SCORE_BEST_PASS)
7691  {
7692  $counted_pass = ilObjTest::_getBestPass($active_id);
7693  }
7694  else
7695  {
7696  $counted_pass = ilObjTest::_getMaxPass($active_id);
7697  }
7698  return $counted_pass;
7699  }
7700 
7710  function getAnsweredQuestionCount($active_id, $pass = NULL)
7711  {
7712  if ($this->isRandomTest())
7713  {
7714  $this->loadQuestions($active_id, $pass);
7715  }
7716  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7717  $workedthrough = 0;
7718  foreach ($this->questions as $value)
7719  {
7720  if (assQuestion::_isWorkedThrough($active_id, $value, $pass))
7721  {
7722  $workedthrough += 1;
7723  }
7724  }
7725  return $workedthrough;
7726  }
7727 
7737  function getPassFinishDate($active_id, $pass)
7738  {
7739  global $ilDB;
7740  if (is_null($pass)) $pass = 0;
7741  $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",
7742  array('integer', 'integer'),
7743  array($active_id, $pass)
7744  );
7745  if ($result->numRows())
7746  {
7747  $row = $ilDB->fetchAssoc($result);
7748  return $row["tstamp"];
7749  }
7750  else
7751  {
7752  return 0;
7753  }
7754  }
7755 
7763  function isExecutable($user_id, $allowPassIncrease = FALSE)
7764  {
7765  $result = array(
7766  "executable" => true,
7767  "errormessage" => ""
7768  );
7769  if (!$this->startingTimeReached())
7770  {
7771  $result["executable"] = false;
7772  $result["errormessage"] = sprintf($this->lng->txt("detail_starting_time_not_reached"), ilFormat::ftimestamp2datetimeDB($this->getStartingTime()));
7773  return $result;
7774  }
7775  if ($this->endingTimeReached())
7776  {
7777  $result["executable"] = false;
7778  $result["errormessage"] = sprintf($this->lng->txt("detail_ending_time_reached"), ilFormat::ftimestamp2datetimeDB($this->getEndingTime()));
7779  return $result;
7780  }
7781 
7782  $active_id = $this->getActiveIdOfUser($user_id);
7783 
7784  if ($this->getEnableProcessingTime())
7785  {
7786  if ($active_id > 0)
7787  {
7788  $starting_time = $this->getStartingTimeOfUser($active_id);
7789  if ($starting_time !== FALSE)
7790  {
7791  if ($this->isMaxProcessingTimeReached($starting_time, $active_id))
7792  {
7793  if ($allowPassIncrease && $this->getResetProcessingTime() && (($this->getNrOfTries() == 0) || ($this->getNrOfTries() > ($this->_getPass($active_id)+1))))
7794  {
7795  // a test pass was quitted because the maximum processing time was reached, but the time
7796  // will be resetted for future passes, so if there are more passes allowed, the participant may
7797  // start the test again.
7798  // This code block is only called when $allowPassIncrease is TRUE which only happens when
7799  // the test info page is opened. Otherwise this will lead to unexpected results!
7800  $this->getTestSession()->increasePass();
7801  $this->getTestSession()->setLastSequence(0);
7802  $this->getTestSession()->saveToDb();
7803  }
7804  else
7805  {
7806  $result["executable"] = false;
7807  $result["errormessage"] = $this->lng->txt("detail_max_processing_time_reached");
7808  }
7809  return $result;
7810  }
7811  }
7812  }
7813  }
7814 
7815  if ($this->hasNrOfTriesRestriction() && ($active_id > 0) && $this->isNrOfTriesReached($this->getTestSession($active_id)->getPass()))
7816  {
7817  $result["executable"] = false;
7818  $result["errormessage"] = $this->lng->txt("maximum_nr_of_tries_reached");
7819  return $result;
7820  }
7821 
7822  if ($this->getTestSession($active_id)->isSubmitted())
7823  {
7824  $result["executable"] = FALSE;
7825  $result["errormessage"] = $this->lng->txt("maximum_nr_of_tries_reached");
7826  return $result;
7827  }
7828 
7829  // TODO: max. processing time
7830 
7831  return $result;
7832  }
7833 
7840  function canViewResults()
7841  {
7842  $result = true;
7843  if ($this->getScoreReporting() == 4) return false;
7844  if ($this->getReportingDate())
7845  {
7846  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches))
7847  {
7848  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
7849  $now = mktime();
7850  if ($now < $epoch_time)
7851  {
7852  $result = false;
7853  }
7854  }
7855  }
7856  return $result;
7857  }
7858 
7859  function canShowTestResults($user_id)
7860  {
7861  $active_id = $this->getActiveIdOfUser($user_id);
7862  if ($active_id > 0)
7863  {
7864  $starting_time = $this->getStartingTimeOfUser($active_id);
7865  }
7866  $notimeleft = FALSE;
7867  if ($starting_time !== FALSE)
7868  {
7869  if ($this->isMaxProcessingTimeReached($starting_time, $active_id))
7870  {
7871  $notimeleft = TRUE;
7872  }
7873  }
7874  $result = TRUE;
7875  if (!$this->isTestFinishedToViewResults($active_id, $this->getTestSession($active_id)->getPass()) && ($this->getScoreReporting() == REPORT_AFTER_TEST))
7876  {
7877  $result = FALSE;
7878  }
7879  if (($this->endingTimeReached()) || $notimeleft) $result = TRUE;
7880  $result = $result & $this->canViewResults();
7881  return $result;
7882  }
7883 
7884  function canEditMarks()
7885  {
7886  $total = $this->evalTotalPersons();
7887  if ($total > 0)
7888  {
7889  if ($this->getReportingDate())
7890  {
7891  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches))
7892  {
7893  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
7894  $now = mktime();
7895  if ($now < $epoch_time)
7896  {
7897  return true;
7898  }
7899  }
7900  }
7901  return false;
7902  }
7903  else
7904  {
7905  return true;
7906  }
7907  }
7908 
7910  {
7911  global $ilDB;
7912 
7913  $times = array();
7914  $result = $ilDB->query("SELECT tst_times.active_fi, tst_times.started FROM tst_times, tst_active WHERE tst_times.active_fi = tst_active.active_id ORDER BY tst_times.tstamp DESC");
7915  while ($row = $ilDB->fetchAssoc($result))
7916  {
7917  $times[$row['active_fi']] = $row['started'];
7918  }
7919  return $times;
7920  }
7921 
7923  {
7924  global $ilDB;
7925 
7926  $times = array();
7927  $result = $ilDB->queryF("SELECT tst_addtime.active_fi, tst_addtime.additionaltime FROM tst_addtime, tst_active WHERE tst_addtime.active_fi = tst_active.active_id AND tst_active.test_fi = %s",
7928  array('integer'),
7929  array($this->getTestId())
7930  );
7931  while ($row = $ilDB->fetchAssoc($result))
7932  {
7933  $times[$row['active_fi']] = $row['additionaltime'];
7934  }
7935  return $times;
7936  }
7937 
7945  function getStartingTimeOfUser($active_id)
7946  {
7947  global $ilDB;
7948 
7949  if ($active_id < 1) return FALSE;
7950  $pass = ($this->getResetProcessingTime()) ? $this->_getPass($active_id) : 0;
7951  $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",
7952  array('integer', 'integer'),
7953  array($active_id, $pass)
7954  );
7955  if ($result->numRows())
7956  {
7957  $row = $ilDB->fetchAssoc($result);
7958  if (preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches))
7959  {
7960  return mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
7961  }
7962  else
7963  {
7964  return mktime();
7965  }
7966  }
7967  else
7968  {
7969  return mktime();
7970  }
7971  }
7972 
7982  {
7983  if ($this->getEnableProcessingTime())
7984  {
7985  $processing_time = $this->getProcessingTimeInSeconds($active_id);
7986  $now = mktime();
7987  if ($now > ($starting_time + $processing_time))
7988  {
7989  return TRUE;
7990  }
7991  else
7992  {
7993  return FALSE;
7994  }
7995  }
7996  else
7997  {
7998  return FALSE;
7999  }
8000  }
8001 
8002  function &getTestQuestions()
8003  {
8004  global $ilDB;
8005  $query_result = $ilDB->queryF("SELECT qpl_questions.*, qpl_qst_type.type_tag, tst_test_question.sequence FROM qpl_questions, qpl_qst_type, tst_test_question WHERE qpl_questions.question_type_fi = qpl_qst_type.question_type_id AND tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id ORDER BY tst_test_question.sequence",
8006  array('integer'),
8007  array($this->getTestId())
8008  );
8009  $removableQuestions = array();
8010  while ($row = $ilDB->fetchAssoc($query_result))
8011  {
8012  array_push($removableQuestions, $row);
8013  }
8014  return $removableQuestions;
8015  }
8016 
8024  {
8025  return ($this->shuffle_questions) ? 1 : 0;
8026  }
8027 
8034  function setShuffleQuestions($a_shuffle)
8035  {
8036  $this->shuffle_questions = ($a_shuffle) ? 1 : 0;
8037  }
8038 
8052  {
8053  return ($this->show_summary) ? $this->show_summary : 0;
8054  }
8055 
8068  function setListOfQuestionsSettings($a_value = 0)
8069  {
8070  $this->show_summary = $a_value;
8071  }
8072 
8080  {
8081  if (($this->show_summary & 1) > 0)
8082  {
8083  return TRUE;
8084  }
8085  else
8086  {
8087  return FALSE;
8088  }
8089  }
8090 
8097  function setListOfQuestions($a_value = TRUE)
8098  {
8099  if ($a_value)
8100  {
8101  $this->show_summary = 1;
8102  }
8103  else
8104  {
8105  $this->show_summary = 0;
8106  }
8107  }
8108 
8116  {
8117  if (($this->show_summary & 2) > 0)
8118  {
8119  return TRUE;
8120  }
8121  else
8122  {
8123  return FALSE;
8124  }
8125  }
8126 
8133  function setListOfQuestionsStart($a_value = TRUE)
8134  {
8135  if ($a_value && $this->getListOfQuestions())
8136  {
8137  $this->show_summary = $this->show_summary | 2;
8138  }
8139  if (!$a_value && $this->getListOfQuestions())
8140  {
8141  if ($this->getListOfQuestionsStart())
8142  {
8143  $this->show_summary = $this->show_summary ^ 2;
8144  }
8145  }
8146  }
8147 
8155  {
8156  if (($this->show_summary & 4) > 0)
8157  {
8158  return TRUE;
8159  }
8160  else
8161  {
8162  return FALSE;
8163  }
8164  }
8165 
8172  function setListOfQuestionsEnd($a_value = TRUE)
8173  {
8174  if ($a_value && $this->getListOfQuestions())
8175  {
8176  $this->show_summary = $this->show_summary | 4;
8177  }
8178  if (!$a_value && $this->getListOfQuestions())
8179  {
8180  if ($this->getListOfQuestionsEnd())
8181  {
8182  $this->show_summary = $this->show_summary ^ 4;
8183  }
8184  }
8185  }
8186 
8194  {
8195  if (($this->show_summary & 8) > 0)
8196  {
8197  return TRUE;
8198  }
8199  else
8200  {
8201  return FALSE;
8202  }
8203  }
8204 
8211  function setListOfQuestionsDescription($a_value = TRUE)
8212  {
8213  if ($a_value && $this->getListOfQuestions())
8214  {
8215  $this->show_summary = $this->show_summary | 8;
8216  }
8217  if (!$a_value && $this->getListOfQuestions())
8218  {
8219  if ($this->getListOfQuestionsDescription())
8220  {
8221  $this->show_summary = $this->show_summary ^ 8;
8222  }
8223  }
8224  }
8225 
8233  {
8234  return ($this->results_presentation) ? $this->results_presentation : 0;
8235  }
8236 
8244  {
8245  if (($this->results_presentation & 1) > 0)
8246  {
8247  return TRUE;
8248  }
8249  else
8250  {
8251  return FALSE;
8252  }
8253  }
8254 
8262  {
8263  if (($this->results_presentation & 2) > 0)
8264  {
8265  return TRUE;
8266  }
8267  else
8268  {
8269  return FALSE;
8270  }
8271  }
8272 
8280  {
8281  if (($this->results_presentation & 4) > 0)
8282  {
8283  return TRUE;
8284  }
8285  else
8286  {
8287  return FALSE;
8288  }
8289  }
8290 
8298  {
8299  if (($this->results_presentation & 8) > 0)
8300  {
8301  return TRUE;
8302  }
8303  else
8304  {
8305  return FALSE;
8306  }
8307  }
8308 
8316  {
8317  if (($this->results_presentation & 16) > 0)
8318  {
8319  return TRUE;
8320  }
8321  else
8322  {
8323  return FALSE;
8324  }
8325  }
8326 
8334  {
8335  if (($this->results_presentation & 32) > 0)
8336  {
8337  return TRUE;
8338  }
8339  else
8340  {
8341  return FALSE;
8342  }
8343  }
8344 
8350  {
8351  if (($this->results_presentation & 64) > 0)
8352  {
8353  return TRUE;
8354  }
8355  else
8356  {
8357  return FALSE;
8358  }
8359  }
8360 
8366  {
8367  if (($this->results_presentation & 128) > 0)
8368  {
8369  return TRUE;
8370  }
8371  else
8372  {
8373  return FALSE;
8374  }
8375  }
8376 
8382  {
8383  if (($this->results_presentation & 256) > 0)
8384  {
8385  return TRUE;
8386  }
8387  else
8388  {
8389  return FALSE;
8390  }
8391  }
8392 
8399  function setResultsPresentation($a_results_presentation = 3)
8400  {
8401  $this->results_presentation = $a_results_presentation;
8402  }
8403 
8412  function setShowPassDetails($a_details = 1)
8413  {
8414  if ($a_details)
8415  {
8416  $this->results_presentation = $this->results_presentation | 1;
8417  }
8418  else
8419  {
8420  if ($this->getShowPassDetails())
8421  {
8422  $this->results_presentation = $this->results_presentation ^ 1;
8423  }
8424  }
8425  }
8426 
8433  function setShowSolutionDetails($a_details = 1)
8434  {
8435  if ($a_details)
8436  {
8437  $this->results_presentation = $this->results_presentation | 2;
8438  }
8439  else
8440  {
8441  if ($this->getShowSolutionDetails())
8442  {
8443  $this->results_presentation = $this->results_presentation ^ 2;
8444  }
8445  }
8446  }
8447 
8454  function setShowSolutionPrintview($a_printview = 1)
8455  {
8456  if ($a_printview)
8457  {
8458  $this->results_presentation = $this->results_presentation | 4;
8459  }
8460  else
8461  {
8462  if ($this->getShowSolutionPrintview())
8463  {
8464  $this->results_presentation = $this->results_presentation ^ 4;
8465  }
8466  }
8467  }
8468 
8475  function setShowSolutionFeedback($a_feedback = TRUE)
8476  {
8477  if ($a_feedback)
8478  {
8479  $this->results_presentation = $this->results_presentation | 8;
8480  }
8481  else
8482  {
8483  if ($this->getShowSolutionFeedback())
8484  {
8485  $this->results_presentation = $this->results_presentation ^ 8;
8486  }
8487  }
8488  }
8489 
8496  function setShowSolutionAnswersOnly($a_full = TRUE)
8497  {
8498  if ($a_full)
8499  {
8500  $this->results_presentation = $this->results_presentation | 16;
8501  }
8502  else
8503  {
8504  if ($this->getShowSolutionAnswersOnly())
8505  {
8506  $this->results_presentation = $this->results_presentation ^ 16;
8507  }
8508  }
8509  }
8510 
8517  function setShowSolutionSignature($a_signature = FALSE)
8518  {
8519  if ($a_signature)
8520  {
8521  $this->results_presentation = $this->results_presentation | 32;
8522  }
8523  else
8524  {
8525  if ($this->getShowSolutionSignature())
8526  {
8527  $this->results_presentation = $this->results_presentation ^ 32;
8528  }
8529  }
8530  }
8531 
8538  function setShowSolutionSuggested($a_solution = FALSE)
8539  {
8540  if ($a_solution)
8541  {
8542  $this->results_presentation = $this->results_presentation | 64;
8543  }
8544  else
8545  {
8546  if ($this->getShowSolutionSuggested())
8547  {
8548  $this->results_presentation = $this->results_presentation ^ 64;
8549  }
8550  }
8551  }
8552 
8559  function setShowSolutionListComparison($a_comparison = FALSE)
8560  {
8561  if ($a_comparison)
8562  {
8563  $this->results_presentation = $this->results_presentation | 128;
8564  }
8565  else
8566  {
8567  if ($this->getShowSolutionListComparison())
8568  {
8569  $this->results_presentation = $this->results_presentation ^ 128;
8570  }
8571  }
8572  }
8573 
8580  function setShowSolutionAnwersBeforeFinish($a_value = FALSE)
8581  {
8582  if ($a_value)
8583  {
8584  $this->results_presentation = $this->results_presentation | 256;
8585  }
8586  else
8587  {
8588  if ($this->getShowSolutionAnwersBeforeFinish())
8589  {
8590  $this->results_presentation = $this->results_presentation ^ 256;
8591  }
8592  }
8593  }
8594 
8601  {
8602  // create a 5 character code
8603  $codestring = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
8604  mt_srand();
8605  $code = "";
8606  for ($i = 1; $i <=5; $i++)
8607  {
8608  $index = mt_rand(0, strlen($codestring)-1);
8609  $code .= substr($codestring, $index, 1);
8610  }
8611  // verify it against the database
8612  while ($this->isAccessCodeUsed($code))
8613  {
8614  $code = $this->createNewAccessCode();
8615  }
8616  return $code;
8617  }
8618 
8619  function isAccessCodeUsed($code)
8620  {
8621  global $ilDB;
8622 
8623  $result = $ilDB->queryF("SELECT anonymous_id FROM tst_active WHERE test_fi = %s AND anonymous_id = %s",
8624  array('integer', 'text'),
8625  array($this->getTestId(), $code)
8626  );
8627  return ($result->numRows() > 0) ? true : false;
8628  }
8629 
8630  public static function _getUserIdFromActiveId($active_id)
8631  {
8632  global $ilDB;
8633  $result = $ilDB->queryF("SELECT user_fi FROM tst_active WHERE active_id = %s",
8634  array('integer'),
8635  array($active_id)
8636  );
8637  if ($result->numRows())
8638  {
8639  $row = $ilDB->fetchAssoc($result);
8640  return $row["user_fi"];
8641  }
8642  else
8643  {
8644  return -1;
8645  }
8646  }
8647 
8649  {
8650  $id = $this->getTestId();
8651  if (!is_array($_SESSION["tst_access_code"]))
8652  {
8653  return "";
8654  }
8655  else
8656  {
8657  return $_SESSION["tst_access_code"]["$id"];
8658  }
8659  }
8660 
8661  function setAccessCodeSession($access_code)
8662  {
8663  $id = $this->getTestId();
8664  if (!is_array($_SESSION["tst_access_code"]))
8665  {
8666  $_SESSION["tst_access_code"] = array();
8667  }
8668  $_SESSION["tst_access_code"]["$id"] = $access_code;
8669  }
8670 
8672  {
8673  $id = $this->getTestId();
8674  unset($_SESSION["tst_access_code"]["$id"]);
8675  }
8676 
8677  function getAllowedUsers()
8678  {
8679  return ($this->allowedUsers) ? $this->allowedUsers : 0;
8680  }
8681 
8682  function setAllowedUsers($a_allowed_users)
8683  {
8684  $this->allowedUsers = $a_allowed_users;
8685  }
8686 
8688  {
8689  return ($this->allowedUsersTimeGap) ? $this->allowedUsersTimeGap : 0;
8690  }
8691 
8692  function setAllowedUsersTimeGap($a_allowed_users_time_gap)
8693  {
8694  $this->allowedUsersTimeGap = $a_allowed_users_time_gap;
8695  }
8696 
8698  {
8699  global $ilDB;
8700 
8701  $nr_of_users = $this->getAllowedUsers();
8702  $time_gap = ($this->getAllowedUsersTimeGap()) ? $this->getAllowedUsersTimeGap() : 60;
8703  if (($nr_of_users > 0) && ($time_gap > 0))
8704  {
8705  $now = mktime();
8706  $time_border = $now - $time_gap;
8707  $str_time_border = strftime("%Y%m%d%H%M%S", $time_border);
8708  $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",
8709  array('integer', 'integer'),
8710  array($time_border, $this->getTestId())
8711  );
8712  if ($result->numRows() >= $nr_of_users)
8713  {
8714  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
8716  {
8717  $this->logAction($this->lng->txtlng("assessment", "log_could_not_enter_test_due_to_simultaneous_users", ilObjAssessmentFolder::_getLogLanguage()));
8718  }
8719  return FALSE;
8720  }
8721  else
8722  {
8723  return TRUE;
8724  }
8725  }
8726  return TRUE;
8727  }
8728 
8729  function _getLastAccess($active_id)
8730  {
8731  global $ilDB;
8732 
8733  $result = $ilDB->queryF("SELECT finished FROM tst_times WHERE active_fi = %s ORDER BY finished DESC",
8734  array('integer'),
8735  array($active_id)
8736  );
8737  if ($result->numRows())
8738  {
8739  $row = $ilDB->fetchAssoc($result);
8740  return $row["finished"];
8741  }
8742  return "";
8743  }
8744 
8752  function isHTML($a_text)
8753  {
8754  if (preg_match("/<[^>]*?>/", $a_text))
8755  {
8756  return TRUE;
8757  }
8758  else
8759  {
8760  return FALSE;
8761  }
8762  }
8763 
8771  function QTIMaterialToString($a_material)
8772  {
8773  $result = "";
8774  for ($i = 0; $i < $a_material->getMaterialCount(); $i++)
8775  {
8776  $material = $a_material->getMaterial($i);
8777  if (strcmp($material["type"], "mattext") == 0)
8778  {
8779  $result .= $material["material"]->getContent();
8780  }
8781  if (strcmp($material["type"], "matimage") == 0)
8782  {
8783  $matimage = $material["material"];
8784  if (preg_match("/(il_([0-9]+)_mob_([0-9]+))/", $matimage->getLabel(), $matches))
8785  {
8786  // import an mediaobject which was inserted using tiny mce
8787  if (!is_array($_SESSION["import_mob_xhtml"])) $_SESSION["import_mob_xhtml"] = array();
8788  array_push($_SESSION["import_mob_xhtml"], array("mob" => $matimage->getLabel(), "uri" => $matimage->getUri()));
8789  }
8790  }
8791  }
8792  global $ilLog;
8793  $ilLog->write(print_r($_SESSION["import_mob_xhtml"], true));
8794  return $result;
8795  }
8796 
8803  function prepareTextareaOutput($txt_output, $prepare_for_latex_output = FALSE)
8804  {
8805  include_once "./Services/Utilities/classes/class.ilUtil.php";
8806  return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output);
8807  }
8808 
8815  function saveCertificateVisibility($a_value)
8816  {
8817  global $ilDB;
8818 
8819  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET certificate_visibility = %s, tstamp = %s WHERE test_id = %s",
8820  array('text', 'integer', 'integer'),
8821  array($a_value, time(), $this->getTestId())
8822  );
8823  }
8824 
8832  {
8833  return (strlen($this->certificate_visibility)) ? $this->certificate_visibility : 0;
8834  }
8835 
8842  function setCertificateVisibility($a_value)
8843  {
8844  $this->certificate_visibility = $a_value;
8845  }
8846 
8853  function getAnonymity()
8854  {
8855  return ($this->anonymity) ? 1 : 0;
8856  }
8857 
8864  function setAnonymity($a_value = 0)
8865  {
8866  switch ($a_value)
8867  {
8868  case 1:
8869  $this->anonymity = 1;
8870  break;
8871  default:
8872  $this->anonymity = 0;
8873  break;
8874  }
8875  }
8876 
8883  function getShowCancel()
8884  {
8885  return ($this->show_cancel) ? 1 : 0;
8886  }
8887 
8894  function setShowCancel($a_value = 1)
8895  {
8896  switch ($a_value)
8897  {
8898  case 1:
8899  $this->show_cancel = 1;
8900  break;
8901  default:
8902  $this->show_cancel = 0;
8903  break;
8904  }
8905  }
8906 
8913  function getAutosave()
8914  {
8915  return ($this->autosave) ? 1 : 0;
8916  }
8917 
8924  function setAutosave($a_value = 1)
8925  {
8926  switch ($a_value)
8927  {
8928  case 1:
8929  $this->autosave = 1;
8930  break;
8931  default:
8932  $this->autosave = 0;
8933  break;
8934  }
8935  }
8936 
8943  function getShowMarker()
8944  {
8945  return ($this->show_marker) ? 1 : 0;
8946  }
8947 
8954  function setShowMarker($a_value = 1)
8955  {
8956  switch ($a_value)
8957  {
8958  case 1:
8959  $this->show_marker = 1;
8960  break;
8961  default:
8962  $this->show_marker = 0;
8963  break;
8964  }
8965  }
8966 
8974  {
8975  return ($this->fixed_participants) ? 1 : 0;
8976  }
8977 
8984  function setFixedParticipants($a_value = 1)
8985  {
8986  switch ($a_value)
8987  {
8988  case 1:
8989  $this->fixed_participants = 1;
8990  break;
8991  default:
8992  $this->fixed_participants = 0;
8993  break;
8994  }
8995  }
8996 
9004  function _lookupAnonymity($a_obj_id)
9005  {
9006  global $ilDB;
9007 
9008  $result = $ilDB->queryF("SELECT anonymity FROM tst_tests WHERE obj_fi = %s",
9009  array('integer'),
9010  array($a_obj_id)
9011  );
9012  while($row = $ilDB->fetchAssoc($result))
9013  {
9014  return $row['anonymity'];
9015  }
9016  return 0;
9017  }
9018 
9026  function _lookupRandomTestFromActiveId($active_id)
9027  {
9028  global $ilDB;
9029 
9030  $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",
9031  array('integer'),
9032  array($active_id)
9033  );
9034  while($row = $ilDB->fetchAssoc($result))
9035  {
9036  return $row['random_test'];
9037  }
9038  return 0;
9039  }
9040 
9049  function userLookupFullName($user_id, $overwrite_anonymity = FALSE, $sorted_order = FALSE, $suffix = "")
9050  {
9051  global $lng;
9052 
9053  if ($this->getAnonymity() && !$overwrite_anonymity)
9054  {
9055  return $lng->txt("unknown") . $suffix;
9056  }
9057  else
9058  {
9059  include_once './Services/User/classes/class.ilObjUser.php';
9060  $uname = ilObjUser::_lookupName($user_id);
9061  if (strlen($uname["firstname"].$uname["lastname"]) == 0) $uname["firstname"] = $lng->txt("deleted_user");
9062  if ($sorted_order)
9063  {
9064  return trim($uname["lastname"] . ", " . $uname["firstname"]) . $suffix;
9065  }
9066  else
9067  {
9068  return trim($uname["firstname"] . " " . $uname["lastname"]) . $suffix;
9069  }
9070  }
9071  }
9072 
9080  function getStartTestLabel($active_id)
9081  {
9082  //if ($this->getNrOfTries() == 1)
9083  //{
9084  // return $this->lng->txt("tst_start_test");
9085  //}
9086  $active_pass = $this->_getPass($active_id);
9087  $res = $this->getNrOfResultsForPass($active_id, $active_pass);
9088  if ($res == 0)
9089  {
9090  if ($active_pass == 0)
9091  {
9092  return $this->lng->txt("tst_start_test");
9093  }
9094  else
9095  {
9096  return $this->lng->txt("tst_start_new_test_pass");
9097  }
9098  }
9099  else
9100  {
9101  return $this->lng->txt("tst_resume_test");
9102  }
9103  }
9104 
9113  function &getAvailableDefaults($sortby = "name", $sortorder = "asc")
9114  {
9115  global $ilDB;
9116  global $ilUser;
9117 
9118  $result = $ilDB->queryF("SELECT * FROM tst_test_defaults WHERE user_fi = %s ORDER BY $sortby $sortorder",
9119  array('integer'),
9120  array($ilUser->getId())
9121  );
9122  $defaults = array();
9123  while ($row = $ilDB->fetchAssoc($result))
9124  {
9125  $defaults[$row["test_defaults_id"]] = $row;
9126  }
9127  return $defaults;
9128  }
9129 
9137  function &getTestDefaults($test_defaults_id)
9138  {
9139  global $ilDB;
9140 
9141  $result = $ilDB->queryF("SELECT * FROM tst_test_defaults WHERE test_defaults_id = %s",
9142  array('integer'),
9143  array($test_defaults_id)
9144  );
9145  if ($result->numRows() == 1)
9146  {
9147  $row = $ilDB->fetchAssoc($result);
9148  return $row;
9149  }
9150  else
9151  {
9152  return NULL;
9153  }
9154  }
9155 
9162  function deleteDefaults($test_default_id)
9163  {
9164  global $ilDB;
9165  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_defaults WHERE test_defaults_id = %s",
9166  array('integer'),
9167  array($test_default_id)
9168  );
9169  }
9170 
9177  function addDefaults($a_name)
9178  {
9179  global $ilDB;
9180  global $ilUser;
9181  $testsettings = array(
9182  "TitleOutput" => $this->getTitleOutput(),
9183  "PassScoring" => $this->getPassScoring(),
9184  "Introduction" => $this->getIntroduction(),
9185  "FinalStatement" => $this->getFinalStatement(),
9186  /* Change Sn */
9187  "RedirectAfterExam" => $this->getRedirectAfterExam(),
9188  "RedirectOnlyKioskMode" => $this->getRedirectOnlyKioskMode(),
9189  "ExamPdf" => $this->getExamPdf(),
9190  /* Change Sn End */
9191  "ShowInfo" => $this->getShowInfo(),
9192  "ForceJS" => $this->getForceJS(),
9193  "CustomStyle" => $this->getCustomStyle(),
9194  "ShowFinalStatement" => $this->getShowFinalStatement(),
9195  "SequenceSettings" => $this->getSequenceSettings(),
9196  "ScoreReporting" => $this->getScoreReporting(),
9197  "InstantFeedbackSolution" => $this->getInstantFeedbackSolution(),
9198  "AnswerFeedback" => $this->getAnswerFeedback(),
9199  "AnswerFeedbackPoints" => $this->getAnswerFeedbackPoints(),
9200  "ResultsPresentation" => $this->getResultsPresentation(),
9201  "Anonymity" => $this->getAnonymity(),
9202  "ShowCancel" => $this->getShowCancel(),
9203  "Autosave" => $this->getAutosave(),
9204  "ShowMarker" => $this->getShowMarker(),
9205  "ReportingDate" => $this->getReportingDate(),
9206  "NrOfTries" => $this->getNrOfTries(),
9207  "Shuffle" => $this->getShuffleQuestions(),
9208  "Kiosk" => $this->getKiosk(),
9209  "Serial" => $this->getShowSerial(),
9210  "Shuffle" => $this->getShuffleQuestions(),
9211  "UsePreviousAnswers" => $this->getUsePreviousAnswers(),
9212  "ProcessingTime" => $this->getProcessingTime(),
9213  "EnableProcessingTime" => $this->getEnableProcessingTime(),
9214  "ResetProcessingTime" => $this->getResetProcessingTime(),
9215  "StartingTime" => $this->getStartingTime(),
9216  "EndingTime" => $this->getEndingTime(),
9217  "ECTSOutput" => $this->getECTSOutput(),
9218  "ECTSFX" => $this->getECTSFX(),
9219  "ECTSGrades" => $this->getECTSGrades(),
9220  "isRandomTest" => $this->isRandomTest(),
9221  "RandomQuestionCount" => $this->getRandomQuestionCount(),
9222  "CountSystem" => $this->getCountSystem(),
9223  "MCScoring" => $this->getMCScoring(),
9224  "mailnotification" => $this->getMailNotification(),
9225  "mailnottype" => $this->getMailNotificationType(),
9226  "exportsettings" => $this->getExportSettings(),
9227  "ListOfQuestionsSettings" => $this->getListOfQuestionsSettings()
9228  );
9229  $next_id = $ilDB->nextId('tst_test_defaults');
9230  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_defaults (test_defaults_id, name, user_fi, defaults, marks, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
9231  array('integer', 'text', 'integer', 'text', 'text', 'integer'),
9232  array($next_id, $a_name, $ilUser->getId(), serialize($testsettings), serialize($this->mark_schema), time())
9233  );
9234  }
9235 
9243  function applyDefaults($test_defaults_id)
9244  {
9245  $total = $this->evalTotalPersons();
9246  $result = FALSE;
9247  if (($this->getQuestionCount() == 0) && ($total == 0))
9248  {
9249  // only apply if there are no questions added and not user datasets exist
9250  $defaults =& $this->getTestDefaults($test_defaults_id);
9251  $testsettings = unserialize($defaults["defaults"]);
9252  include_once "./Modules/Test/classes/class.assMarkSchema.php";
9253  $this->mark_schema = unserialize($defaults["marks"]);
9254  $this->setTitleOutput($testsettings["TitleOutput"]);
9255  $this->setPassScoring($testsettings["PassScoring"]);
9256  $this->setIntroduction($testsettings["Introduction"]);
9257  $this->setFinalStatement($testsettings["FinalStatement"]);
9258  /* Change Sn */
9259  $this->setRedirectAfterExam($testsettings["RedirectAfterExam"]);
9260  $this->setRedirectOnlyKioskMode($testsettings["RedirectOnlyKioskMode"]);
9261  $this->setExamPdf($testsettings["ExamPdf"]);
9262  /* Change Sn End */
9263  $this->setShowInfo($testsettings["ShowInfo"]);
9264  $this->setForceJS($testsettings["ForceJS"]);
9265  $this->setCustomStyle($testsettings["CustomStyle"]);
9266  $this->setShowFinalStatement($testsettings["ShowFinalStatement"]);
9267  $this->setSequenceSettings($testsettings["SequenceSettings"]);
9268  $this->setScoreReporting($testsettings["ScoreReporting"]);
9269  $this->setInstantFeedbackSolution($testsettings["InstantFeedbackSolution"]);
9270  $this->setAnswerFeedback($testsettings["AnswerFeedback"]);
9271  $this->setAnswerFeedbackPoints($testsettings["AnswerFeedbackPoints"]);
9272  $this->setResultsPresentation($testsettings["ResultsPresentation"]);
9273  $this->setAnonymity($testsettings["Anonymity"]);
9274  $this->setShowCancel($testsettings["ShowCancel"]);
9275  $this->setAutosave($testsettings["Autosave"]);
9276  $this->setShowMarker($testsettings["ShowMarker"]);
9277  $this->setReportingDate($testsettings["ReportingDate"]);
9278  $this->setShuffleQuestions($testsettings["this"]);
9279  $this->setNrOfTries($testsettings["NrOfTries"]);
9280  $this->setUsePreviousAnswers($testsettings["UsePreviousAnswers"]);
9281  $this->setProcessingTime($testsettings["ProcessingTime"]);
9282  $this->setResetProcessingTime($testsettings["ResetProcessingTime"]);
9283  $this->setShuffleQuestions($testsettings["Shuffle"]);
9284  $this->setEnableProcessingTime($testsettings["EnableProcessingTime"]);
9285  $this->setStartingTime($testsettings["StartingTime"]);
9286  $this->setKiosk($testsettings["Kiosk"]);
9287  $this->setShowSerial($testsettings["Serial"]);
9288  $this->setEndingTime($testsettings["EndingTime"]);
9289  $this->setECTSOutput($testsettings["ECTSOutput"]);
9290  $this->setECTSFX($testsettings["ECTSFX"]);
9291  $this->setECTSGrades($testsettings["ECTSGrades"]);
9292  $this->setRandomTest($testsettings["isRandomTest"]);
9293  $this->setRandomQuestionCount($testsettings["RandomQuestionCount"]);
9294  $this->setCountSystem($testsettings["CountSystem"]);
9295  $this->setMCScoring($testsettings["MCScoring"]);
9296  $this->setMailNotification($testsettings["mailnotification"]);
9297  $this->setMailNotificationType($testsettings["mailnottype"]);
9298  $this->setExportSettings($testsettings['exportsettings']);
9299  $this->setListOfQuestionsSettings($testsettings["ListOfQuestionsSettings"]);
9300  $this->saveToDb();
9301  $result = TRUE;
9302  }
9303  return $result;
9304  }
9305 
9312  public function deliverPDFFromHTML($content, $title = NULL, $filename = NULL)
9313  {
9314  global $ilLog;
9315  $content = preg_replace("/href=\".*?\"/", "", $content);
9316  $printbody = new ilTemplate("tpl.il_as_tst_print_body.html", TRUE, TRUE, "Modules/Test");
9317  $printbody->setVariable("TITLE", ilUtil::prepareFormOutput($this->getTitle()));
9318  $printbody->setVariable("ADM_CONTENT", $content);
9319  $printbody->setCurrentBlock("css_file");
9320  $printbody->setVariable("CSS_FILE", $this->getTestStyleLocation("filesystem"));
9321  $printbody->parseCurrentBlock();
9322  $printbody->setCurrentBlock("css_file");
9323  $printbody->setVariable("CSS_FILE", ilUtil::getStyleSheetLocation("filesystem", "delos.css"));
9324  $printbody->parseCurrentBlock();
9325  $printoutput = $printbody->get();
9326  $html = str_replace("href=\"./", "href=\"" . ILIAS_HTTP_PATH . "/", $printoutput);
9327  $html = str_replace("img src=\"./", "img src=\"" . ILIAS_HTTP_PATH . "/", $html);
9328  $html = preg_replace("/<div id=\"dontprint\">.*?<\\/div>/ims", "", $html);
9329  if (extension_loaded("tidy"))
9330  {
9331  $config = array(
9332  "indent" => false,
9333  "output-xml" => true,
9334  "numeric-entities" => true
9335  );
9336  $tidy = new tidy();
9337  $tidy->parseString($html, $config, 'utf8');
9338  $tidy->cleanRepair();
9339  $html = tidy_get_output($tidy);
9340  $html = preg_replace("/^.*?(<html)/", "\\1", $html);
9341  }
9342  else
9343  {
9344  $html = str_replace("&nbsp;", "&#160;", $html);
9345  $html = str_replace("&otimes;", "X", $html);
9346  }
9347  $title = ($title) ? $title : $this->getTitle();
9348 
9349 
9350  $active_id = $_GET["active_id"];
9351  include_once './Services/User/classes/class.ilObjUser.php';
9352  $user_id = $this->_getUserIdFromActiveId($active_id);
9353  $uname = ilObjUser::_lookupName($user_id);
9354  $filename_pdf = $uname['lastname']."_".$uname['firstname']."_".$uname['login']."_";
9355 
9356 
9357 
9358  $pdfSetting = new ilSetting("pdf");
9359  include_once "./Services/PDF/classes/class.ilHTMLToPDF.php";
9360  $transformer = null;
9361  switch ($pdfSetting->get('html_to_pdf_method'))
9362  {
9363  case 'fop':
9364  include_once "./Services/PDF/classes/class.ilHTMLToPDFTransformerUsingFOP.php";
9365  $transformer = new ilHTMLtoPDFTransformerUsingFOP($filename_pdf . $title . '.pdf');
9366  $transformer->xsl = "./Modules/Test/xml/question2fo.xsl";
9367  break;
9368  case 'webkit':
9369  include_once "./Services/PDF/classes/class.ilHTMLToPDFTransformerUsingWebkit.php";
9370  $transformer = new ilHTMLtoPDFTransformerUsingWebkit($filename_pdf . $title . '.pdf');
9371  break;
9372  }
9373  if (is_object($transformer))
9374  {
9375  $generator = new ilHTMLtoPDF($transformer);
9376  if ($filename)
9377  {
9378  $generator->createPDFFileFromHTMLString($html, $filename);
9379  }
9380  else
9381  {
9382  $generator->deliverPDFFromHTMLString($html);
9383  }
9384  }
9385  }
9386 
9396  static function getManualFeedback($active_id, $question_id, $pass)
9397  {
9398  global $ilDB;
9399  $feedback = "";
9400  $result = $ilDB->queryF("SELECT feedback FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
9401  array('integer', 'integer', 'integer'),
9402  array($active_id, $question_id, $pass)
9403  );
9404  if ($result->numRows())
9405  {
9406  $row = $ilDB->fetchAssoc($result);
9407  include_once("./Services/RTE/classes/class.ilRTE.php");
9408  $feedback = ilRTE::_replaceMediaObjectImageSrc($row["feedback"], 1);
9409  }
9410  return $feedback;
9411  }
9412 
9423  function saveManualFeedback($active_id, $question_id, $pass, $feedback)
9424  {
9425  global $ilDB;
9426 
9427  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
9428  array('integer', 'integer', 'integer'),
9429  array($active_id, $question_id, $pass)
9430  );
9431 
9432  if (strlen($feedback))
9433  {
9434  $next_id = $ilDB->nextId('tst_manual_fb');
9435  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_manual_fb (manual_feedback_id, active_fi, question_fi, pass, feedback, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
9436  array('integer', 'integer', 'integer', 'integer', 'text', 'integer'),
9437  array($next_id, $active_id, $question_id, $pass, ilRTE::_replaceMediaObjectImageSrc($feedback, 0), time())
9438  );
9439  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
9441  {
9442  global $lng, $ilUser;
9443  include_once "./Modules/Test/classes/class.ilObjTestAccess.php";
9444  $username = ilObjTestAccess::_getParticipantData($active_id);
9445  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
9446  $this->logAction(sprintf($lng->txtlng("assessment", "log_manual_feedback", ilObjAssessmentFolder::_getLogLanguage()), $ilUser->getFullname() . " (" . $ilUser->getLogin() . ")", $username, assQuestion::_getQuestionTitle($question_id), $feedback));
9447  }
9448  }
9449  if (PEAR::isError($result))
9450  {
9451  global $ilias;
9452  $ilias->raiseError($result->getMessage());
9453  }
9454  else
9455  {
9456  return TRUE;
9457  }
9458  }
9459 
9468  {
9469  global $ilUser;
9470  if (strcmp($_GET["tst_javascript"], "0") == 0) return FALSE;
9471  if ($this->getForceJS()) return TRUE;
9472  $assessmentSetting = new ilSetting("assessment");
9473  return ($ilUser->getPref("tst_javascript") === FALSE) ? $assessmentSetting->get("use_javascript") : $ilUser->getPref("tst_javascript");
9474  }
9475 
9482  function &createTestSession()
9483  {
9484  global $ilUser;
9485 
9486  include_once "./Modules/Test/classes/class.ilTestSession.php";
9487  $testSession = FALSE;
9488  $testSession = new ilTestSession();
9489  $testSession->setRefId($this->getRefId());
9490  $testSession->setTestId($this->getTestId());
9491  $testSession->setUserId($ilUser->getId());
9492  $testSession->setAnonymousId($_SESSION["tst_access_code"][$this->getTestId()]);
9493  $testSession->saveToDb();
9494  $this->testSession =& $testSession;
9495  return $this->testSession;
9496  }
9497 
9503  public function setTestId($a_id)
9504  {
9505  $this->test_id = $a_id;
9506  }
9507 
9515  function &setTestSession($active_id = "")
9516  {
9517  if (is_object($this->testSession) && ($this->testSession->getActiveId() > 0)) return $this->testSession;
9518 
9519  global $ilUser;
9520 
9521  include_once "./Modules/Test/classes/class.ilTestSession.php";
9522  $testSession = FALSE;
9523  if ($active_id > 0)
9524  {
9525  $testSession = new ilTestSession($active_id);
9526  $testSession->setRefId($this->getRefId());
9527  }
9528  else
9529  {
9530  $testSession = new ilTestSession();
9531  $testSession->setRefId($this->getRefId());
9532  $testSession->loadTestSession($this->getTestId(), $ilUser->getId(), $_SESSION["tst_access_code"][$this->getTestId()]);
9533  }
9534  $this->testSession =& $testSession;
9535  return $this->testSession;
9536  }
9537 
9544  function &getTestSession($active_id = "")
9545  {
9546  if (is_object($this->testSession) && ($this->testSession->getActiveId() > 0)) return $this->testSession;
9547  return $this->setTestSession($active_id);
9548  }
9549 
9550  function &createTestSequence($active_id, $pass, $shuffle)
9551  {
9552  include_once "./Modules/Test/classes/class.ilTestSequence.php";
9553  $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
9554  if (!$this->testSequence->hasSequence())
9555  {
9556  $this->testSequence->createNewSequence($this->getQuestionCount(), $shuffle);
9557  $this->testSequence->saveToDb();
9558  }
9559  }
9560 
9561  function &getTestSequence($active_id = "", $pass = "")
9562  {
9563  if (is_object($this->testSequence) && ($this->testSequence->getActiveId() > 0)) return $this->testSequence;
9564 
9565  include_once "./Modules/Test/classes/class.ilTestSequence.php";
9566  if (($active_id > 0) && (strlen($pass)))
9567  {
9568  $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
9569  }
9570  else
9571  {
9572  $this->testSequence = new ilTestSequence($this->getTestSession()->getActiveId(), $this->getTestSession()->getPass(), $this->isRandomTest());
9573  }
9574  return $this->testSequence;
9575  }
9576 
9578  {
9579  if ($this->getTestSession()->getActiveId() > 0)
9580  {
9581  $result = $this->getTestResult($this->getTestSession()->getActiveId(), $this->getTestSession()->getPass(), TRUE);
9582  foreach ($result as $sequence => $question)
9583  {
9584  if (is_numeric($sequence))
9585  {
9586  if ($question["reached"] == $question["max"])
9587  {
9588  $this->getTestSequence()->hideQuestion($question["qid"]);
9589  }
9590  }
9591  }
9592  $this->getTestSequence()->saveToDb();
9593  }
9594  }
9595 
9604  function getDetailedTestResults($participants)
9605  {
9606  global $lng;
9607 
9608  $results = array();
9609  if (count($participants))
9610  {
9611  foreach ($participants as $active_id => $user_rec)
9612  {
9613  $row = array();
9614  $reached_points = 0;
9615  $max_points = 0;
9616  foreach ($this->questions as $value)
9617  {
9618  $question =& ilObjTest::_instanciateQuestion($value);
9619  if (is_object($question))
9620  {
9621  $max_points += $question->getMaximumPoints();
9622  $reached_points += $question->getReachedPoints($active_id);
9623  if ($max_points > 0)
9624  {
9625  $percentvalue = $reached_points / $max_points;
9626  if ($percentvalue < 0) $percentvalue = 0.0;
9627  }
9628  else
9629  {
9630  $percentvalue = 0;
9631  }
9632  if ($this->getAnonymity())
9633  {
9634  $user_rec['firstname'] = "";
9635  $user_rec['lastname'] = $lng->txt("unknown");
9636  }
9637  $row = array(
9638  "user_id"=>$user_rec['usr_id'],
9639  "matriculation" => $user_rec['matriculation'],
9640  "lastname" => $user_rec['lastname'],
9641  "firstname" => $user_rec['firstname'],
9642  "login"=>$user_rec['login'],
9643  "question_id" => $question->getId(),
9644  "question_title" => $question->getTitle(),
9645  "reached_points" => $reached_points,
9646  "max_points" => $max_points
9647  );
9648  $results[] = $row;
9649  }
9650  }
9651  }
9652  }
9653  return $results;
9654  }
9655 
9660  {
9661  global $ilDB;
9662 
9663  $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",
9664  array('integer'),
9665  array($a_q_id)
9666  );
9667  $rec = $ilDB->fetchAssoc($result);
9668  return $rec["obj_id"];
9669  }
9670 
9677  function isPluginActive($a_pname)
9678  {
9679  global $ilPluginAdmin;
9680  if ($ilPluginAdmin->isActive(IL_COMP_MODULE, "TestQuestionPool", "qst", $a_pname))
9681  {
9682  return TRUE;
9683  }
9684  else
9685  {
9686  return FALSE;
9687  }
9688  }
9689 
9696  {
9697  $assessmentSetting = new ilSetting("assessment");
9698  $assessmentSetting->set("evalFields_" . $this->getId(), serialize($fields));
9699  }
9700 
9707  {
9708  include_once "./Modules/Test/classes/class.ilObjTestGUI.php";
9709  include_once "./Modules/Test/classes/tables/class.ilEvaluationAllTableGUI.php";
9710  $table_gui = new ilEvaluationAllTableGUI(new ilObjTestGUI(), 'outEvaluation');
9711  return $table_gui->getSelectedColumns();
9712  }
9713 
9714  protected function getPassed($active_id)
9715  {
9716  global $ilDB;
9717 
9718  $result = $ilDB->queryF("SELECT passed FROM tst_result_cache WHERE active_fi = %s",
9719  array('integer'),
9720  array($active_id)
9721  );
9722  if ($result->numRows())
9723  {
9724  $row = $ilDB->fetchAssoc($result);
9725  return $row['passed'];
9726  }
9727  else
9728  {
9729  $counted_pass = ilObjTest::_getResultPass($active_id);
9730  $result_array =& $this->getTestResult($active_id, $counted_pass);
9731  return $result_array["test"]["passed"];
9732  }
9733  }
9734 
9740  function canShowCertificate($user_id, $active_id)
9741  {
9742  if ($this->canShowTestResults($user_id))
9743  {
9744  include_once "./Services/Certificate/classes/class.ilCertificate.php";
9745  include_once "./Modules/Test/classes/class.ilTestCertificateAdapter.php";
9746  $cert = new ilCertificate(new ilTestCertificateAdapter($this));
9747  if ($cert->isComplete())
9748  {
9749  $vis = $this->getCertificateVisibility();
9750  $showcert = FALSE;
9751  switch ($vis)
9752  {
9753  case 0:
9754  $showcert = TRUE;
9755  break;
9756  case 1:
9757  if ($this->getPassed($active_id))
9758  {
9759  $showcert = TRUE;
9760  }
9761  break;
9762  case 2:
9763  $showcert = FALSE;
9764  break;
9765  }
9766  if ($showcert)
9767  {
9768  return TRUE;
9769  }
9770  else
9771  {
9772  return FALSE;
9773  }
9774  }
9775  else
9776  {
9777  return FALSE;
9778  }
9779  }
9780  else
9781  {
9782  return FALSE;
9783  }
9784  }
9785 
9792  {
9793  global $ilDB;
9794  /*$result = $ilDB->queryF("SELECT tst_test_result.active_fi, tst_test_result.question_fi, tst_test_result.pass FROM tst_test_result, tst_active, qpl_questions WHERE tst_active.active_id = tst_test_result.active_fi AND tst_active.test_fi = %s AND tst_test_result.question_fi = qpl_questions.question_id AND qpl_questions.original_id = %s",
9795  array('integer', 'integer'),
9796  array($test_id, $question_id)
9797  );*/
9798  $result = $ilDB->queryF("SELECT tst_test_result.active_fi, tst_test_result.question_fi, tst_test_result.pass FROM tst_test_result, tst_active, usr_data WHERE tst_active.active_id = tst_test_result.active_fi AND tst_active.test_fi = %s AND tst_test_result.question_fi = %s AND tst_active.user_fi=usr_data.usr_id ORDER BY usr_data.lastname ASC",
9799  array('integer', 'integer'),
9800  array($test_id, $question_id)
9801  );
9802  $foundusers = array();
9803  while ($row = $ilDB->fetchAssoc($result))
9804  {
9805  if (!array_key_exists($row["active_fi"], $foundusers))
9806  {
9807  $foundusers[$row["active_fi"]] = array();
9808  }
9809  array_push($foundusers[$row["active_fi"]], array("pass" => $row["pass"], "qid" => $row["question_fi"]));
9810  }
9811  return $foundusers;
9812  }
9813 
9819  public function getAggregatedResultsData()
9820  {
9821  $data =& $this->getCompleteEvaluationData();
9822  $foundParticipants =& $data->getParticipants();
9823  $results = array("overview" => array(), "questions" => array());
9824  if (count($foundParticipants))
9825  {
9826  $results["overview"][$this->lng->txt("tst_eval_total_persons")] = count($foundParticipants);
9827  $total_finished = $this->evalTotalFinished();
9828  $results["overview"][$this->lng->txt("tst_eval_total_finished")] = $total_finished;
9829  $average_time = $this->evalTotalStartedAverageTime();
9830  $diff_seconds = $average_time;
9831  $diff_hours = floor($diff_seconds/3600);
9832  $diff_seconds -= $diff_hours * 3600;
9833  $diff_minutes = floor($diff_seconds/60);
9834  $diff_seconds -= $diff_minutes * 60;
9835  $results["overview"][$this->lng->txt("tst_eval_total_finished_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds);
9836  $total_passed = 0;
9837  $total_passed_reached = 0;
9838  $total_passed_max = 0;
9839  $total_passed_time = 0;
9840  foreach ($foundParticipants as $userdata)
9841  {
9842  if ($userdata->getPassed())
9843  {
9844  $total_passed++;
9845  $total_passed_reached += $userdata->getReached();
9846  $total_passed_max += $userdata->getMaxpoints();
9847  $total_passed_time += $userdata->getTimeOfWork();
9848  }
9849  }
9850  $average_passed_reached = $total_passed ? $total_passed_reached / $total_passed : 0;
9851  $average_passed_max = $total_passed ? $total_passed_max / $total_passed : 0;
9852  $average_passed_time = $total_passed ? $total_passed_time / $total_passed : 0;
9853  $results["overview"][$this->lng->txt("tst_eval_total_passed")] = $total_passed;
9854  $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);
9855  $average_time = $average_passed_time;
9856  $diff_seconds = $average_time;
9857  $diff_hours = floor($diff_seconds/3600);
9858  $diff_seconds -= $diff_hours * 3600;
9859  $diff_minutes = floor($diff_seconds/60);
9860  $diff_seconds -= $diff_minutes * 60;
9861  $results["overview"][$this->lng->txt("tst_eval_total_passed_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds);
9862  }
9863 
9864  foreach ($data->getQuestionTitles() as $question_id => $question_title)
9865  {
9866  $answered = 0;
9867  $reached = 0;
9868  $max = 0;
9869  foreach ($foundParticipants as $userdata)
9870  {
9871  for ($i = 0; $i <= $userdata->getLastPass(); $i++)
9872  {
9873  if (is_object($userdata->getPass($i)))
9874  {
9875  $question =& $userdata->getPass($i)->getAnsweredQuestionByQuestionId($question_id);
9876  if (is_array($question))
9877  {
9878  $answered++;
9879  $reached += $question["reached"];
9880  $max += $question["points"];
9881  }
9882  }
9883  }
9884  }
9885  $percent = $max ? $reached/$max * 100.0 : 0;
9886  $counter++;
9887  $results["questions"][$question_id] = array(
9888  $question_title,
9889  sprintf("%.2f", $answered ? $reached / $answered : 0) . " " . strtolower($this->lng->txt("of")) . " " . sprintf("%.2f", $answered ? $max / $answered : 0),
9890  sprintf("%.2f", $percent) . "%",
9891  $answered,
9892  sprintf("%.2f", $answered ? $reached / $answered : 0),
9893  sprintf("%.2f", $answered ? $max / $answered : 0),
9894  $percent / 100.0
9895  );
9896  }
9897  return $results;
9898  }
9899 
9903  function getXMLZip()
9904  {
9905  include_once("./Modules/Test/classes/class.ilTestExport.php");
9906  $test_exp = new ilTestExport($this, "xml");
9907  return $test_exp->buildExportFile();
9908  }
9909 
9913  public function getMailNotification()
9914  {
9915  return $this->mailnotification;
9916  }
9917 
9923  public function setMailNotification($a_notification)
9924  {
9925  $this->mailnotification = $a_notification;
9926  }
9927 
9928  public function sendSimpleNotification($active_id)
9929  {
9930  include_once "./Services/Mail/classes/class.ilMail.php";
9931  $mail = new ilMail(ANONYMOUS_USER_ID);
9932 
9933  $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id));
9934  $message = new ilTemplate("tpl.il_as_tst_finish_notification_simple.html", TRUE, TRUE, "Modules/Test");
9935  $message->setVariable('TEXT_TEST_TITLE', $this->lng->txt('title'));
9936  $message->setVariable('VALUE_TEST_TITLE', $this->getTitle());
9937  $message->setVariable('TEXT_USER_NAME', $this->lng->txt('username'));
9938  $message->setVariable('VALUE_USER_NAME', $usr_data);
9939  $message->setVariable('TEXT_FINISH_TIME', $this->lng->txt('tst_finished'));
9941  $message->setVariable('VALUE_FINISH_TIME', ilDatePresentation::formatDate(new ilDateTime(time(), IL_CAL_UNIX)));
9942 
9943  $res = $mail->sendMail(
9944  ilObjUser::_lookupLogin($this->getOwner()), // to
9945  "", // cc
9946  "", // bcc
9947  sprintf($this->lng->txt('tst_user_finished_test'), $this->getTitle()), // subject
9948  $message->get(), // message
9949  array(), // attachments
9950  array('normal') // type
9951  );
9952  global $ilLog; $ilLog->write("sending mail: " . $res);
9953  }
9954 
9955 
9956  public function sendAdvancedNotification($active_id)
9957  {
9958  include_once "./Services/Mail/classes/class.ilMail.php";
9959  $mail = new ilMail(ANONYMOUS_USER_ID);
9960 
9961  $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id));
9962  $message = new ilTemplate("tpl.il_as_tst_finish_notification_simple.html", TRUE, TRUE, "Modules/Test");
9963  $message->setVariable('TEXT_TEST_TITLE', $this->lng->txt('title'));
9964  $message->setVariable('VALUE_TEST_TITLE', $this->getTitle());
9965  $message->setVariable('TEXT_USER_NAME', $this->lng->txt('username'));
9966  $message->setVariable('VALUE_USER_NAME', $usr_data);
9967  $message->setVariable('TEXT_FINISH_TIME', $this->lng->txt('tst_finished'));
9969  $message->setVariable('VALUE_FINISH_TIME', ilDatePresentation::formatDate(new ilDateTime(time(), IL_CAL_UNIX)));
9970 
9971  include_once "./Modules/Test/classes/class.ilTestExport.php";
9972  $exportObj = new ilTestExport($this, "results");
9973  $file = $exportObj->exportToExcel($deliver = FALSE, 'active_id', $active_id, $passedonly = FALSE);
9974  include_once "./classes/class.ilFileDataMail.php";
9975  $fd = new ilFileDataMail(ANONYMOUS_USER_ID);
9976  $fd->copyAttachmentFile($file, "result_" . $active_id . ".xls");
9977  $file_names[] = "result_" . $active_id . ".xls";
9978  $result = $mail->sendMail(
9979  ilObjUser::_lookupLogin($this->getOwner()), // to
9980  "", // cc
9981  "", // bcc
9982  sprintf($this->lng->txt('tst_user_finished_test'), $this->getTitle()), // subject
9983  $message->get(), // message
9984  count($file_names) ? $file_names : array(), // attachments
9985  array('normal') // type
9986  );
9987  if(count($file_names))
9988  {
9989  $fd->unlinkFiles($file_names);
9990  unset($fd);
9991  @unlink($file);
9992  }
9993  }
9994 
9995  function createRandomSolutions($number)
9996  {
9997  global $ilDB;
9998 
9999  // 1. get a user
10000  $query = "SELECT usr_id FROM usr_data";
10001  $result = $ilDB->query($query);
10002  while ($data = $ilDB->fetchAssoc($result))
10003  {
10004  $activequery = sprintf("SELECT user_fi FROM tst_active WHERE test_fi = %s AND user_fi = %s",
10005  $ilDB->quote($this->getTestId()),
10006  $ilDB->quote($data['usr_id'])
10007  );
10008  $activeresult = $ilDB->query($activequery);
10009  if ($activeresult->numRows() == 0)
10010  {
10011  $user_id = $data['usr_id'];
10012  if ($user_id != 13)
10013  {
10014  include_once "./Modules/Test/classes/class.ilTestSession.php";
10015  $testSession = FALSE;
10016  $testSession = new ilTestSession();
10017  $testSession->setRefId($this->getRefId());
10018  $testSession->setTestId($this->getTestId());
10019  $testSession->setUserId($user_id);
10020  $testSession->saveToDb();
10021  $passes = ($this->getNrOfTries()) ? $this->getNrOfTries() : 10;
10022  $nr_of_passes = rand(1, $passes);
10023  $active_id = $testSession->getActiveId();
10024  for ($pass = 0; $pass < $nr_of_passes; $pass++)
10025  {
10026  include_once "./Modules/Test/classes/class.ilTestSequence.php";
10027  $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
10028  if (!$testSequence->hasSequence())
10029  {
10030  $testSequence->createNewSequence($this->getQuestionCount(), $shuffle);
10031  $testSequence->saveToDb();
10032  }
10033  for ($seq = 1; $seq <= count($this->questions); $seq++)
10034  {
10035  $question_id = $testSequence->getQuestionForSequence($seq);
10036  $objQuestion = ilObjTest::_instanciateQuestion($question_id);
10037  $objQuestion->createRandomSolution($testSession->getActiveId(), $pass);
10038  }
10039  if ($pass < $nr_of_passes - 1)
10040  {
10041  $testSession->increasePass();
10042  $testSession->setLastSequence(0);
10043  $testSession->saveToDb();
10044  }
10045  else
10046  {
10047  $this->setActiveTestSubmitted($user_id);
10048  }
10049  }
10050  $number--;
10051  if ($number == 0) return;
10052  }
10053  }
10054  }
10055  }
10056 
10057  public function getResultsForActiveId($active_id)
10058  {
10059  global $ilDB;
10060 
10061  $result = $ilDB->queryF("SELECT * FROM tst_result_cache WHERE active_fi = %s",
10062  array('integer'),
10063  array($active_id)
10064  );
10065  if (!$result->numRows())
10066  {
10067  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
10069  $result = $ilDB->queryF("SELECT * FROM tst_result_cache WHERE active_fi = %s",
10070  array('integer'),
10071  array($active_id)
10072  );
10073  }
10074  $row = $ilDB->fetchAssoc($result);
10075  return $row;
10076 
10077  }
10078 
10084  function getRunDates()
10085  {
10086  global $ilLog;
10087  $metafiles = glob(ARCHIVE_HTML_DIR . "/*.metafile");
10088  $ilLog->write("Metafiles: ".print_r($metafiles,true));
10089  $testid = $this->getTestId();
10090  $dates = array();
10091  foreach ($metafiles as $metafile)
10092  {
10093  if (preg_match('/(\d\d\d\d-\d\d-\d\d)\_' . $testid . '/',$metafile, $matches))
10094  {
10095  $dates[] = $matches[1];
10096  }
10097  }
10098 
10099  return $dates;
10100  }
10101 
10108  {
10109  $dates_in = $this->getRunDates();
10110  $dates_out = array();
10111  $testid = $this->getTestId();
10112 
10113  foreach($dates_in as $date)
10114  {
10115  if (file_exists(ARCHIVE_PDF_DIR . "/" . $date . '_' . $testid . '.zip'))
10116  {
10117  $dates_out[] = $date;
10118  }
10119  }
10120 
10121  return $dates_out;
10122  }
10123 
10124  /* TODO: the following prototypes make it abundantly clear that I should really
10125  be developing an ilTestRun class */
10126 
10132  function hasRun($date)
10133  {
10134  // TODO: check $date syntax?
10135 
10136  $dates = $this->getRunDates();
10137  return in_array($date, $dates, TRUE);
10138  }
10139 
10145  function hasRunPackage($date)
10146  {
10147  // TODO: check $date syntax?
10148 
10149  $dates = $this->getRunPackageDates();
10150  return in_array($date, $dates, TRUE);
10151  }
10152 
10153 
10160  function packageRun($date)
10161  {
10162  global $ilLog;
10163 
10164  if (!preg_match("/^\d\d\d\d-\d\d-\d\d$/", $date))
10165  {
10166  $ilLog->write("$date doesn't seem to be a date in YYYY-MM-DD format");
10167  return false;
10168  }
10169 
10170  // okay, first check if we have results for this date...
10171  if (!($this->hasRun($date)))
10172  {
10173  $ilLog->write("no run for $date");
10174  return false;
10175  }
10176  if ($this->hasRunPackage($date))
10177  {
10178  $ilLog->write("run for $date already packaged, returning");
10179  // returning true is okay - run has already been packaged
10180  return true;
10181  }
10182  $testid = $this->getTestId();
10183  $archive_id = $date . "_" . $testid;
10184 
10185  $html_files = glob(ARCHIVE_HTML_DIR . "/" . $archive_id . "_*.html");
10186  if(empty($html_files))
10187  {
10188  $ilLog->write("No HTML archives found for run");
10189  return false;
10190  }
10191 
10192  // next up: create target directory for PDF versions of HTML run archives
10193  // TODO make paths configurable
10194  $pdfdir = ARCHIVE_PDF_DIR . "/" . $archive_id;
10195  if (file_exists($pdfdir))
10196  {
10197  if (!is_dir($pdfdir))
10198  {
10199  /* quietly clobber (nobody has any business dropping files with
10200  * archive ID names in the output directory)
10201  */
10202  if(!unlink($pdfdir))
10203  {
10204  $ilLog->write("Could not delete bogus file blocking location for PDF output directory ($pdfdir)");
10205  return false;
10206  }
10207  }
10208  else
10209  {
10210  $ilLog->write("PDF output directory for run already exists");
10211  return false;
10212  }
10213  }
10214  // TODO make mode, user and group configurable?
10215  $oldumask = umask(0);
10216  if (!mkdir($pdfdir, 0755))
10217  {
10218  $ilLog->write("Could not create PDF output directory for run");
10219  return false;
10220  }
10221  umask($oldumask);
10222 
10223  // let's get ourselves a PDF generator
10224  $pdfSetting = new ilSetting("pdf");
10225  include_once "./Services/PDF/classes/class.ilHTMLToPDF.php";
10226  $transformer = null;
10227  switch ($pdfSetting->get('html_to_pdf_method'))
10228  {
10229  case 'fop':
10230  include_once "./Services/PDF/classes/class.ilHTMLToPDFTransformerUsingFOP.php";
10231  $transformer = new ilHTMLtoPDFTransformerUsingFOP($pdf_file);
10232  $transformer->xsl = "./Modules/Test/xml/question2fo.xsl";
10233  break;
10234  case 'webkit':
10235  include_once "./Services/PDF/classes/class.ilHTMLToPDFTransformerUsingWebkit.php";
10236  $transformer = new ilHTMLtoPDFTransformerUsingWebkit($pdf_file);
10237  break;
10238  }
10239  if (is_object($transformer))
10240  {
10241  $generator = new ilHTMLtoPDF($transformer);
10242  }
10243  else
10244  {
10245  $ilLog->write("Could not generate PDF transformer");
10246  return false;
10247  }
10248 
10249 
10250  // create PDF files and key file lines
10251  $keylines = array();
10252  $pdf_filenames = array();
10253  $pdf_filelocalnames = array();
10254  foreach($html_files as $html_filename)
10255  {
10256  $html_file_parts = pathinfo($html_filename);
10257  $pdf_file_basename = $html_file_parts['filename'] . ".pdf";
10258  $pdf_filename = $pdfdir . "/" . $pdf_file_basename;
10259  $pdf_filelocalname = $archive_id . "/" . $pdf_file_basename;
10260  $ilLog->write("Converting HTML file ".$html_filename." into PDF file ".$pdf_filename);
10261  $generator->createPDFFileFromHTMLFile($html_filename, $pdf_filename);
10262  $keylines[] = (hash('sha512', file_get_contents($pdf_filename))." ".$pdf_file_basename);
10263  $pdf_filenames[] = $pdf_filename;
10264  $pdf_filelocalnames[] = $pdf_filelocalname;
10265  }
10266 
10267  // write key file
10268  $keyfilename = $pdfdir . "/" . $archive_id . ".key";
10269  $keyfilelocalname = $archive_id . "/" . $archive_id . ".key";
10270  $keyfile = fopen($keyfilename, 'w');
10271  if (!$keyfile)
10272  {
10273  $ilLog->write("Could not write key file");
10274  return false;
10275  }
10276  foreach ($keylines as $line)
10277  {
10278  fwrite($keyfile, $line . "\n");
10279  }
10280  fclose($keyfile);
10281 
10282  $metafile_basename = $archive_id . ".metafile";
10283  $metafile_source = ARCHIVE_HTML_DIR . "/" . $metafile_basename;
10284  $metafile_target = $pdfdir . "/" . $metafile_basename;
10285  $metafile_localname = $archive_id . "/" . $metafile_basename;
10286  if (!copy($metafile_source, $metafile_target))
10287  {
10288  $ilLog->write("Could not copy $metafile_source to $metafile_target");
10289  return false;
10290  }
10291 
10292  include_once "./Modules/Test/classes/class.ilTestExport.php";
10293  $exporter = new ilTestExport($this, "results");
10294  $excelfile = $exporter->exportToExcel($deliver = FALSE);
10295  $excelfile_target = $pdfdir . "/" . $archive_id . "_results.xls";
10296  $excelfile_localname = $archive_id . "/" . $archive_id . "_results.xls";
10297  if (!rename($excelfile, $excelfile_target))
10298  {
10299  $ilLog->write("Could not move $excelfile to $excelfile_target");
10300  return false;
10301  }
10302 
10303  // ****************************************************************************
10304  // BEGIN ugly code to get scoring template
10305  // ****************************************************************************
10306  $this->lng = new ilLanguage($_SESSION['lang']);
10307  $this->lng->loadLanguageModule("assessment");
10308  $scoring_template = $pdfdir . "/" . $archive_id . "_scoring.pdf";
10309  $scoring_template_localname = $archive_id . "/" . $archive_id . "_scoring.pdf";
10310 
10311  // the following is lifted straight from ilObjTestGUI::printobject()
10312  // ugly, but I wasn't able to figure out how to create an ilObjTestGUI
10313  // and tell it to print itself from outside an interactive HTTP session
10314  //$this->getQuestionsSubTabs();
10315  $template = new ilTemplate("tpl.il_as_tst_print_test_confirm.html", TRUE, TRUE, "Modules/Test");
10316 
10317  // $this->ctrl->setParameter($this, "pdf", "1");
10318  $template->setCurrentBlock("pdf_export");
10319  // $template->setVariable("PDF_URL", $this->ctrl->getLinkTarget($this, "print"));
10320  // $this->ctrl->setParameter($this, "pdf", "");
10321  $template->setVariable("PDF_TEXT", $this->lng->txt("pdf_export"));
10322  $template->setVariable("PDF_IMG_ALT", $this->lng->txt("pdf_export"));
10323  $template->setVariable("PDF_IMG_URL", ilUtil::getHtmlPath(ilUtil::getImagePath("application-pdf.png")));
10324  $template->parseCurrentBlock();
10325 
10326  $template->addCss(ilUtil::getStyleSheetLocation("output", "test_print.css", "Modules/Test"), "print");
10327 
10328  $print_date = date("c"); // ISO 8601 timestamp FTW
10329  $max_points= 0;
10330  $counter = 1;
10331 
10332  foreach ($this->questions as $question)
10333  {
10334  $template->setCurrentBlock("question");
10335  $question_gui = $this->createQuestionGUI("", $question);
10336  $template->setVariable("COUNTER_QUESTION", $counter.".");
10337  $template->setVariable("QUESTION_ID", $question_gui->object->getId());
10338  $template->setVariable("QUESTION_TITLE", ilUtil::prepareFormOutput($question_gui->object->getTitle()));
10339  if ($question_gui->object->getMaximumPoints() == 1)
10340  {
10341  $template->setVariable("QUESTION_POINTS", $question_gui->object->getMaximumPoints() . " " . $this->lng->txt("point"));
10342  }
10343  else
10344  {
10345  $template->setVariable("QUESTION_POINTS", $question_gui->object->getMaximumPoints() . " " . $this->lng->txt("points"));
10346  }
10347  $result_output = $question_gui->getSolutionOutput("", NULL, FALSE, TRUE, FALSE, $this->getShowSolutionFeedback());
10348  if (strlen($result_output) == 0) $result_output = $question_gui->getPreview(FALSE);
10349  $result_html = str_replace("src=\"../","src=\"" . ILIAS_HTTP_PATH . "/",$result_output);
10350  $template->setVariable("SOLUTION_OUTPUT", $result_html);
10351  $template->parseCurrentBlock("question");
10352  $counter ++;
10353  $max_points += $question_gui->object->getMaximumPoints();
10354  }
10355 
10356  // $template->setCurrentBlock("navigation_buttons");
10357  // $template->setVariable("BUTTON_PRINT", $this->lng->txt("print"));
10358  // $template->parseCurrentBlock();
10359 
10360  $template->setVariable("TITLE", ilUtil::prepareFormOutput($this->getTitle()));
10361  $template->setVariable("PRINT_TEST", ilUtil::prepareFormOutput($this->lng->txt("tst_print")));
10362  $template->setVariable("TXT_PRINT_DATE", ilUtil::prepareFormOutput($this->lng->txt("date")));
10363  $template->setVariable("VALUE_PRINT_DATE", ilUtil::prepareFormOutput($print_date));
10364  $template->setVariable("TXT_MAXIMUM_POINTS", ilUtil::prepareFormOutput($this->lng->txt("tst_maximum_points")));
10365  $template->setVariable("VALUE_MAXIMUM_POINTS", ilUtil::prepareFormOutput($max_points));
10366 
10367  $this->deliverPDFFromHTML($template->get(), $this->getTitle(), $scoring_template);
10368  // ****************************************************************************
10369  // END ugly code to get scoring template
10370  // ****************************************************************************
10371 
10372  // now create the archive
10373  $zip = new ZipArchive();
10374  $zipfilebasename = $archive_id . ".zip";
10375  $zipfilename = ARCHIVE_PDF_DIR . "/" . $zipfilebasename;
10376  if (TRUE !== $zip->open($zipfilename, ZIPARCHIVE::CREATE))
10377  {
10378  $ilLog->write("Could not create ZIP file $zipfilename");
10379  return false;
10380  }
10381  for ($i=0; $i<count($pdf_filenames); $i++)
10382  {
10383  $zip->addFile($pdf_filenames[$i], $pdf_filelocalnames[$i]);
10384  }
10385  $zip->addFile($keyfilename, $keyfilelocalname);
10386  $zip->addFile($metafile_target, $metafile_localname);
10387  $zip->addFile($excelfile_target, $excelfile_localname);
10388  $zip->addFile($scoring_template, $scoring_template_localname);
10389  $zip->close();
10390 
10391  return true;
10392  }
10393 
10394  /* returns filename of run package for date, or '' if none exists */
10395 
10396  function getRunPackageFilename($date)
10397  {
10398  if ($this->hasRunPackage($date))
10399  {
10400  $testid = $this->getTestId();
10401  return ARCHIVE_PDF_DIR . "/" . $date . '_' . $testid . '.zip';
10402  }
10403  else
10404  {
10405  return '';
10406  }
10407  }
10408 
10409  function deleteRunPackage($date)
10410  {
10411  $packagefilename = $this->getRunPackageFilename($date);
10412  if ('' === $packagefilename)
10413  {
10414  return false;
10415  }
10416  else
10417  {
10418  unlink ($packagefilename);
10419 
10420  // cut suffix off to make directory name
10421  $pdfdirname = substr($packagefilename, 0, -4);
10422  // recursively delete directory and contents
10423  // I can't believe there is no library function for this. PHP FTW.
10424  $diriterator = new RecursiveDirectoryIterator($pdfdirname);
10425  $diriterator->setFlags(RecursiveDirectoryIterator::SKIP_DOTS);
10426  $iterator =
10427  new RecursiveIteratorIterator($diriterator,
10428  RecursiveIteratorIterator::CHILD_FIRST);
10429  foreach ($iterator as $file)
10430  {
10431  if ($file->isDir())
10432  {
10433  rmdir($file->getPathname());
10434  }
10435  else
10436  {
10437  unlink($file->getPathname());
10438  }
10439  }
10440  rmdir($pdfdirname);
10441 
10442  // TODO there should be some kind of error handling
10443  return true;
10444  }
10445  }
10446 
10452  function obliterateRun($date)
10453  {
10454  global $ilLog;
10455 
10456  if (!preg_match("/^\d\d\d\d-\d\d-\d\d$/", $date))
10457  {
10458  $ilLog->write("$date doesn't seem to be a date in YYYY-MM-DD format");
10459  return false;
10460  }
10461 
10462  // okay, first check if we have results for this date...
10463  if (!($this->hasRun($date)))
10464  {
10465  $ilLog->write("no run for $date");
10466  return false;
10467  }
10468  if ($this->hasRunPackage($date))
10469  {
10470  $ilLog->write("run for $date has package, deleting it");
10471  $this->deleteRunPackage($date);
10472  }
10473  $testid = $this->getTestId();
10474  $archive_id = $date . "_" . $testid;
10475 
10476  $files_to_delete = glob(ARCHIVE_HTML_DIR . "/" . $archive_id . "_*.html");
10477  $files_to_delete[] = ARCHIVE_HTML_DIR . "/" . $archive_id . ".metafile";
10478  if(empty($files_to_delete))
10479  {
10480  $ilLog->write("No HTML archives or metafile found for run");
10481  return false;
10482  }
10483  else
10484  {
10485  foreach ($files_to_delete as $file_to_delete)
10486  {
10487  unlink($file_to_delete);
10488  }
10489  }
10490  return true;
10491  }
10492 
10493  public function getMailNotificationType()
10494  {
10495  if ($this->mailnottype == 1)
10496  {
10497  return $this->mailnottype;
10498  }
10499  else
10500  {
10501  return 0;
10502  }
10503  }
10504 
10505  public function setMailNotificationType($a_type)
10506  {
10507  if ($a_type == 1)
10508  {
10509  $this->mailnottype = 1;
10510  }
10511  else
10512  {
10513  $this->mailnottype = 0;
10514  }
10515  }
10516 
10517  public function getExportSettings()
10518  {
10519  if ($this->exportsettings)
10520  {
10521  return $this->exportsettings;
10522  }
10523  else
10524  {
10525  return 0;
10526  }
10527  }
10528 
10529  public function setExportSettings($a_settings)
10530  {
10531  if ($a_settings)
10532  {
10533  $this->exportsettings = $a_settings;
10534  }
10535  else
10536  {
10537  $this->exportsettings = 0;
10538  }
10539  }
10540 
10542  {
10543  if (($this->exportsettings & 1) > 0)
10544  {
10545  return true;
10546  }
10547  else
10548  {
10549  return false;
10550  }
10551  }
10552 
10553  public function setExportSettingsSingleChoiceShort($a_settings)
10554  {
10555  if ($a_settings)
10556  {
10557  $this->exportsettings = $this->exportsettings | 1;
10558  }
10559  else
10560  {
10562  {
10563  $this->exportsettings = $this->exportsettings ^ 1;
10564  }
10565  }
10566  }
10567 
10569  {
10570  if (($this->exportsettings & 2) > 0)
10571  {
10572  return true;
10573  }
10574  else
10575  {
10576  return false;
10577  }
10578  }
10579 
10580  public function setExportSettingsRespectShuffle($a_settings)
10581  {
10582  if ($a_settings)
10583  {
10584  $this->exportsettings = $this->exportsettings | 2;
10585  }
10586  else
10587  {
10588  if ($this->getExportSettingsRespectShuffle())
10589  {
10590  $this->exportsettings = $this->exportsettings ^ 2;
10591  }
10592  }
10593  }
10594 
10595  public function getQuestions()
10596  {
10597  return $this->questions;
10598  }
10599 
10600  public function getExtraTime($active_id)
10601  {
10602  global $ilDB;
10603 
10604  $result = $ilDB->queryF("SELECT additionaltime FROM tst_addtime WHERE active_fi = %s",
10605  array('integer'),
10606  array($active_id)
10607  );
10608  if ($result->numRows() > 0)
10609  {
10610  $row = $ilDB->fetchAssoc($result);
10611  return $row['additionaltime'];
10612  }
10613  return 0;
10614  }
10615 
10616  public function addExtraTime($active_id, $minutes)
10617  {
10618  global $ilDB;
10619 
10620  $participants = array();
10621  if ($active_id == 0)
10622  {
10623  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s",
10624  array('integer'),
10625  array($this->getTestId())
10626  );
10627  while ($row = $ilDB->fetchAssoc($result))
10628  {
10629  array_push($participants, $row['active_id']);
10630  }
10631  }
10632  else
10633  {
10634  array_push($participants, $active_id);
10635  }
10636  foreach ($participants as $active_id)
10637  {
10638  $result = $ilDB->queryF("SELECT active_fi FROM tst_addtime WHERE active_fi = %s",
10639  array('integer'),
10640  array($active_id)
10641  );
10642  if ($result->numRows() > 0)
10643  {
10644  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_addtime WHERE active_fi = %s",
10645  array('integer'),
10646  array($active_id)
10647  );
10648  }
10649 
10650  $result = $ilDB->manipulateF("UPDATE tst_active SET tries = %s, submitted = %s, submittimestamp = %s WHERE active_id = %s",
10651  array('integer','integer','timestamp','integer'),
10652  array(0, 0, NULL, $active_id)
10653  );
10654 
10655  $result = $ilDB->manipulateF("INSERT INTO tst_addtime (active_fi, additionaltime, tstamp) VALUES (%s, %s, %s)",
10656  array('integer','integer','integer'),
10657  array($active_id, $minutes, time())
10658  );
10659 
10661  {
10662  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_added_extratime", ilObjAssessmentFolder::_getLogLanguage()), $minutes, $active_id));
10663  }
10664  }
10665  }
10666 
10667  public function recalculateSolutions()
10668  {
10669  $participants = $this->getCompleteEvaluationData(false)->getParticipants();
10670  if (is_array($participants))
10671  {
10672  require_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
10673  foreach ($participants as $active_id => $userdata)
10674  {
10675  if (is_object($userdata) && is_array($userdata->getPasses()))
10676  {
10677  $passes = $userdata->getPasses();
10678  foreach ($passes as $pass => $passdata)
10679  {
10680  if (is_object($passdata))
10681  {
10682  $questions = $passdata->getAnsweredQuestions();
10683  if (is_array($questions))
10684  {
10685  foreach ($questions as $questiondata)
10686  {
10687  $question_gui = $this->createQuestionGUI("", $questiondata['id']);
10688  if (is_object($question_gui))
10689  {
10690  $reached = $question_gui->object->calculateReachedPoints($active_id, $pass);
10691  assQuestion::_setReachedPoints($active_id, $questiondata['id'], $reached, $question_gui->object->getMaximumPoints(), $pass, false, true);
10692  }
10693  }
10694  }
10695  }
10696  }
10697  }
10698  }
10699  }
10700  }
10701 
10702  public function getShowSerial()
10703  {
10704  return ($this->show_serial) ? 1 : 0;
10705  }
10706 
10707  public function setShowSerial($a_value = 1)
10708  {
10709  switch ($a_value)
10710  {
10711  case 1:
10712  $this->show_serial = 1;
10713  break;
10714  default:
10715  $this->show_serial = 0;
10716  break;
10717  }
10718  }
10719 
10720  public function getSerial()
10721  {
10722  return $this->getTestSession()->getActiveId();
10723  }
10724 
10725 } // END class.ilObjTest
10726 
10727 ?>