ILIAS  Release_4_2_x_branch Revision 61807
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilObjTest.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
5 include_once "./classes/class.ilObject.php";
6 include_once "./Modules/Test/classes/inc.AssessmentConstants.php";
7 
17 class ilObjTest extends ilObject
18 {
26  protected $_kiosk;
27 
33  var $test_id;
34 
41 
42 
49  var $author;
50 
56  var $metadata;
57 
64 
72 
79 
88 
99 
108 
115 
125 
132 
140 
148 
158 
165 
172 
179 
186 
193 
200 
206  var $ects_fx;
207 
214 
223 
230 
238 
245 
252 
260 
267 
274 
281 
288 
295 
302 
309 
316 
323 
330 
337 
344 
351 
358 
365 
372 
378  private $_showinfo;
379 
385  private $_forcejs;
386 
392  private $_customStyle;
393 
394  protected $mailnotification;
395  protected $mailnottype;
397  protected $exportsettings;
398 
399  protected $poolUsage;
400 
401  private $template_id;
402 
408  private $online = null;
409 
416  function ilObjTest($a_id = 0,$a_call_by_reference = true)
417  {
418  global $ilUser;
419  $this->type = "tst";
420  include_once "./Modules/Test/classes/class.assMarkSchema.php";
421  $this->mark_schema = new ASS_MarkSchema();
422  $this->test_id = -1;
423  $this->author = $ilUser->fullname;
424  $this->introduction = "";
425  $this->questions = array();
426  $this->sequence_settings = TEST_FIXED_SEQUENCE;
427  $this->score_reporting = REPORT_AFTER_TEST;
428  $this->instant_verification = 0;
429  $this->answer_feedback_points = 0;
430  $this->reporting_date = "";
431  $this->nr_of_tries = 0;
432  $this->_kiosk = 0;
433  $this->use_previous_answers = 1;
434  $this->title_output = 0;
435  $this->starting_time = "";
436  $this->ending_time = "";
437  $this->processing_time = "00:00:00";
438  $this->enable_processing_time = "0";
439  $this->reset_processing_time = 0;
440  $this->ects_output = 0;
441  $this->ects_fx = NULL;
442  $this->random_test = 0;
443  $this->shuffle_questions = FALSE;
444  $this->mailnottype = 0;
445  $this->exportsettings = 0;
446  $this->show_summary = 8;
447  $this->random_question_count = "";
448  $this->count_system = COUNT_PARTIAL_SOLUTIONS;
449  $this->mc_scoring = SCORE_ZERO_POINTS_WHEN_UNANSWERED;
450  $this->score_cutting = SCORE_CUT_QUESTION;
451  $this->pass_scoring = SCORE_LAST_PASS;
452  $this->answer_feedback = 0;
453  $this->password = "";
454  $this->certificate_visibility = 0;
455  $this->allowedUsers = "";
456  $this->_showfinalstatement = FALSE;
457  $this->_finalstatement = "";
458  $this->_showinfo = TRUE;
459  $this->_forcejs = FALSE;
460  $this->_customStyle = "";
461  $this->allowedUsersTimeGap = "";
462  $this->anonymity = 0;
463  $this->show_cancel = 1;
464  $this->show_marker = 0;
465  $this->fixed_participants = 0;
466  $this->setShowPassDetails(TRUE);
467  $this->setShowSolutionDetails(TRUE);
468  $this->setShowSolutionAnswersOnly(FALSE);
469  $this->setShowSolutionSignature(FALSE);
470  $this->testSession = FALSE;
471  $this->testSequence = FALSE;
472  $this->mailnotification = 0;
473  $this->poolUsage = 0;
474  global $lng;
475  $lng->loadLanguageModule("assessment");
476  $this->mark_schema->createSimpleSchema($lng->txt("failed_short"), $lng->txt("failed_official"), 0, 0, $lng->txt("passed_short"), $lng->txt("passed_official"), 50, 1);
477  $this->ects_grades = array(
478  "A" => 90,
479  "B" => 65,
480  "C" => 35,
481  "D" => 10,
482  "E" => 0
483  );
484  $this->express_mode = false;
485  $this->template_id = '';
486  $this->ilObject($a_id, $a_call_by_reference);
487  }
488 
492  function create($a_upload = false)
493  {
494  parent::create();
495 
496  // meta data will be created by
497  // import parser
498  if (!$a_upload)
499  {
500  $this->createMetaData();
501  }
502  }
503 
510  function update()
511  {
512  if (!parent::update())
513  {
514  return false;
515  }
516 
517  // put here object specific stuff
518 
519  return true;
520  }
521 
528  function createReference() {
530 
531  $this->setPoolUsage(true);
532 
533  $this->saveToDb();
534  return $result;
535  }
536 
542  function read($a_force_db = false)
543  {
544  parent::read($a_force_db);
545  $this->loadFromDb();
546  }
547 
548 
555  function delete()
556  {
557  // always call parent delete function first!!
558  if (!parent::delete())
559  {
560  return false;
561  }
562 
563  // delet meta data
564  $this->deleteMetaData();
565 
566  //put here your module specific stuff
567  $this->deleteTest();
568 
569  return true;
570  }
571 
577  function deleteTest()
578  {
579  global $ilDB;
580 
581  // first of all remove all test editings, because the delete statements used for this
582  // contain a subquery for active ids, that are deleted in the next steps
583  $this->removeAllTestEditings();
584 
585  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s",
586  array('integer'),
587  array($this->getTestId())
588  );
589  $active_array = array();
590  while ($row = $ilDB->fetchAssoc($result))
591  {
592  array_push($active_array, $row["active_id"]);
593  }
594 
595  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
596  array('integer'),
597  array($this->getTestId())
598  );
599 
600  if (count($active_array))
601  {
602  foreach ($active_array as $active_id)
603  {
604  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_times WHERE active_fi = %s",
605  array('integer'),
606  array($active_id)
607  );
608 
609  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
610  array('integer'),
611  array($active_id)
612  );
613  }
614  }
615 
616  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_mark WHERE test_fi = %s",
617  array('integer'),
618  array($this->getTestId())
619  );
620 
621  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s",
622  array('integer'),
623  array($this->getTestId())
624  );
625  while ($row = $ilDB->fetchAssoc($result))
626  {
627  $this->removeQuestion($row["question_fi"]);
628  }
629 
630  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_tests WHERE test_id = %s",
631  array('integer'),
632  array($this->getTestId())
633  );
634 
635  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
636  array('integer'),
637  array($this->getTestId())
638  );
639 
640  // this delete is allready done by call to removeAllTestEditings some lines above
641  /*$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)",
642  array('integer'),
643  array($this->getTestId())
644  );*/
645 
646  // moved to top of this method because this method performs delete statements,
647  // that use a subquery for active ids, that are allready deleted some lines above
648  //$this->removeAllTestEditings();
649 
650  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
651  array('integer'),
652  array($this->getTestId())
653  );
654 
655  if ($this->isRandomTest())
656  {
658  }
659 
660 
661  // delete export files
662  include_once "./Services/Utilities/classes/class.ilUtil.php";
663  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
664  $directory = $tst_data_dir."/tst_".$this->getId();
665  if (is_dir($directory))
666  {
667  include_once "./Services/Utilities/classes/class.ilUtil.php";
668  ilUtil::delDir($directory);
669  }
670  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
671  $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId());
672  // remaining usages are not in text anymore -> delete them
673  // and media objects (note: delete method of ilObjMediaObject
674  // checks whether object is used in another context; if yes,
675  // the object is not deleted!)
676  foreach($mobs as $mob)
677  {
678  ilObjMediaObject::_removeUsage($mob, "tst:html", $this->getId());
679  if (ilObjMediaObject::_exists($mob))
680  {
681  $mob_obj =& new ilObjMediaObject($mob);
682  $mob_obj->delete();
683  }
684  }
685  }
686 
696  function initDefaultRoles()
697  {
698  global $rbacadmin;
699  return array();
700  }
701 
715  function notify($a_event,$a_ref_id,$a_parent_non_rbac_id,$a_node_id,$a_params = 0)
716  {
717  global $tree;
718 
719  switch ($a_event)
720  {
721  case "link":
722 
723  //var_dump("<pre>",$a_params,"</pre>");
724  //echo "Module name ".$this->getRefId()." triggered by link event. Objects linked into target object ref_id: ".$a_ref_id;
725  //exit;
726  break;
727 
728  case "cut":
729 
730  //echo "Module name ".$this->getRefId()." triggered by cut event. Objects are removed from target object ref_id: ".$a_ref_id;
731  //exit;
732  break;
733 
734  case "copy":
735 
736  //var_dump("<pre>",$a_params,"</pre>");
737  //echo "Module name ".$this->getRefId()." triggered by copy event. Objects are copied into target object ref_id: ".$a_ref_id;
738  //exit;
739  break;
740 
741  case "paste":
742 
743  //echo "Module name ".$this->getRefId()." triggered by paste (cut) event. Objects are pasted into target object ref_id: ".$a_ref_id;
744  //exit;
745  break;
746 
747  case "new":
748 
749  //echo "Module name ".$this->getRefId()." triggered by paste (new) event. Objects are applied to target object ref_id: ".$a_ref_id;
750  //exit;
751  break;
752  }
753 
754  // At the beginning of the recursive process it avoids second call of the notify function with the same parameter
755  if ($a_node_id==$_GET["ref_id"])
756  {
757  $parent_obj =& $this->ilias->obj_factory->getInstanceByRefId($a_node_id);
758  $parent_type = $parent_obj->getType();
759  if ($parent_type == $this->getType())
760  {
761  $a_node_id = (int) $tree->getParentId($a_node_id);
762  }
763  }
764 
765  parent::notify($a_event,$a_ref_id,$a_parent_non_rbac_id,$a_node_id,$a_params);
766  }
767 
774  {
775  include_once "./Services/Utilities/classes/class.ilUtil.php";
776  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
777  ilUtil::makeDir($tst_data_dir);
778  if (!is_writable($tst_data_dir))
779  {
780  $this->ilias->raiseError("Test Data Directory (".$tst_data_dir
781  .") not writeable.",$this->ilias->error_obj->MESSAGE);
782  }
783 
784  // create learning module directory (data_dir/lm_data/lm_<id>)
785  $tst_dir = $tst_data_dir."/tst_".$this->getId();
786  ilUtil::makeDir($tst_dir);
787  if (!@is_dir($tst_dir))
788  {
789  $this->ilias->raiseError("Creation of Test Directory failed.",$this->ilias->error_obj->MESSAGE);
790  }
791  // create Export subdirectory (data_dir/lm_data/lm_<id>/Export)
792  $export_dir = $tst_dir."/export";
793  ilUtil::makeDir($export_dir);
794  if (!@is_dir($export_dir))
795  {
796  $this->ilias->raiseError("Creation of Export Directory failed.",$this->ilias->error_obj->MESSAGE);
797  }
798  }
799 
806  {
807  include_once "./Services/Utilities/classes/class.ilUtil.php";
808  $export_dir = ilUtil::getDataDir()."/tst_data"."/tst_".$this->getId()."/export";
809  return $export_dir;
810  }
811 
819  {
820  // quit if import dir not available
821  if (!@is_dir($dir) or
822  !is_writeable($dir))
823  {
824  return array();
825  }
826 
827  // open directory
828  $dir = dir($dir);
829 
830  // initialize array
831  $file = array();
832 
833  // get files and save the in the array
834  while ($entry = $dir->read())
835  {
836  if ($entry != "." and
837  $entry != ".." and
838  //substr($entry, -4) == ".zip" and
839  ereg("^[0-9]{10}_{2}[0-9]+_{2}(tst(__results)?_)*[0-9]+\.[a-z]{1,3}\$", $entry))
840  {
841  $file[] = $entry;
842  }
843  }
844 
845  // close import directory
846  $dir->close();
847 
848  // sort files
849  sort ($file);
850  reset ($file);
851 
852  return $file;
853  }
854 
858  function _setImportDirectory($a_import_dir = null)
859  {
860  if (strlen($a_import_dir))
861  {
862  $_SESSION["tst_import_dir"] = $a_import_dir;
863  }
864  else
865  {
866  unset($_SESSION["tst_import_dir"]);
867  }
868  }
869 
877  {
878  if (strlen($_SESSION["tst_import_dir"]))
879  {
880  return $_SESSION["tst_import_dir"];
881  }
882  return null;
883  }
884 
886  {
888  }
889 
896  {
897  include_once "./Services/Utilities/classes/class.ilUtil.php";
898  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
899  ilUtil::makeDir($tst_data_dir);
900 
901  if (!is_writable($tst_data_dir))
902  {
903  $this->ilias->raiseError("Test Data Directory (".$tst_data_dir
904  .") not writeable.",$this->ilias->error_obj->FATAL);
905  }
906 
907  // create test directory (data_dir/tst_data/tst_import)
908  $tst_dir = $tst_data_dir."/tst_import";
909  ilUtil::makeDir($tst_dir);
910  if (!@is_dir($tst_dir))
911  {
912  $ilias->raiseError("Creation of test import directory failed.",$ilias->error_obj->FATAL);
913  }
914  return $tst_dir;
915  }
916 
924  {
925  global $ilDB;
926 
927  $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",
928  array('integer'),
929  array($this->getTestId())
930  );
931  $hasSC = false;
932  while ($row = $ilDB->fetchAssoc($result))
933  {
934  if (strcmp($row['foundtypes'], 'assSingleChoice') == 0)
935  {
936  $hasSC = true;
937  }
938  }
939  return $hasSC;
940  }
941 
949  {
950  global $ilDB;
951 
952  $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",
953  array('integer'),
954  array($this->getTestId())
955  );
956  if ($result->numRows() == 1)
957  {
958  $row = $ilDB->fetchAssoc($result);
959  if (strcmp($row['foundtypes'], 'assSingleChoice') == 0)
960  {
961  return TRUE;
962  }
963  else
964  {
965  return false;
966  }
967  }
968  return FALSE;
969  }
970 
978  {
979  global $ilDB;
980 
981  if (!$this->hasSingleChoiceQuestions()) return false;
982 
983  $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",
984  array('integer', 'text'),
985  array($this->getTestId(), 'assSingleChoice')
986  );
987  if ($result->numRows() == 1)
988  {
989  $row = $ilDB->fetchAssoc($result);
990  return ($row['foundshuffles'] == 0);
991  }
992  return FALSE;
993  }
994 
1001  function isComplete()
1002  {
1003  if ((count($this->mark_schema->mark_steps)) and (count($this->questions)))
1004  {
1005  return 1;
1006  }
1007  else
1008  {
1009  if ($this->isRandomTest())
1010  {
1011  $arr = $this->getRandomQuestionpools();
1012  if (count($arr) && ($this->getRandomQuestionCount() > 0))
1013  {
1014  return 1;
1015  }
1016  $count = 0;
1017  foreach ($arr as $array)
1018  {
1019  $count += $array["count"];
1020  }
1021  if ($count)
1022  {
1023  return 1;
1024  }
1025  }
1026  return 0;
1027  }
1028  return 0;
1029  }
1030 
1037  function _isComplete($obj_id)
1038  {
1039  $test = new ilObjTest($obj_id, false);
1040  $test->loadFromDb();
1041  return $test->isComplete();
1042  }
1043 
1049  function saveECTSStatus($ects_output = 0, $fx_support = "", $ects_a = 90, $ects_b = 65, $ects_c = 35, $ects_d = 10, $ects_e = 0)
1050  {
1051  global $ilDB;
1052  if ($this->test_id > 0)
1053  {
1054  $fx_support = preg_replace("/,/", ".", $fx_support);
1055  if (preg_match("/\d+/", $fx_support))
1056  {
1057  $fx_support = $fx_support;
1058  }
1059  else
1060  {
1061  $fx_support = NULL;
1062  }
1063  $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",
1064  array('text','float','float','float','float','float','float','integer'),
1065  array($ects_output, $ects_a, $ects_b, $ects_c, $ects_d, $ects_e, $fx_support, $this->getTestId())
1066  );
1067  $this->ects_output = $ects_output;
1068  $this->ects_fx = $fx_support;
1069  }
1070  }
1071 
1078  {
1079  global $ilDB;
1080 
1081  $complete = 0;
1082  if ($this->isComplete())
1083  {
1084  $complete = 1;
1085  }
1086  if ($this->test_id > 0)
1087  {
1088  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET complete = %s WHERE test_id = %s",
1089  array('text','integer'),
1090  array($complete, $this->test_id)
1091  );
1092  }
1093  }
1094 
1100  function getAllRTEContent()
1101  {
1102  $result = array();
1103  array_push($result, $this->getIntroduction());
1104  array_push($result, $this->getFinalStatement());
1105  return $result;
1106  }
1107 
1114  {
1115  include_once("./Services/RTE/classes/class.ilRTE.php");
1116  $completecontent = "";
1117  foreach ($this->getAllRTEContent() as $content)
1118  {
1119  $completecontent .= $content;
1120  }
1121  ilRTE::_cleanupMediaObjectUsage($completecontent, $this->getType() . ":html",
1122  $this->getId());
1123  }
1124 
1131  function saveToDb($properties_only = FALSE)
1132  {
1133  global $ilDB, $ilLog;
1134 
1135  // cleanup RTE images
1136  $this->cleanupMediaobjectUsage();
1137 
1138  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1139  if ($this->test_id == -1)
1140  {
1141  // Create new dataset
1142  $next_id = $ilDB->nextId('tst_tests');
1143  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_tests (test_id, obj_fi, author, introduction, " .
1144  "finalstatement, showinfo, forcejs, customstyle, showfinalstatement, sequence_settings, " .
1145  "score_reporting, instant_verification, answer_feedback_points, answer_feedback, anonymity, show_cancel, show_marker, " .
1146  "fixed_participants, nr_of_tries, kiosk, use_previous_answers, title_output, processing_time, enable_processing_time, " .
1147  "reset_processing_time, reporting_date, starting_time, ending_time, complete, ects_output, ects_a, ects_b, ects_c, ects_d, " .
1148  "ects_e, ects_fx, random_test, random_question_count, count_system, mc_scoring, score_cutting, pass_scoring, " .
1149  "shuffle_questions, results_presentation, show_summary, password, allowedusers, mailnottype, exportsettings, " .
1150  "alloweduserstimegap, certificate_visibility, mailnotification, created, tstamp, enabled_view_mode, template_id, pool_usage, online_status) " .
1151  "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, " .
1152  "%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, %s, %s)",
1153  array(
1154  'integer', 'integer', 'text', 'text',
1155  'text', 'integer', 'integer', 'text', 'integer', 'integer',
1156  'integer', 'text', 'text', 'text', 'text', 'text', 'integer',
1157  'text', 'integer', 'integer', 'text', 'text', 'text', 'text',
1158  'integer', 'text', 'text', 'text', 'text', 'text', 'float', 'float', 'float', 'float',
1159  'float', 'float', 'text', 'integer', 'text', 'text', 'text', 'text',
1160  'text', 'integer', 'integer', 'text', 'integer', 'integer', 'integer',
1161  'integer', 'text', 'integer', 'integer', 'integer', 'text', 'text', 'integer', 'integer'
1162  ),
1163  array(
1164  $next_id,
1165  $this->getId(),
1166  $this->getAuthor(),
1169  $this->getShowInfo(),
1170  $this->getForceJS(),
1171  $this->getCustomStyle(),
1172  $this->getShowFinalStatement(),
1173  $this->getSequenceSettings(),
1174  $this->getScoreReporting(),
1175  $this->getInstantFeedbackSolution(),
1176  $this->getAnswerFeedbackPoints(),
1177  $this->getAnswerFeedback(),
1178  $this->getAnonymity(),
1179  $this->getShowCancel(),
1180  $this->getShowMarker(),
1181  $this->getFixedParticipants(),
1182  $this->getNrOfTries(),
1183  $this->getKiosk(),
1184  $this->getUsePreviousAnswers(),
1185  $this->getTitleOutput(),
1186  $this->getProcessingTime(),
1187  $this->getEnableProcessingTime(),
1188  $this->getResetProcessingTime(),
1189  $this->getReportingDate(),
1190  $this->getStartingTime(),
1191  $this->getEndingTime(),
1192  $this->isComplete(),
1193  $this->getECTSOutput(),
1194  strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : NULL,
1195  strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : NULL,
1196  strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : NULL,
1197  strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : NULL,
1198  strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : NULL,
1199  $this->getECTSFX(),
1200  $this->isRandomTest(),
1201  $this->getRandomQuestionCount(),
1202  $this->getCountSystem(),
1203  $this->getMCScoring(),
1204  $this->getScoreCutting(),
1205  $this->getPassScoring(),
1206  $this->getShuffleQuestions(),
1207  $this->getResultsPresentation(),
1208  $this->getListOfQuestionsSettings(),
1209  $this->getPassword(),
1210  $this->getAllowedUsers(),
1211  $this->getMailNotificationType(),
1212  $this->getExportSettings(),
1213  $this->getAllowedUsersTimeGap(),
1214  $this->getCertificateVisibility(),
1215  $this->getMailNotification(),
1216  time(),
1217  time(),
1218  $this->getEnabledViewMode(),
1219  $this->getTemplate(),
1220  $this->getPoolUsage(),
1221  (int)$this->isOnline()
1222  )
1223  );
1224  $this->test_id = $next_id;
1225 
1227  {
1228  $this->logAction($this->lng->txtlng("assessment", "log_create_new_test", ilObjAssessmentFolder::_getLogLanguage()));
1229  }
1230  }
1231  else
1232  {
1233  // Modify existing dataset
1234  $oldrow = array();
1236  {
1237  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
1238  array('integer'),
1239  array($this->test_id)
1240  );
1241  if ($result->numRows() == 1)
1242  {
1243  $oldrow = $ilDB->fetchAssoc($result);
1244  }
1245  }
1246 
1247  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET author = %s, introduction = %s, " .
1248  "finalstatement = %s, showinfo = %s, forcejs = %s, customstyle = %s, showfinalstatement = %s, sequence_settings = %s, " .
1249  "score_reporting = %s, instant_verification = %s, answer_feedback_points = %s, answer_feedback = %s, anonymity = %s, show_cancel = %s, show_marker = %s, " .
1250  "fixed_participants = %s, nr_of_tries = %s, kiosk = %s, use_previous_answers = %s, title_output = %s, processing_time = %s, enable_processing_time = %s, " .
1251  "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, " .
1252  "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, " .
1253  "shuffle_questions = %s, results_presentation = %s, show_summary = %s, password = %s, allowedusers = %s, mailnottype = %s, exportsettings = %s, " .
1254  "alloweduserstimegap = %s, certificate_visibility = %s, mailnotification = %s, tstamp = %s, enabled_view_mode = %s, template_id = %s, pool_usage = %s, online_status = %s WHERE test_id = %s",
1255  array(
1256  'text', 'text',
1257  'text', 'integer', 'integer', 'text', 'integer', 'integer',
1258  'integer', 'text', 'text', 'text', 'text', 'text', 'integer',
1259  'text', 'integer', 'integer', 'text', 'text', 'text', 'text',
1260  'integer', 'text', 'text', 'text', 'text', 'text', 'float', 'float', 'float', 'float',
1261  'float', 'float', 'text', 'integer', 'text', 'text', 'text', 'text',
1262  'text', 'integer', 'integer', 'text', 'integer','integer', 'integer',
1263  'integer', 'text', 'integer', 'integer', 'text', 'text', 'integer', 'integer', 'integer'
1264  ),
1265  array(
1266  $this->getAuthor(),
1269  $this->getShowInfo(),
1270  $this->getForceJS(),
1271  $this->getCustomStyle(),
1272  $this->getShowFinalStatement(),
1273  $this->getSequenceSettings(),
1274  $this->getScoreReporting(),
1275  $this->getInstantFeedbackSolution(),
1276  $this->getAnswerFeedbackPoints(),
1277  $this->getAnswerFeedback(),
1278  $this->getAnonymity(),
1279  $this->getShowCancel(),
1280  $this->getShowMarker(),
1281  $this->getFixedParticipants(),
1282  $this->getNrOfTries(),
1283  $this->getKiosk(),
1284  $this->getUsePreviousAnswers(),
1285  $this->getTitleOutput(),
1286  $this->getProcessingTime(),
1287  $this->getEnableProcessingTime(),
1288  $this->getResetProcessingTime(),
1289  $this->getReportingDate(),
1290  $this->getStartingTime(),
1291  $this->getEndingTime(),
1292  $this->isComplete(),
1293  $this->getECTSOutput(),
1294  strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : NULL,
1295  strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : NULL,
1296  strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : NULL,
1297  strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : NULL,
1298  strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : NULL,
1299  $this->getECTSFX(),
1300  $this->isRandomTest(),
1301  $this->getRandomQuestionCount(),
1302  $this->getCountSystem(),
1303  $this->getMCScoring(),
1304  $this->getScoreCutting(),
1305  $this->getPassScoring(),
1306  $this->getShuffleQuestions(),
1307  $this->getResultsPresentation(),
1308  $this->getListOfQuestionsSettings(),
1309  $this->getPassword(),
1310  $this->getAllowedUsers(),
1311  $this->getMailNotificationType(),
1312  $this->getExportSettings(),
1313  $this->getAllowedUsersTimeGap(),
1314  $this->getCertificateVisibility(),
1315  $this->getMailNotification(),
1316  time(),
1317  $this->getEnabledViewMode(),
1318  $this->getTemplate(),
1319  $this->getPoolUsage(),
1320  (int)$this->isOnline(),
1321  $this->getTestId()
1322  )
1323  );
1324 
1325  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1327  {
1328  $logresult = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
1329  array('integer'),
1330  array($this->getTestId())
1331  );
1332  $newrow = array();
1333  if ($logresult->numRows() == 1)
1334  {
1335  $newrow = $ilDB->fetchAssoc($logresult);
1336  }
1337  $changed_fields = array();
1338  foreach ($oldrow as $key => $value)
1339  {
1340  if (strcmp($oldrow[$key], $newrow[$key]) != 0)
1341  {
1342  array_push($changed_fields, "$key: " . $oldrow[$key] . " => " . $newrow[$key]);
1343  }
1344  }
1345  $changes = join($changed_fields, ", ");
1346  if (count($changed_fields) > 0)
1347  {
1348  $this->logAction($this->lng->txtlng("assessment", "log_modified_test", ilObjAssessmentFolder::_getLogLanguage()) . " [".$changes."]");
1349  }
1350  }
1351  if ($this->evalTotalPersons() > 0)
1352  {
1353  // reset the finished status of participants if the nr of test passes did change
1354  if ($this->getNrOfTries() > 0)
1355  {
1356  // set all unfinished tests with nr of passes >= allowed passes finished
1357  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND tries >= %s AND submitted = %s",
1358  array('integer', 'integer', 'integer'),
1359  array($this->getTestId(), $this->getNrOfTries(), 0)
1360  );
1361  while ($row = $ilDB->fetchAssoc($aresult))
1362  {
1363  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1364  array('integer', 'timestamp', 'integer'),
1365  array(1, date('Y-m-d H:i:s'), $row["active_id"])
1366  );
1367  }
1368 
1369  // set all finished tests with nr of passes < allowed passes not finished
1370  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND tries < %s AND submitted = %s",
1371  array('integer', 'integer', 'integer'),
1372  array($this->getTestId(), $this->getNrOfTries()-1, 1)
1373  );
1374  while ($row = $ilDB->fetchAssoc($aresult))
1375  {
1376  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1377  array('integer', 'timestamp', 'integer'),
1378  array(0, NULL, $row["active_id"])
1379  );
1380  }
1381  }
1382  else
1383  {
1384  // set all finished tests with nr of passes >= allowed passes not finished
1385  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND submitted = %s",
1386  array('integer', 'integer'),
1387  array($this->getTestId(), 1)
1388  );
1389  while ($row = $ilDB->fetchAssoc($aresult))
1390  {
1391  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1392  array('integer', 'timestamp', 'integer'),
1393  array(0, NULL, $row["active_id"])
1394  );
1395  }
1396  }
1397  }
1398  }
1399  if (!$this->isRandomTest())
1400  {
1402  }
1403  if (!$properties_only)
1404  {
1405  if (PEAR::isError($result))
1406  {
1407  global $ilias;
1408  $ilias->raiseError($result->getMessage());
1409  }
1410  else
1411  {
1412  if (!$this->isRandomTest())
1413  {
1414  $this->saveQuestionsToDb();
1415  }
1416  $this->mark_schema->saveToDb($this->test_id);
1417  }
1418  }
1419  }
1420 
1428  {
1429  global $ilDB;
1430 
1431  $oldquestions = array();
1432  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
1434  {
1435  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence",
1436  array('integer'),
1437  array($this->getTestId())
1438  );
1439  if ($result->numRows() > 0)
1440  {
1441  while ($row = $ilDB->fetchAssoc($result))
1442  {
1443  array_push($oldquestions, $row["question_fi"]);
1444  }
1445  }
1446  }
1447 
1448  // delete existing category relations
1449  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
1450  array('integer'),
1451  array($this->getTestId())
1452  );
1453  // create new category relations
1454  foreach ($this->questions as $key => $value)
1455  {
1456  $next_id = $ilDB->nextId('tst_test_question');
1457  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
1458  array('integer','integer', 'integer', 'integer', 'integer'),
1459  array($next_id, $this->getTestId(), $value, $key, time())
1460  );
1461  }
1462  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1464  {
1465  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence",
1466  array('integer'),
1467  array($this->getTestId())
1468  );
1469  $newquestions = array();
1470  if ($result->numRows() > 0)
1471  {
1472  while ($row = $ilDB->fetchAssoc($result))
1473  {
1474  array_push($newquestions, $row["question_fi"]);
1475  }
1476  }
1477  foreach ($oldquestions as $index => $question_id)
1478  {
1479  if (strcmp($newquestions[$index], $question_id) != 0)
1480  {
1481  $pos = array_search($question_id, $newquestions);
1482  if ($pos === FALSE)
1483  {
1484  $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id);
1485  }
1486  else
1487  {
1488  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index+1) . " => " . ($pos+1), $question_id);
1489  }
1490  }
1491  }
1492  foreach ($newquestions as $index => $question_id)
1493  {
1494  if (array_search($question_id, $oldquestions) === FALSE)
1495  {
1496  $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index+1), $question_id);
1497  }
1498  }
1499  }
1500  }
1501 
1505  protected function isNewRandomTest()
1506  {
1507  global $ilDB;
1508  $result = $ilDB->queryF('SELECT copy_id FROM tst_rnd_cpy WHERE tst_fi = %s',
1509  array('integer'),
1510  array($this->getTestId())
1511  );
1512  return $result->numRows() > 0;
1513  }
1514 
1521  function saveRandomQuestion($active_id, $question_id, $pass = NULL, $maxcount)
1522  {
1523  global $ilUser;
1524  global $ilDB;
1525 
1526  if (is_null($pass)) $pass = 0;
1527  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s",
1528  array('integer','integer'),
1529  array($active_id, $pass)
1530  );
1531  if ($result->numRows() < $maxcount)
1532  {
1533  $duplicate_id = $question_id;
1534  if (!$this->isNewRandomTest())
1535  {
1536  $duplicate_id = $this->getRandomQuestionDuplicate($question_id, $active_id);
1537  if ($duplicate_id === FALSE)
1538  {
1539  $duplicate_id = $this->duplicateQuestionForTest($question_id);
1540  }
1541  }
1542  $next_id = $ilDB->nextId('tst_test_rnd_qst');
1543  $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)",
1544  array('integer','integer','integer','integer','integer','integer'),
1545  array($next_id,$active_id, $duplicate_id, $result->numRows()+1, $pass, time())
1546  );
1547  }
1548  }
1549 
1559  function getRandomQuestionDuplicate($question_id, $active_id)
1560  {
1561  global $ilDB;
1562 
1563  $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",
1564  array('integer', 'integer'),
1565  array($question_id, $active_id)
1566  );
1567  $num = $result->numRows();
1568  if ($num > 0)
1569  {
1570  $row = $ilDB->fetchAssoc($result);
1571  return $row["question_id"];
1572  }
1573  else
1574  {
1575  return FALSE;
1576  }
1577  }
1578 
1584  function getNrOfResultsForPass($active_id, $pass)
1585  {
1586  global $ilDB;
1587 
1588  $result = $ilDB->queryF("SELECT test_result_id FROM tst_test_result WHERE active_fi = %s AND pass = %s",
1589  array('integer','integer'),
1590  array($active_id, $pass)
1591  );
1592  return $result->numRows();
1593  }
1594 
1603  function hasRandomQuestionsForPass($active_id, $pass)
1604  {
1605  global $ilDB;
1606  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s",
1607  array('integer','integer'),
1608  array($active_id, $pass)
1609  );
1610  return ($result->numRows() > 0) ? true : false;
1611  }
1612 
1619  function generateRandomQuestions($active_id, $pass = NULL)
1620  {
1621  global $ilUser;
1622  global $ilDB;
1623 
1624  if ($active_id > 0)
1625  {
1626  if ($this->hasRandomQuestionsForPass($active_id, $pass) > 0)
1627  {
1628  // Something went wrong. Maybe the user pressed the start button twice
1629  // Questions already exist so there is no need to create new questions
1630  return;
1631  }
1632  if (false && $pass > 0) // fixed mantis #9221
1633  {
1634  if ($this->getNrOfResultsForPass($active_id, $pass - 1) == 0)
1635  {
1636  // This means that someone maybe reloaded the test submission page
1637  // If there are no existing results for the previous test, it makes
1638  // no sense to create a new set of random questions
1639  return;
1640  }
1641  }
1642  }
1643  else
1644  {
1645  // This may not happen! If it happens, raise a fatal error...
1646  global $ilias, $ilErr;
1647  $ilias->raiseError(sprintf($this->lng->txt("error_random_question_generation"), $ilUser->getId(), $this->getTestId()), $ilErr->FATAL);
1648  }
1649 
1650  $num = $this->getRandomQuestionCount();
1651  if ($num > 0)
1652  {
1653  $qpls =& $this->getRandomQuestionpools();
1654  $rndquestions = $this->generateRandomPass($num, $qpls, $pass);
1655  $allquestions = array();
1656  foreach ($rndquestions as $question_id)
1657  {
1658  array_push($allquestions, $question_id);
1659  }
1660  if ($this->getShuffleQuestions())
1661  {
1662  srand ((float)microtime()*1000000);
1663  shuffle($allquestions);
1664  }
1665 
1666  $maxcount = 0;
1667  foreach ($qpls as $data)
1668  {
1669  $maxcount += $data["contains"];
1670  }
1671  if ($num > $maxcount) $num = $maxcount;
1672  foreach ($allquestions as $question_id)
1673  {
1674  $this->saveRandomQuestion($active_id, $question_id, $pass, $num);
1675  }
1676  }
1677  else
1678  {
1679  $qpls =& $this->getRandomQuestionpools();
1680  $allquestions = array();
1681  $maxcount = 0;
1682  foreach ($qpls as $key => $value)
1683  {
1684  if ($value["count"] > 0)
1685  {
1686  $rndquestions = $this->generateRandomPass($value["count"], array($value), $pass);
1687  foreach ($rndquestions as $question_id)
1688  {
1689  array_push($allquestions, $question_id);
1690  }
1691  }
1692  $add = ($value["count"] <= $value["contains"]) ? $value["count"] : $value["contains"];
1693  $maxcount += $add;
1694  }
1695  if ($this->getShuffleQuestions())
1696  {
1697  srand ((float)microtime()*1000000);
1698  shuffle($allquestions);
1699  }
1700  foreach ($allquestions as $question_id)
1701  {
1702  $this->saveRandomQuestion($active_id, $question_id, $pass, $maxcount);
1703  }
1704  }
1705  }
1706 
1713  function saveRandomQuestionCount($total_questions = NULL)
1714  {
1715  global $ilDB;
1716 
1717  if (strlen($total_questions))
1718  {
1719  $this->setRandomQuestionCount($total_questions);
1720  }
1721  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET random_question_count = %s, tstamp = %s WHERE test_id = %s",
1722  array('integer', 'integer', 'integer'),
1723  array($this->getRandomQuestionCount(), time(), $this->getTestId())
1724  );
1725  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1727  {
1728  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_total_amount_of_questions", ilObjAssessmentFolder::_getLogLanguage()), $this->getRandomQuestionCount()));
1729  }
1730  }
1731 
1740  {
1741  global $ilDB;
1742 
1743  $qpls = array();
1744  $counter = 0;
1745  $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",
1746  array("integer"),
1747  array($this->getTestId())
1748  );
1749  if ($result->numRows())
1750  {
1751  while ($row = $ilDB->fetchAssoc($result))
1752  {
1753  $qpls[$counter] = array(
1754  "index" => $counter,
1755  "count" => $row["num_of_q"],
1756  "qpl" => $row["questionpool_fi"],
1757  "contains" => $row["questioncount"]
1758  );
1759  $counter++;
1760  }
1761  }
1762  return $qpls;
1763  }
1764 
1768  public function saveRandomQuestionpools()
1769  {
1770  global $ilDB;
1771 
1772  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1773  // delete existing random questionpools
1774  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
1775  array('integer'),
1776  array($this->getTestId())
1777  );
1779  {
1780  $this->logAction($this->lng->txtlng("assessment", "log_random_question_pool_deleted", ilObjAssessmentFolder::_getLogLanguage()));
1781  }
1782  // delete existing duplicated questions
1784 
1785  // create new random questionpools
1786  foreach ($this->random_questionpool_data as $idx => $data)
1787  {
1788  if ($data->qpl > 0)
1789  {
1790  // save questionpool information
1791  $next_id = $ilDB->nextId('tst_test_random');
1792  $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)",
1793  array('integer','integer', 'integer', 'integer', 'integer', 'integer'),
1794  array($next_id, $this->getTestId(), $data->qpl, $data->count, time(), $idx)
1795  );
1797  {
1798  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_random_question_pool_added", ilObjAssessmentFolder::_getLogLanguage()), $value["title"] . " (" . $value["qpl"] . ")", $value["count"]));
1799  }
1800  // duplicate all questions of the questionpools
1801  $this->duplicateQuestionpoolForTest($data->qpl);
1802  }
1803  }
1804  }
1805 
1809  protected function removeDuplicatedQuestionpools()
1810  {
1811  global $ilDB;
1812 
1813  $result = $ilDB->queryF('SELECT * FROM tst_rnd_cpy WHERE tst_fi = %s',
1814  array('integer'),
1815  array($this->getTestId())
1816  );
1817  while ($row = $ilDB->fetchAssoc($result))
1818  {
1819  $question =& ilObjTest::_instanciateQuestion($row['qst_fi']);
1820  $question->delete($row['qst_fi']);
1821  }
1822 
1823  $affectedRows = $ilDB->manipulateF('DELETE FROM tst_rnd_cpy WHERE tst_fi = %s',
1824  array('integer'),
1825  array($this->getTestId())
1826  );
1827 
1828  $affectedRows = $ilDB->manipulateF('DELETE FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1829  array('integer'),
1830  array($this->getTestId())
1831  );
1832  }
1833 
1837  public function getUsedRandomQuestionpools()
1838  {
1839  global $ilDB;
1840  if ($this->isNewRandomTest())
1841  {
1842  $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',
1843  array('integer'),
1844  array($this->getTestId())
1845  );
1846  $pools = array();
1847  while ($row = $ilDB->fetchAssoc($result))
1848  {
1849  if (is_array($pools[$row['qpl_fi']]))
1850  {
1851  $pools[$row['qpl_fi']]['count']++;
1852  }
1853  else
1854  {
1855  $pools[$row['qpl_fi']]['count'] = 1;
1856  }
1857  $pools[$row['qpl_fi']]['num_of_q'] = $row['num_of_q'];
1858  }
1859  $result = $ilDB->queryF('SELECT * FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1860  array('integer'),
1861  array($this->getTestId())
1862  );
1863 
1864  while ($row = $ilDB->fetchAssoc($result))
1865  {
1866  $pools[$row['qpl_fi']]['title'] = $row['qpl_title'];
1867  }
1868  return $pools;
1869  }
1870  else
1871  {
1872  $result = $ilDB->queryF('SELECT tst_test_random.* FROM tst_test_random WHERE tst_test_random.test_fi = %s ORDER BY sequence, test_random_id',
1873  array('integer'),
1874  array($this->getTestId())
1875  );
1876  $pools = array();
1877  while ($row = $ilDB->fetchAssoc($result))
1878  {
1879  $pools[$row['questionpool_fi']]['count'] = $row['num_of_q'];
1880  $pools[$row['questionpool_fi']]['num_of_q'] = $row['num_of_q'];
1881  }
1882  $result = $ilDB->queryF('SELECT * FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1883  array('integer'),
1884  array($this->getTestId())
1885  );
1886 
1887  while ($row = $ilDB->fetchAssoc($result))
1888  {
1889  $pools[$row['qpl_fi']]['title'] = $row['qpl_title'];
1890  }
1891  return $pools;
1892  }
1893  }
1894 
1898  protected function duplicateQuestionpoolForTest($questionpool_id)
1899  {
1900  global $ilDB;
1901  $result = $ilDB->queryF('SELECT question_id FROM qpl_questions WHERE obj_fi = %s AND complete = %s AND original_id IS NULL',
1902  array('integer', 'text'),
1903  array($questionpool_id, 1)
1904  );
1905  $saved_titles = array();
1906  while ($row = $ilDB->fetchAssoc($result))
1907  {
1908  $question =& ilObjTest::_instanciateQuestion($row['question_id']);
1909  $duplicate_id = $question->duplicate(true, null, null, null, $this->getId());
1910  if ($duplicate_id > 0)
1911  {
1912  $next_id = $ilDB->nextId('tst_rnd_cpy');
1913  $ilDB->manipulateF('INSERT INTO tst_rnd_cpy (copy_id, tst_fi, qst_fi, qpl_fi) VALUES (%s, %s, %s, %s)',
1914  array('integer', 'integer', 'integer', 'integer'),
1915  array($next_id, $this->getTestId(), $duplicate_id, $questionpool_id)
1916  );
1917  if (!array_key_exists($questionpool_id, $saved_titles))
1918  {
1919  $next_id = $ilDB->nextId('tst_rnd_qpl_title');
1920  $ilDB->manipulateF('INSERT INTO tst_rnd_qpl_title (title_id, tst_fi, qpl_fi, qpl_title) VALUES (%s, %s, %s, %s)',
1921  array('integer', 'integer', 'integer', 'text'),
1922  array($next_id, $this->getTestId(), $questionpool_id, ilObject::_lookupTitle($questionpool_id))
1923  );
1924  $saved_titles[$questionpool_id] = 1;
1925  }
1926  }
1927  }
1928  }
1929 
1930  function addRandomQuestionpoolData($count = 0, $qpl = 0, $position)
1931  {
1932  include_once "./Modules/Test/classes/class.ilRandomTestData.php";
1933  if (array_key_exists($position, $this->random_questionpool_data))
1934  {
1935  $newitems = array();
1936  for ($i = 0; $i < $position; $i++)
1937  {
1938  array_push($newitems, $this->random_questionpool_data[$i]);
1939  }
1940  array_push($newitems, new ilRandomTestData($count, $qpl));
1941  for ($i = $position; $i < count($this->random_questionpool_data); $i++)
1942  {
1943  array_push($newitems, $this->random_questionpool_data[$i]);
1944  }
1945  $this->random_questionpool_data = $newitems;
1946  }
1947  else
1948  {
1949  array_push($this->random_questionpool_data, new ilRandomTestData($count, $qpl));
1950  }
1951  }
1952 
1953  function removeRandomQuestionpoolData($position)
1954  {
1955  if (array_key_exists($position, $this->random_questionpool_data))
1956  {
1957  unset($this->random_questionpool_data[$position]);
1958  }
1959  }
1960 
1961  function setRandomQuestionpoolData($a_data)
1962  {
1963  $this->random_questionpool_data = $a_data;
1964  }
1965 
1974  {
1975  if (is_array($this->random_questionpool_data) && count($this->random_questionpool_data)) return $this->random_questionpool_data;
1976 
1977  global $ilDB;
1978 
1979  $qpls = array();
1980  $counter = 0;
1981  $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",
1982  array("integer"),
1983  array($this->getTestId())
1984  );
1985  include_once "./Modules/Test/classes/class.ilRandomTestData.php";
1986  if ($result->numRows())
1987  {
1988  while ($row = $ilDB->fetchAssoc($result))
1989  {
1990  array_push($qpls, new ilRandomTestData($row['num_of_q'], $row['questionpool_fi']));
1991  }
1992  }
1993  return $qpls;
1994  }
1995 
2003  function loadFromDb()
2004  {
2005  global $ilDB;
2006 
2007  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE obj_fi = %s",
2008  array('integer'),
2009  array($this->getId())
2010  );
2011  if ($result->numRows() == 1)
2012  {
2013  $data = $ilDB->fetchObject($result);
2014  $this->setTestId($data->test_id);
2015  if (strlen($this->getAuthor()) == 0)
2016  {
2017  $this->saveAuthorToMetadata($data->author);
2018  }
2019  $this->setAuthor($data->author);
2020  include_once("./Services/RTE/classes/class.ilRTE.php");
2021  $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc($data->introduction, 1));
2022  $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc($data->finalstatement, 1));
2023  $this->setShowInfo($data->showinfo);
2024  $this->setForceJS($data->forcejs);
2025  $this->setCustomStyle($data->customstyle);
2026  $this->setShowFinalStatement($data->showfinalstatement);
2027  $this->setSequenceSettings($data->sequence_settings);
2028  $this->setScoreReporting($data->score_reporting);
2029  $this->setInstantFeedbackSolution($data->instant_verification);
2030  $this->setAnswerFeedbackPoints($data->answer_feedback_points);
2031  $this->setAnswerFeedback($data->answer_feedback);
2032  $this->setAnonymity($data->anonymity);
2033  $this->setShowCancel($data->show_cancel);
2034  $this->setShowMarker($data->show_marker);
2035  $this->setFixedParticipants($data->fixed_participants);
2036  $this->setNrOfTries($data->nr_of_tries);
2037  $this->setKiosk($data->kiosk);
2038  $this->setUsePreviousAnswers($data->use_previous_answers);
2039  $this->setTitleOutput($data->title_output);
2040  $this->setProcessingTime($data->processing_time);
2041  $this->setEnableProcessingTime($data->enable_processing_time);
2042  $this->setResetProcessingTime($data->reset_processing_time);
2043  $this->setReportingDate($data->reporting_date);
2044  $this->setShuffleQuestions($data->shuffle_questions);
2045  $this->setResultsPresentation($data->results_presentation);
2046  $this->setListOfQuestionsSettings($data->show_summary);
2047  $this->setStartingTime($data->starting_time);
2048  $this->setEndingTime($data->ending_time);
2049  $this->setECTSOutput($data->ects_output);
2050  $this->setECTSGrades(
2051  array(
2052  "A" => $data->ects_a,
2053  "B" => $data->ects_b,
2054  "C" => $data->ects_c,
2055  "D" => $data->ects_d,
2056  "E" => $data->ects_e
2057  )
2058  );
2059  $this->setECTSFX($data->ects_fx);
2060  $this->setRandomTest($data->random_test);
2061  $this->setRandomQuestionCount($data->random_question_count);
2062  $this->mark_schema->flush();
2063  $this->mark_schema->loadFromDb($this->getTestId());
2064  $this->setCountSystem($data->count_system);
2065  $this->setMCScoring($data->mc_scoring);
2066  $this->setMailNotification($data->mailnotification);
2067  $this->setMailNotificationType($data->mailnottype);
2068  $this->setExportSettings($data->exportsettings);
2069  $this->setScoreCutting($data->score_cutting);
2070  $this->setPassword($data->password);
2071  $this->setAllowedUsers($data->allowedusers);
2072  $this->setAllowedUsersTimeGap($data->alloweduserstimegap);
2073  $this->setPassScoring($data->pass_scoring);
2074  $this->setCertificateVisibility($data->certificate_visibility);
2075  $this->setEnabledViewMode($data->enabled_view_mode);
2076  $this->setTemplate($data->template_id);
2077  $this->setPoolUsage($data->pool_usage);
2078  $this->setOnline($data->online_status);
2079  $this->loadQuestions();
2080  }
2081  }
2082 
2089 function loadQuestions($active_id = "", $pass = NULL)
2090 {
2091  global $ilUser;
2092  global $ilDB;
2093 
2094  $this->questions = array();
2095  if (strcmp($active_id, "") == 0)
2096  {
2097  $active_id = $this->getActiveIdOfUser($ilUser->getId());
2098  }
2099  if ($this->isRandomTest())
2100  {
2101  if (is_null($pass))
2102  {
2103  $pass = $this->_getPass($active_id);
2104  }
2105  $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",
2106  array('integer', 'integer'),
2107  array($active_id, $pass)
2108  );
2109  // The following is a fix for random tests prior to ILIAS 3.8. If someone started a random test in ILIAS < 3.8, there
2110  // is only one test pass (pass = 0) in tst_test_rnd_qst while with ILIAS 3.8 there are questions for every test pass.
2111  // To prevent problems with tests started in an older version and continued in ILIAS 3.8, the first pass should be taken if
2112  // no questions are present for a newer pass.
2113  if ($result->numRows() == 0)
2114  {
2115  $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",
2116  array('integer'),
2117  array($active_id)
2118  );
2119  }
2120  }
2121  else
2122  {
2123  $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",
2124  array('integer'),
2125  array($this->test_id)
2126  );
2127  }
2128  $index = 1;
2129  while ($data = $ilDB->fetchAssoc($result))
2130  {
2131  $this->questions[$index++] = $data["question_fi"];
2132  }
2133 }
2134 
2143  {
2144  $this->introduction = $introduction;
2145  }
2146 
2154  public function setFinalStatement($a_statement = "")
2155  {
2156  $this->_finalstatement = $a_statement;
2157  }
2158 
2166  public function setShowInfo($a_info = 1)
2167  {
2168  $this->_showinfo = ($a_info) ? 1 : 0;
2169  }
2170 
2178  public function setForceJS($a_js = 1)
2179  {
2180  $this->_forcejs = ($a_js) ? 1 : 0;
2181  }
2182 
2190  public function setCustomStyle($a_customStyle = NULL)
2191  {
2192  $this->_customStyle = $a_customStyle;
2193  }
2194 
2202  public function getCustomStyle()
2203  {
2204  return (strlen($this->_customStyle)) ? $this->_customStyle : NULL;
2205  }
2206 
2214  public function getCustomStyles()
2215  {
2216  $css_path = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test");
2217  $css_path = str_replace("ta.css", "customstyles", $css_path) . "/";
2218  $customstyles = array();
2219  if (is_dir($css_path))
2220  {
2221  $results = array();
2222  include_once "./Services/Utilities/classes/class.ilFileUtils.php";
2224  if (is_array($results["file"]))
2225  {
2226  foreach ($results["file"] as $filename)
2227  {
2228  if (strpos($filename, ".css"))
2229  {
2230  array_push($customstyles, $filename);
2231  }
2232  }
2233  }
2234  }
2235  return $customstyles;
2236  }
2237 
2245  public function getTestStyleLocation($mode = "output")
2246  {
2247  if (strlen($this->getCustomStyle()))
2248  {
2249  $default = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test");
2250  $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $default);
2251  if (file_exists($custom))
2252  {
2253  $custom = ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2254  $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $custom);
2255  return $custom;
2256  }
2257  else
2258  {
2259  return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2260  }
2261  }
2262  else
2263  {
2264  return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2265  }
2266  }
2267 
2275  public function setShowFinalStatement($show = 0)
2276  {
2277  $this->_showfinalstatement = ($show) ? 1 : 0;
2278  }
2279 
2280 
2288  function isRandomTest()
2289  {
2290  return ($this->random_test) ? 1 : 0;
2291  }
2292 
2293 
2299  public static function _lookupRandomTest($a_obj_id)
2300  {
2301  global $ilDB;
2302 
2303  $query = "SELECT random_test FROM tst_tests ".
2304  "WHERE obj_fi = ".$ilDB->quote($a_obj_id,'integer');
2305  $res = $ilDB->query($query);
2306  while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
2307  {
2308  return $row->random_test ? true : false;
2309  }
2310  return false;
2311  }
2312 
2321  {
2322  return ($this->random_question_count) ? $this->random_question_count : 0;
2323  }
2324 
2331  public function getIntroduction()
2332  {
2333  return (strlen($this->introduction)) ? $this->introduction : NULL;
2334  }
2335 
2342  public function getFinalStatement()
2343  {
2344  return (strlen($this->_finalstatement)) ? $this->_finalstatement : NULL;
2345  }
2346 
2354  public function getShowInfo()
2355  {
2356  return ($this->_showinfo) ? 1 : 0;
2357  }
2358 
2366  public function getForceJS()
2367  {
2368  return ($this->_forcejs) ? 1 : 0;
2369  }
2370 
2378  public function getShowFinalStatement()
2379  {
2380  return ($this->_showfinalstatement) ? 1 : 0;
2381  }
2382 
2390  function getTestId()
2391  {
2392  return $this->test_id;
2393  }
2394 
2402  function getECTSOutput()
2403  {
2404  return ($this->ects_output) ? 1 : 0;
2405  }
2406 
2414  function setECTSOutput($a_ects_output)
2415  {
2416  $this->ects_output = $a_ects_output ? 1 : 0;
2417  }
2418 
2426  function getECTSFX()
2427  {
2428  return (strlen($this->ects_fx)) ? $this->ects_fx : NULL;
2429  }
2430 
2438  function setECTSFX($a_ects_fx)
2439  {
2440  $this->ects_fx = $a_ects_fx;
2441  }
2442 
2450  function &getECTSGrades()
2451  {
2452  return $this->ects_grades;
2453  }
2454 
2462  function setECTSGrades($a_ects_grades)
2463  {
2464  if (is_array($a_ects_grades))
2465  {
2466  $this->ects_grades = $a_ects_grades;
2467  }
2468  }
2469 
2478  {
2479  $this->sequence_settings = $sequence_settings;
2480  }
2481 
2490  {
2491  $this->score_reporting = $score_reporting;
2492  }
2493 
2501  function setInstantFeedbackSolution($instant_feedback = 0)
2502  {
2503  switch ($instant_feedback)
2504  {
2505  case 1:
2506  $this->instant_verification = 1;
2507  break;
2508  default:
2509  $this->instant_verification = 0;
2510  break;
2511  }
2512  }
2513 
2522  {
2523  switch ($answer_feedback)
2524  {
2525  case 1:
2526  $this->answer_feedback = 1;
2527  break;
2528  default:
2529  $this->answer_feedback = 0;
2530  break;
2531  }
2532  }
2533 
2542  {
2543  switch ($answer_feedback_points)
2544  {
2545  case 1:
2546  $this->answer_feedback_points = 1;
2547  break;
2548  default:
2549  $this->answer_feedback_points = 0;
2550  break;
2551  }
2552  }
2553 
2561  function setRandomTest($a_random_test = 0)
2562  {
2563  $this->random_test = $a_random_test;
2564  }
2565 
2573  function setRandomQuestionCount($a_random_question_count = "")
2574  {
2575  $this->random_question_count = $a_random_question_count;
2576  }
2577 
2586  {
2587  if (!$reporting_date)
2588  {
2589  $this->reporting_date = "";
2590  $this->ects_output = 0;
2591  }
2592  else
2593  {
2594  $this->reporting_date = $reporting_date;
2595  }
2596  }
2597 
2606  {
2607  return ($this->sequence_settings) ? $this->sequence_settings : 0;
2608  }
2609 
2618  {
2619  return ($this->score_reporting) ? $this->score_reporting : 0;
2620  }
2621 
2630  {
2631  return ($this->instant_verification) ? $this->instant_verification : 0;
2632  }
2633 
2642  {
2643  return ($this->answer_feedback) ? $this->answer_feedback : 0;
2644  }
2645 
2654  {
2655  return ($this->answer_feedback_points) ? $this->answer_feedback_points : 0;
2656  }
2657 
2665  function getCountSystem()
2666  {
2667  return ($this->count_system) ? $this->count_system : 0;
2668  }
2669 
2677  function _getCountSystem($active_id)
2678  {
2679  global $ilDB;
2680  $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",
2681  array('integer'),
2682  array($active_id)
2683  );
2684  if ($result->numRows())
2685  {
2686  $row = $ilDB->fetchAssoc($result);
2687  return $row["count_system"];
2688  }
2689  return FALSE;
2690  }
2691 
2699  function getMCScoring()
2700  {
2701  return ($this->mc_scoring) ? $this->mc_scoring : 0;
2702  }
2703 
2711  function getScoreCutting()
2712  {
2713  return ($this->score_cutting) ? $this->score_cutting : 0;
2714  }
2715 
2723  function getPassword()
2724  {
2725  return (strlen($this->password)) ? $this->password : NULL;
2726  }
2727 
2735  function getPassScoring()
2736  {
2737  return ($this->pass_scoring) ? $this->pass_scoring : 0;
2738  }
2739 
2747  function _getPassScoring($active_id)
2748  {
2749  global $ilDB;
2750  $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",
2751  array('integer'),
2752  array($active_id)
2753  );
2754  if ($result->numRows())
2755  {
2756  $row = $ilDB->fetchAssoc($result);
2757  return $row["pass_scoring"];
2758  }
2759  return 0;
2760  }
2761 
2769  function _getMCScoring($active_id)
2770  {
2771  global $ilDB;
2772  $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",
2773  array('integer'),
2774  array($active_id)
2775  );
2776  if ($result->numRows())
2777  {
2778  $row = $ilDB->fetchAssoc($result);
2779  return $row["mc_scoring"];
2780  }
2781  return FALSE;
2782  }
2783 
2791  function _getScoreCutting($active_id)
2792  {
2793  global $ilDB;
2794  $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",
2795  array('integer'),
2796  array($active_id)
2797  );
2798  if ($result->numRows())
2799  {
2800  $row = $ilDB->fetchAssoc($result);
2801  return $row["score_cutting"];
2802  }
2803  return FALSE;
2804  }
2805 
2813  function getReportingDate()
2814  {
2815  return (strlen($this->reporting_date)) ? $this->reporting_date : NULL;
2816  }
2817 
2825  function getNrOfTries()
2826  {
2827  return ($this->nr_of_tries) ? $this->nr_of_tries : 0;
2828  }
2829 
2837  function getKiosk()
2838  {
2839  return ($this->_kiosk) ? $this->_kiosk : 0;
2840  }
2841 
2842 
2850  function setKiosk($kiosk = 0)
2851  {
2852  $this->_kiosk = $kiosk;
2853  }
2854 
2862  function getKioskMode()
2863  {
2864  if (($this->_kiosk & 1) > 0)
2865  {
2866  return TRUE;
2867  }
2868  else
2869  {
2870  return FALSE;
2871  }
2872  }
2873 
2881  public function setKioskMode($a_kiosk = FALSE)
2882  {
2883  if ($a_kiosk)
2884  {
2885  $this->_kiosk = $this->_kiosk | 1;
2886  }
2887  else
2888  {
2889  if ($this->getKioskMode())
2890  {
2891  $this->_kiosk = $this->_kiosk ^ 1;
2892  }
2893  }
2894  }
2895 
2903  public function getShowKioskModeTitle()
2904  {
2905  if (($this->_kiosk & 2) > 0)
2906  {
2907  return TRUE;
2908  }
2909  else
2910  {
2911  return FALSE;
2912  }
2913  }
2914 
2921  public function setShowKioskModeTitle($a_title = FALSE)
2922  {
2923  if ($a_title)
2924  {
2925  $this->_kiosk = $this->_kiosk | 2;
2926  }
2927  else
2928  {
2929  if ($this->getShowKioskModeTitle())
2930  {
2931  $this->_kiosk = $this->_kiosk ^ 2;
2932  }
2933  }
2934  }
2935 
2944  {
2945  if (($this->_kiosk & 4) > 0)
2946  {
2947  return TRUE;
2948  }
2949  else
2950  {
2951  return FALSE;
2952  }
2953  }
2954 
2961  public function setShowKioskModeParticipant($a_participant = FALSE)
2962  {
2963  if ($a_participant)
2964  {
2965  $this->_kiosk = $this->_kiosk | 4;
2966  }
2967  else
2968  {
2969  if ($this->getShowKioskModeParticipant())
2970  {
2971  $this->_kiosk = $this->_kiosk ^ 4;
2972  }
2973  }
2974  }
2975 
2984  {
2985  return ($this->use_previous_answers) ? $this->use_previous_answers : 0;
2986  }
2987 
2995  function getTitleOutput()
2996  {
2997  return ($this->title_output) ? $this->title_output : 0;
2998  }
2999 
3008  function _getTitleOutput($active_id)
3009  {
3010  global $ilDB;
3011 
3012  $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",
3013  array('integer'),
3014  array($active_id)
3015  );
3016  if ($result->numRows())
3017  {
3018  $row = $ilDB->fetchAssoc($result);
3019  return $row["title_output"];
3020  }
3021  return 0;
3022  }
3023 
3033  function _getUsePreviousAnswers($active_id, $user_active_user_setting = false)
3034  {
3035  global $ilDB;
3036  global $ilUser;
3037 
3039 
3040  $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",
3041  array("integer"),
3042  array($active_id)
3043  );
3044  if ($result->numRows())
3045  {
3046  $row = $ilDB->fetchAssoc($result);
3047  $use_previous_answers = $row["use_previous_answers"];
3048  }
3049 
3050  if ($use_previous_answers == 1)
3051  {
3052  if ($user_active_user_setting)
3053  {
3054  $res = $ilUser->getPref("tst_use_previous_answers");
3055  if ($res !== FALSE)
3056  {
3058  }
3059  }
3060  }
3061  return $use_previous_answers;
3062  }
3063 
3072  {
3073  return (strlen($this->processing_time)) ? $this->processing_time : NULL;
3074  }
3075 
3082  public function getProcessingTimeAsArray()
3083  {
3084  if (strlen($this->processing_time))
3085  {
3086  if (preg_match("/(\d{2}):(\d{2}):(\d{2})/is", $this->processing_time, $matches))
3087  {
3088  if ((int)$matches[1]+(int)$matches[2]+(int)$matches[3] == 0)
3089  {
3090  return $this->getEstimatedWorkingTime();
3091  }
3092  else
3093  {
3094  return array(
3095  'hh' => $matches[1],
3096  'mm' => $matches[2],
3097  'ss' => $matches[3],
3098  );
3099  }
3100  }
3101  }
3102  return $this->getEstimatedWorkingTime();
3103  }
3104 
3113  {
3114  if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $this->getProcessingTime(), $matches))
3115  {
3116  return ($matches[1] * 3600) + ($matches[2] * 60) + $matches[3];
3117  }
3118  else
3119  {
3120  return 0;
3121  }
3122  }
3123 
3132  {
3133  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndingTime(), $matches))
3134  {
3135  $ending = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
3136  $now = time();
3137  return $ending - $now;
3138  }
3139  else
3140  {
3141  return 0;
3142  }
3143  }
3144 
3153  {
3154  return ($this->enable_processing_time) ? $this->enable_processing_time : 0;
3155  }
3156 
3165  {
3166  return ($this->reset_processing_time) ? $this->reset_processing_time : 0;
3167  }
3168 
3176  function getStartingTime()
3177  {
3178  return (strlen($this->starting_time)) ? $this->starting_time : NULL;
3179  }
3180 
3188  function getEndingTime()
3189  {
3190  return (strlen($this->ending_time)) ? $this->ending_time : NULL;
3191  }
3192 
3201  {
3202  $this->nr_of_tries = $nr_of_tries;
3203  }
3204 
3213  {
3215  {
3216  $this->use_previous_answers = 1;
3217  }
3218  else
3219  {
3220  $this->use_previous_answers = 0;
3221  }
3222  }
3223 
3232  {
3233  switch ($title_output)
3234  {
3235  case 1:
3236  $this->title_output = 1;
3237  break;
3238  case 2:
3239  $this->title_output = 2;
3240  break;
3241  default:
3242  $this->title_output = 0;
3243  break;
3244  }
3245  }
3246 
3254  function setProcessingTime($processing_time = "00:00:00")
3255  {
3256  $this->processing_time = $processing_time;
3257  }
3258 
3266  function setEnableProcessingTime($enable = 0)
3267  {
3268  if ($enable) {
3269  $this->enable_processing_time = "1";
3270  } else {
3271  $this->enable_processing_time = "0";
3272  }
3273  }
3274 
3282  function setResetProcessingTime($reset = 0)
3283  {
3284  if ($reset)
3285  {
3286  $this->reset_processing_time = 1;
3287  }
3288  else
3289  {
3290  $this->reset_processing_time = 0;
3291  }
3292  }
3293 
3302  {
3303  $this->starting_time = $starting_time;
3304  }
3305 
3313  function setEndingTime($ending_time = NULL)
3314  {
3315  $this->ending_time = $ending_time;
3316  }
3317 
3325  function setCountSystem($a_count_system = COUNT_PARTIAL_SOLUTIONS)
3326  {
3327  $this->count_system = $a_count_system;
3328  }
3329 
3337  function setPassword($a_password = NULL)
3338  {
3339  $this->password = $a_password;
3340  }
3341 
3349  function setScoreCutting($a_score_cutting = SCORE_CUT_QUESTION)
3350  {
3351  $this->score_cutting = $a_score_cutting;
3352  }
3353 
3362  {
3363  $this->mc_scoring = $a_mc_scoring;
3364  }
3365 
3373  function setPassScoring($a_pass_scoring = SCORE_LAST_PASS)
3374  {
3375  switch ($a_pass_scoring)
3376  {
3377  case SCORE_BEST_PASS:
3378  $this->pass_scoring = SCORE_BEST_PASS;
3379  break;
3380  default:
3381  $this->pass_scoring = SCORE_LAST_PASS;
3382  break;
3383  }
3384  }
3385 
3393  function removeQuestion($question_id)
3394  {
3395  $question =& ilObjTest::_instanciateQuestion($question_id);
3396  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3398  {
3399  $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id);
3400  }
3401  $question->delete($question_id);
3402  $this->removeAllTestEditings($question_id);
3403  $this->loadQuestions();
3404  $this->saveQuestionsToDb();
3405  }
3406 
3414  public function removeAllTestEditings($question_id = "")
3415  {
3416  global $ilDB;
3417 
3418  // remove the question from tst_solutions
3419  if ($question_id)
3420  {
3421  $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",
3422  array('integer','integer'),
3423  array($this->getTestId(), $question_id)
3424  );
3425  $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",
3426  array('integer','integer'),
3427  array($this->getTestId(), $question_id)
3428  );
3429  $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",
3430  array('integer','integer'),
3431  array($this->getTestId(), $question_id)
3432  );
3433  $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)",
3434  array('integer'),
3435  array($this->getTestId())
3436  );
3437  }
3438  else
3439  {
3440  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE tst_solutions.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3441  array('integer'),
3442  array($this->getTestId())
3443  );
3444  $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)",
3445  array('integer'),
3446  array($this->getTestId())
3447  );
3448  $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)",
3449  array('integer'),
3450  array($this->getTestId())
3451  );
3452  $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)",
3453  array('integer'),
3454  array($this->getTestId())
3455  );
3456  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3458  {
3459  $this->logAction($this->lng->txtlng("assessment", "log_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()));
3460  }
3461  }
3462  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE tst_sequence.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3463  array('integer'),
3464  array($this->getTestId())
3465  );
3466 
3467  if ($this->isRandomTest())
3468  {
3469  $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)",
3470  array('integer'),
3471  array($this->getTestId())
3472  );
3473  }
3474 
3475  // remove test_active entries, because test has changed
3476  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
3477  array('integer'),
3478  array($this->getTestId())
3479  );
3480 
3481  // remove saved user passwords
3482  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE keyword = %s",
3483  array('text'),
3484  array("tst_password_".$this->getTestId())
3485  );
3486 
3487  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3488  // remove file uploads
3489  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId()))
3490  {
3491  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId());
3492  }
3493  }
3494 
3495  function removeSelectedTestResults($active_ids)
3496  {
3497  global $ilDB;
3498 
3499  // remove the question from tst_solutions
3500  foreach ($active_ids as $active_id)
3501  {
3502  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s",
3503  array('integer'),
3504  array($active_id)
3505  );
3506  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s",
3507  array('integer'),
3508  array($active_id)
3509  );
3510  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE active_fi = %s",
3511  array('integer'),
3512  array($active_id)
3513  );
3514  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE active_fi = %s",
3515  array('integer'),
3516  array($active_id)
3517  );
3518 
3519  if ($this->isRandomTest())
3520  {
3521  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE active_fi = %s",
3522  array('integer'),
3523  array($active_id)
3524  );
3525  }
3526 
3527  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3529  {
3530  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id))));
3531  }
3532  }
3533 
3534  // remove test_active entries of selected users
3535  foreach ($active_ids as $active_id)
3536  {
3537  $usr_id = $this->_getUserIdFromActiveId($active_id);
3538 
3539  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE active_id = %s",
3540  array('integer'),
3541  array($active_id)
3542  );
3543 
3544  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
3545  array('integer'),
3546  array($active_id)
3547  );
3548 
3549  // remove saved user password
3550  if ($usr_id > 0)
3551  {
3552  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE usr_id = %s AND keyword = %s",
3553  array('integer', 'text'),
3554  array($usr_id, "tst_password_".$this->getTestId())
3555  );
3556  }
3557 
3558  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3559  // remove file uploads
3560  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"))
3561  {
3562  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id");
3563  }
3564  }
3565  }
3566 
3567  function removeTestResultsForUser($user_id)
3568  {
3569  global $ilDB;
3570 
3571  $active_id = $this->getActiveIdOfUser($user_id);
3572 
3573  // remove the question from tst_solutions
3574  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s",
3575  array('integer'),
3576  array($active_id)
3577  );
3578  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s",
3579  array('integer'),
3580  array($active_id)
3581  );
3582  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE active_fi = %s",
3583  array('integer'),
3584  array($active_id)
3585  );
3586  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE active_fi = %s",
3587  array('integer'),
3588  array($active_id)
3589  );
3590 
3591  if ($this->isRandomTest())
3592  {
3593  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE active_fi = %s",
3594  array('integer'),
3595  array($active_id)
3596  );
3597  }
3598 
3599  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3601  {
3602  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id))));
3603  }
3604 
3605  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
3606  array('integer'),
3607  array($active_id)
3608  );
3609 
3610  // remove test_active entry
3611  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE active_id = %s",
3612  array('integer'),
3613  array($active_id)
3614  );
3615 
3616  // remove saved user password
3617  if ($user_id > 0)
3618  {
3619  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE usr_id = %s AND keyword = %s",
3620  array('integer', 'text'),
3621  array($user_id, "tst_password_".$this->getTestId())
3622  );
3623  }
3624 
3625  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3626  // remove file uploads
3627  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"))
3628  {
3629  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id");
3630  }
3631  }
3632 
3640  function questionMoveUp($question_id)
3641  {
3642  global $ilDB;
3643 
3644  // Move a question up in sequence
3645  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
3646  array('integer', 'integer'),
3647  array($this->getTestId(), $question_id)
3648  );
3649  $data = $ilDB->fetchObject($result);
3650  if ($data->sequence > 1)
3651  {
3652  // OK, it's not the top question, so move it up
3653  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
3654  array('integer','integer'),
3655  array($this->getTestId(), $data->sequence - 1)
3656  );
3657  $data_previous = $ilDB->fetchObject($result);
3658  // change previous dataset
3659  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3660  array('integer','integer'),
3661  array($data->sequence, $data_previous->test_question_id)
3662  );
3663  // move actual dataset up
3664  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3665  array('integer','integer'),
3666  array($data->sequence - 1, $data->test_question_id)
3667  );
3668  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3670  {
3671  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence-1), $question_id);
3672  }
3673  }
3674  $this->loadQuestions();
3675  }
3676 
3684  function questionMoveDown($question_id)
3685  {
3686  global $ilDB;
3687 
3688  // Move a question down in sequence
3689  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
3690  array('integer','integer'),
3691  array($this->getTestId(), $question_id)
3692  );
3693  $data = $ilDB->fetchObject($result);
3694  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
3695  array('integer','integer'),
3696  array($this->getTestId(), $data->sequence + 1)
3697  );
3698  if ($result->numRows() == 1)
3699  {
3700  // OK, it's not the last question, so move it down
3701  $data_next = $ilDB->fetchObject($result);
3702  // change next dataset
3703  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3704  array('integer','integer'),
3705  array($data->sequence, $data_next->test_question_id)
3706  );
3707  // move actual dataset down
3708  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3709  array('integer','integer'),
3710  array($data->sequence + 1, $data->test_question_id)
3711  );
3712  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3714  {
3715  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence+1), $question_id);
3716  }
3717  }
3718  $this->loadQuestions();
3719  }
3720 
3728  function duplicateQuestionForTest($question_id)
3729  {
3730  global $ilUser;
3731  $question =& ilObjTest::_instanciateQuestion($question_id);
3732  $duplicate_id = $question->duplicate(true, null, null, null, $this->getId());
3733 
3734  return $duplicate_id;
3735  }
3736 
3743  function insertQuestion($question_id, $linkOnly = false)
3744  {
3745  global $ilDB;
3746 #var_dump($question_id);
3747  if ($linkOnly)
3748  $duplicate_id = $question_id;
3749  else
3750  $duplicate_id = $this->duplicateQuestionForTest($question_id);
3751 
3752  // get maximum sequence index in test
3753  $result = $ilDB->queryF("SELECT MAX(sequence) seq FROM tst_test_question WHERE test_fi=%s",
3754  array('integer'),
3755  array($this->getTestId())
3756  );
3757  $sequence = 1;
3758 
3759  if ($result->numRows() == 1)
3760  {
3761  $data = $ilDB->fetchObject($result);
3762  $sequence = $data->seq + 1;
3763  }
3764 
3765  $next_id = $ilDB->nextId('tst_test_question');
3766  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
3767  array('integer', 'integer','integer','integer','integer'),
3768  array($next_id, $this->getTestId(), $duplicate_id, $sequence, time())
3769  );
3770  if ($affectedRows == 1)
3771  {
3772  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3774  {
3775  $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . $sequence, $duplicate_id);
3776  }
3777  }
3778  // remove test_active entries, because test has changed
3779  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
3780  array('integer'),
3781  array($this->getTestId())
3782  );
3783  $this->loadQuestions();
3784  $this->saveCompleteStatus();
3785  return $duplicate_id;
3786  }
3787 
3795  function &getQuestionTitles()
3796  {
3797  $titles = array();
3798  if (!$this->isRandomTest())
3799  {
3800  global $ilDB;
3801  $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",
3802  array('integer'),
3803  array($this->getTestId())
3804  );
3805  while ($row = $ilDB->fetchAssoc($result))
3806  {
3807  array_push($titles, $row["title"]);
3808  }
3809  }
3810  return $titles;
3811  }
3812 
3821  {
3822  $titles = array();
3823  if (!$this->isRandomTest())
3824  {
3825  global $ilDB;
3826  $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",
3827  array('integer'),
3828  array($this->getTestId())
3829  );
3830  while ($row = $ilDB->fetchAssoc($result))
3831  {
3832  $titles[$row['question_id']] = $row["title"];
3833  }
3834  }
3835  return $titles;
3836  }
3837 
3847  {
3848  if ($this->getTitleOutput() == 2)
3849  {
3850  return $this->lng->txt("ass_question");
3851  }
3852  else
3853  {
3854  return $title;
3855  }
3856  }
3857 
3866  function getQuestionDataset($question_id)
3867  {
3868  global $ilDB;
3869 
3870  $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",
3871  array('integer'),
3872  array($question_id)
3873  );
3874  $row = $ilDB->fetchObject($result);
3875  return $row;
3876  }
3877 
3884  function &getExistingQuestions($pass = NULL)
3885  {
3886  global $ilUser;
3887  global $ilDB;
3888 
3889  $existing_questions = array();
3890  $active_id = $this->getActiveIdOfUser($ilUser->getId());
3891  if ($this->isRandomTest())
3892  {
3893  if (is_null($pass)) $pass = 0;
3894  $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",
3895  array('integer','integer'),
3896  array($active_id, $pass)
3897  );
3898  }
3899  else
3900  {
3901  $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",
3902  array('integer'),
3903  array($this->getTestId())
3904  );
3905  }
3906  while ($data = $ilDB->fetchObject($result))
3907  {
3908  array_push($existing_questions, $data->original_id);
3909  }
3910  return $existing_questions;
3911  }
3912 
3920  function getQuestionType($question_id)
3921  {
3922  global $ilDB;
3923 
3924  if ($question_id < 1) return -1;
3925  $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",
3926  array('integer'),
3927  array($question_id)
3928  );
3929  if ($result->numRows() == 1)
3930  {
3931  $data = $ilDB->fetchObject($result);
3932  return $data->type_tag;
3933  }
3934  else
3935  {
3936  return "";
3937  }
3938  }
3939 
3946  function startWorkingTime($active_id, $pass)
3947  {
3948  global $ilDB;
3949 
3950  $next_id = $ilDB->nextId('tst_times');
3951  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_times (times_id, active_fi, started, finished, pass, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
3952  array('integer', 'integer', 'timestamp', 'timestamp', 'integer', 'integer'),
3953  array($next_id, $active_id, strftime("%Y-%m-%d %H:%M:%S"), strftime("%Y-%m-%d %H:%M:%S"), $pass, time())
3954  );
3955  return $next_id;
3956  }
3957 
3964  function updateWorkingTime($times_id)
3965  {
3966  global $ilDB;
3967 
3968  $affectedRows = $ilDB->manipulateF("UPDATE tst_times SET finished = %s, tstamp = %s WHERE times_id = %s",
3969  array('timestamp', 'integer', 'integer'),
3970  array(strftime("%Y-%m-%d %H:%M:%S"), time(), $times_id)
3971  );
3972  }
3973 
3980  function &getWorkedQuestions($active_id, $pass = NULL)
3981  {
3982  global $ilUser;
3983  global $ilDB;
3984 
3985  if (is_null($pass))
3986  {
3987  $result = $ilDB->queryF("SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
3988  array('integer','integer'),
3989  array($active_id, 0)
3990  );
3991  }
3992  else
3993  {
3994  $result = $ilDB->queryF("SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
3995  array('integer','integer'),
3996  array($active_id, $pass)
3997  );
3998  }
3999  $result_array = array();
4000  while ($row = $ilDB->fetchAssoc($result))
4001  {
4002  array_push($result_array, $row["question_fi"]);
4003  }
4004  return $result_array;
4005  }
4006 
4015  function isTestFinishedToViewResults($active_id, $currentpass)
4016  {
4017  $num = $this->getPassFinishDate($active_id, $currentpass);
4018  return ((($currentpass > 0) && ($num == 0)) || $this->isTestFinished($active_id)) ? true : false;
4019  }
4020 
4027  function &getAllQuestions($pass = NULL)
4028  {
4029  global $ilUser;
4030  global $ilDB;
4031 
4032  $result_array = array();
4033  if ($this->isRandomTest())
4034  {
4035  $active_id = $this->getActiveIdOfUser($ilUser->getId());
4036  $this->loadQuestions($active_id, $pass);
4037  if (count($this->questions) == 0) return $result_array;
4038  if (is_null($pass))
4039  {
4040  $pass = $this->_getPass($active_id);
4041  }
4042  $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'),
4043  array('integer','integer'),
4044  array($active_id, $pass)
4045  );
4046  }
4047  else
4048  {
4049  if (count($this->questions) == 0) return $result_array;
4050  $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'));
4051  }
4052  while ($row = $ilDB->fetchAssoc($result))
4053  {
4054  $result_array[$row["question_id"]] = $row;
4055  }
4056  return $result_array;
4057  }
4058 
4067  function getActiveIdOfUser($user_id = "", $anonymous_id = "")
4068  {
4069  global $ilDB;
4070  global $ilUser;
4071 
4072  if (!$user_id) $user_id = $ilUser->getId();
4073  if (($_SESSION["AccountId"] == ANONYMOUS_USER_ID) && (strlen($_SESSION["tst_access_code"][$this->getTestId()])))
4074  {
4075  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
4076  array('integer','integer','text'),
4077  array($user_id, $this->test_id, $_SESSION["tst_access_code"][$this->getTestId()])
4078  );
4079  }
4080  else if (strlen($anonymous_id))
4081  {
4082  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
4083  array('integer','integer','text'),
4084  array($user_id, $this->test_id, $anonymous_id)
4085  );
4086  }
4087  else
4088  {
4089  if ($_SESSION["AccountId"] == ANONYMOUS_USER_ID)
4090  {
4091  return NULL;
4092  }
4093  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
4094  array('integer','integer'),
4095  array($user_id, $this->test_id)
4096  );
4097  }
4098  if ($result->numRows())
4099  {
4100  $row = $ilDB->fetchAssoc($result);
4101  return $row["active_id"];
4102  }
4103  else
4104  {
4105  return 0;
4106  }
4107  }
4108 
4117  function _getActiveIdOfUser($user_id = "", $test_id = "")
4118  {
4119  global $ilDB;
4120  global $ilUser;
4121 
4122  if (!$user_id) {
4123  $user_id = $ilUser->id;
4124  }
4125  if (!$test_id)
4126  {
4127  return "";
4128  }
4129  $result = $ilDB->queryF("SELECT tst_active.active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
4130  array('integer', 'integer'),
4131  array($user_id, $test_id)
4132  );
4133  if ($result->numRows())
4134  {
4135  $row = $ilDB->fetchAssoc($result);
4136  return $row["active_id"];
4137  }
4138  else
4139  {
4140  return "";
4141  }
4142  }
4143 
4150  function pcArrayShuffle($array)
4151  {
4152  $keys = array_keys($array);
4153  shuffle($keys);
4154  $result = array();
4155  foreach ($keys as $key)
4156  {
4157  $result[$key] = $array[$key];
4158  }
4159  return $result;
4160  }
4161 
4169  function &getTestResult($active_id, $pass = NULL, $ordered_sequence = FALSE)
4170  {
4171  global $ilDB;
4172 
4173  $results = $this->getResultsForActiveId($active_id);
4174  if (is_null($pass))
4175  {
4176  $pass = $results['pass'];
4177  }
4178  include_once "./Modules/Test/classes/class.ilTestSequence.php";
4179  $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
4180  $sequence = array();
4181  if ($ordered_sequence)
4182  {
4183  $sequence = $testSequence->getOrderedSequenceQuestions();
4184  }
4185  else
4186  {
4187  $sequence = $testSequence->getUserSequenceQuestions();
4188  }
4189  $arrResults = array();
4190  $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",
4191  array('integer', 'integer'),
4192  array($active_id, $pass)
4193  );
4194  while ($row = $ilDB->fetchAssoc($solutionresult))
4195  {
4196  $arrResults[$row['question_fi']] = $row;
4197  }
4198 
4199  require_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
4200  $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'));
4201  $found = array();
4202  $unordered = array();
4203  $key = 1;
4204  while ($row = $ilDB->fetchAssoc($result))
4205  {
4206  $percentvalue = ($row['points']) ? $arrResults[$row['question_id']]['reached'] / $row['points'] : 0;
4207  if ($percentvalue < 0) $percentvalue = 0.0;
4208  $data = array(
4209  "nr" => "$key",
4210  "title" => ilUtil::prepareFormOutput($row['title']),
4211  "max" => round($row['points'], 2),
4212  "reached" => round($arrResults[$row['question_id']]['reached'],2),
4213  "percent" => sprintf("%2.2f ", ($percentvalue) * 100) . "%",
4214  "solution" => ($row['has_sug_sol']) ? assQuestion::_getSuggestedSolutionOutput($row['question_id']) : '',
4215  "type" => $row["type_tag"],
4216  "qid" => $row['question_id'],
4217  "original_id" => $row["original_id"],
4218  "workedthrough" => ($arrResults[$row['question_id']]['workedthru']) ? 1 : 0
4219  );
4220  $unordered[$row['question_id']] = $data;
4221  $key++;
4222  }
4223 
4224  $pass_max = 0;
4225  $pass_reached = 0;
4226  $key = 1;
4227  foreach ($sequence as $qid)
4228  {
4229  // building pass point sums based on prepared data
4230  // for question that exists in users qst sequence
4231  $pass_max += round($unordered[$qid]['max'], 2);
4232  $pass_reached += round($unordered[$qid]['reached'], 2);
4233 
4234  // pickup prepared data for question
4235  // that exists in users qst sequence
4236  $unordered[$qid]['nr'] = $key;
4237  array_push($found, $unordered[$qid]);
4238 
4239  // increment key counter
4240  $key++;
4241  }
4242  $unordered = null;
4243  if ($this->getScoreCutting() == 1)
4244  {
4245  if ($results['reached_points'] < 0)
4246  {
4247  $results['reached_points'] = 0;
4248  }
4249  if ($pass_reached < 0) $pass_reached = 0;
4250  }
4251  $found['pass']['total_max_points'] = $pass_max;
4252  $found['pass']['total_reached_points'] = $pass_reached;
4253  $found['pass']['percent'] = ($pass_max > 0) ? $pass_reached / $pass_max : 0;
4254  $found["test"]["total_max_points"] = $results['max_points'];
4255  $found["test"]["total_reached_points"] = $results['reached_points'];
4256  $found["test"]["result_pass"] = $results['pass'];
4257  if ((!$total_reached_points) or (!$total_max_points))
4258  {
4259  $percentage = 0.0;
4260  }
4261  else
4262  {
4263  $percentage = ($total_reached_points / $total_max_points) * 100.0;
4264  if ($percentage < 0) $percentage = 0.0;
4265  }
4266  $found["test"]["passed"] = $results['passed'];
4267  return $found;
4268  }
4269 
4276  function evalTotalPersons()
4277  {
4278  global $ilDB;
4279 
4280  $result = $ilDB->queryF("SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s",
4281  array('integer'),
4282  array($this->getTestId())
4283  );
4284  $row = $ilDB->fetchAssoc($result);
4285  return $row["total"];
4286  }
4287 
4294  function getCompleteWorkingTime($user_id)
4295  {
4296  global $ilDB;
4297 
4298  $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",
4299  array('integer','integer'),
4300  array($this->getTestId(), $user_id)
4301  );
4302  $time = 0;
4303  while ($row = $ilDB->fetchAssoc($result))
4304  {
4305  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4306  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4307  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4308  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4309  $time += ($epoch_2 - $epoch_1);
4310  }
4311  return $time;
4312  }
4313 
4321  {
4322  return $this->_getCompleteWorkingTimeOfParticipants($this->getTestId());
4323  }
4324 
4333  {
4334  global $ilDB;
4335 
4336  $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",
4337  array('integer'),
4338  array($test_id)
4339  );
4340  $time = 0;
4341  $times = array();
4342  while ($row = $ilDB->fetchAssoc($result))
4343  {
4344  if (!array_key_exists($row["active_fi"], $times))
4345  {
4346  $times[$row["active_fi"]] = 0;
4347  }
4348  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4349  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4350  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4351  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4352  $times[$row["active_fi"]] += ($epoch_2 - $epoch_1);
4353  }
4354  return $times;
4355  }
4356 
4364  {
4365  global $ilDB;
4366 
4367  $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",
4368  array('integer','integer'),
4369  array($this->getTestId(), $active_id)
4370  );
4371  $time = 0;
4372  while ($row = $ilDB->fetchAssoc($result))
4373  {
4374  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4375  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4376  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4377  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4378  $time += ($epoch_2 - $epoch_1);
4379  }
4380  return $time;
4381  }
4382 
4390  {
4391  global $ilDB;
4392 
4393  $result = $ilDB->queryF("SELECT * FROM tst_times WHERE active_fi = %s AND pass = %s ORDER BY started",
4394  array('integer','integer'),
4395  array($active_id, $pass)
4396  );
4397  $time = 0;
4398  while ($row = $ilDB->fetchAssoc($result))
4399  {
4400  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4401  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4402  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4403  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4404  $time += ($epoch_2 - $epoch_1);
4405  }
4406  return $time;
4407  }
4408 
4416  function getVisitTimeOfParticipant($active_id)
4417  {
4418  return ilObjTest::_getVisitTimeOfParticipant($this->getTestId(), $active_id);
4419  }
4420 
4429  function _getVisitTimeOfParticipant($test_id, $active_id)
4430  {
4431  global $ilDB;
4432 
4433  $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",
4434  array('integer','integer'),
4435  array($test_id, $active_id)
4436  );
4437  $firstvisit = 0;
4438  $lastvisit = 0;
4439  while ($row = $ilDB->fetchAssoc($result))
4440  {
4441  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4442  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4443  if ($firstvisit == 0 || $epoch_1 < $firstvisit) $firstvisit = $epoch_1;
4444  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4445  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4446  if ($epoch_2 > $lastvisit) $lastvisit = $epoch_2;
4447  }
4448  return array("firstvisit" => $firstvisit, "lastvisit" => $lastvisit);
4449  }
4450 
4457  function &evalStatistical($active_id)
4458  {
4459  global $ilDB;
4460 // global $ilBench;
4461  $pass = ilObjTest::_getResultPass($active_id);
4462  $test_result =& $this->getTestResult($active_id, $pass);
4463  $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",
4464  array('integer'),
4465  array($active_id)
4466  );
4467  $times = array();
4468  $first_visit = 0;
4469  $last_visit = 0;
4470  while ($row = $ilDB->fetchObject($result))
4471  {
4472  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
4473  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4474  if (!$first_visit) {
4475  $first_visit = $epoch_1;
4476  }
4477  if ($epoch_1 < $first_visit) {
4478  $first_visit = $epoch_1;
4479  }
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  if (!$last_visit) {
4483  $last_visit = $epoch_2;
4484  }
4485  if ($epoch_2 > $last_visit) {
4486  $last_visit = $epoch_2;
4487  }
4488  $times[$row->active_fi] += ($epoch_2 - $epoch_1);
4489  }
4490  $max_time = 0;
4491  foreach ($times as $key => $value) {
4492  $max_time += $value;
4493  }
4494  if ((!$test_result["test"]["total_reached_points"]) or (!$test_result["test"]["total_max_points"]))
4495  {
4496  $percentage = 0.0;
4497  }
4498  else
4499  {
4500  $percentage = ($test_result["test"]["total_reached_points"] / $test_result["test"]["total_max_points"]) * 100.0;
4501  if ($percentage < 0) $percentage = 0.0;
4502  }
4503  $mark_obj = $this->mark_schema->getMatchingMark($percentage);
4504  $first_date = getdate($first_visit);
4505  $last_date = getdate($last_visit);
4506  $qworkedthrough = 0;
4507  foreach ($test_result as $key => $value)
4508  {
4509  if (preg_match("/\d+/", $key))
4510  {
4511  $qworkedthrough += $value["workedthrough"];
4512  }
4513  }
4514  if (!$qworkedthrough)
4515  {
4516  $atimeofwork = 0;
4517  }
4518  else
4519  {
4520  $atimeofwork = $max_time / $qworkedthrough;
4521  }
4522  $result_mark = "";
4523  $passed = "";
4524  if ($mark_obj)
4525  {
4526  $result_mark = $mark_obj->getShortName();
4527  if ($mark_obj->getPassed())
4528  {
4529  $passed = 1;
4530  }
4531  else
4532  {
4533  $passed = 0;
4534  }
4535  }
4536  $percent_worked_through = 0;
4537  if (count($this->questions))
4538  {
4539  $percent_worked_through = $qworkedthrough / count($this->questions);
4540  }
4541  $result_array = array(
4542  "qworkedthrough" => $qworkedthrough,
4543  "qmax" => count($this->questions),
4544  "pworkedthrough" => $percent_worked_through,
4545  "timeofwork" => $max_time,
4546  "atimeofwork" => $atimeofwork,
4547  "firstvisit" => $first_date,
4548  "lastvisit" => $last_date,
4549  "resultspoints" => $test_result["test"]["total_reached_points"],
4550  "maxpoints" => $test_result["test"]["total_max_points"],
4551  "resultsmarks" => $result_mark,
4552  "passed" => $passed,
4553  "distancemedian" => "0"
4554  );
4555  foreach ($test_result as $key => $value)
4556  {
4557  if (preg_match("/\d+/", $key))
4558  {
4559  $result_array[$key] = $value;
4560  }
4561  }
4562  return $result_array;
4563  }
4564 
4573  {
4574  $totalpoints_array = array();
4575  $all_users =& $this->evalTotalParticipantsArray();
4576  foreach ($all_users as $active_id => $user_name)
4577  {
4578  $test_result =& $this->getTestResult($active_id);
4579  $reached = $test_result["test"]["total_reached_points"];
4580  $total = $test_result["test"]["total_max_points"];
4581  $percentage = $total != 0 ? $reached/$total : 0;
4582  $mark = $this->mark_schema->getMatchingMark($percentage*100.0);
4583  if ($mark)
4584  {
4585  if ($mark->getPassed())
4586  {
4587  array_push($totalpoints_array, $test_result["test"]["total_reached_points"]);
4588  }
4589  }
4590  }
4591  return $totalpoints_array;
4592  }
4593 
4600  function &getParticipants()
4601  {
4602  global $ilDB;
4603  $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",
4604  array('integer'),
4605  array($this->getTestId())
4606  );
4607  $persons_array = array();
4608  while ($row = $ilDB->fetchAssoc($result))
4609  {
4610  $name = $this->lng->txt("unknown");
4611  $fullname = $this->lng->txt("unknown");
4612  $login = "";
4613  if (!$this->getAnonymity())
4614  {
4615  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4616  {
4617  $name = $this->lng->txt("deleted_user");
4618  $fullname = $this->lng->txt("deleted_user");
4619  $login = $this->lng->txt("unknown");
4620  }
4621  else
4622  {
4623  $login = $row["login"];
4624  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4625  {
4626  $name = $this->lng->txt("unknown");
4627  $fullname = $this->lng->txt("unknown");
4628  }
4629  else
4630  {
4631  $name = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]);
4632  $fullname = trim($row["title"] . " " . $row["firstname"] . " " . $row["lastname"]);
4633  }
4634  }
4635  }
4636  $persons_array[$row["active_id"]] = array(
4637  "name" => $name,
4638  "fullname" => $fullname,
4639  "login" => $login
4640  );
4641  }
4642  return $persons_array;
4643  }
4644 
4651  function &evalTotalPersonsArray($name_sort_order = "asc")
4652  {
4653  global $ilDB;
4654  $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),
4655  array('integer'),
4656  array($this->getTestId())
4657  );
4658  $persons_array = array();
4659  while ($row = $ilDB->fetchAssoc($result))
4660  {
4661  if ($this->getAnonymity())
4662  {
4663  $persons_array[$row["active_id"]] = $this->lng->txt("unknown");
4664  }
4665  else
4666  {
4667  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4668  {
4669  $persons_array[$row["active_id"]] = $this->lng->txt("deleted_user");
4670  }
4671  else
4672  {
4673  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4674  {
4675  $persons_array[$row["active_id"]] = $row["lastname"];
4676  }
4677  else
4678  {
4679  $persons_array[$row["active_id"]] = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]);
4680  }
4681  }
4682  }
4683  }
4684  return $persons_array;
4685  }
4686 
4693  function &evalTotalParticipantsArray($name_sort_order = "asc")
4694  {
4695  global $ilDB;
4696  $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),
4697  array('integer'),
4698  array($this->getTestId())
4699  );
4700  $persons_array = array();
4701  while ($row = $ilDB->fetchAssoc($result))
4702  {
4703  if ($this->getAnonymity())
4704  {
4705  $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("unknown"));
4706  }
4707  else
4708  {
4709  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4710  {
4711  $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("deleted_user"));
4712  }
4713  else
4714  {
4715  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4716  {
4717  $persons_array[$row["active_id"]] = array("name" => $row["lastname"]);
4718  }
4719  else
4720  {
4721  $persons_array[$row["active_id"]] = array("name" => trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]), "login" => $row["login"]);
4722  }
4723  }
4724  }
4725  }
4726  return $persons_array;
4727  }
4728 
4736  {
4737  global $ilDB;
4738 
4739  $result = $ilDB->queryF("SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s AND submitted = %s",
4740  array('integer', 'integer'),
4741  array($this->getTestId(), 1)
4742  );
4743  $row = $ilDB->fetchAssoc($result);
4744  return $row["total"];
4745  }
4746 
4753  function &getQuestionsOfTest($active_id)
4754  {
4755  global $ilDB;
4756  if ($this->isRandomTest())
4757  {
4758  $ilDB->setLimit($this->getQuestionCount(), 0);
4759  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " .
4760  "tst_test_rnd_qst.pass, qpl_questions.points " .
4761  "FROM tst_test_rnd_qst, qpl_questions " .
4762  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4763  "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence",
4764  array('integer'),
4765  array($active_id)
4766  );
4767  }
4768  else
4769  {
4770  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4771  "qpl_questions.points " .
4772  "FROM tst_test_question, tst_active, qpl_questions " .
4773  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4774  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi",
4775  array('integer'),
4776  array($active_id)
4777  );
4778  }
4779  $qtest = array();
4780  if ($result->numRows())
4781  {
4782  while ($row = $ilDB->fetchAssoc($result))
4783  {
4784  array_push($qtest, $row);
4785  }
4786  }
4787  return $qtest;
4788  }
4789 
4796  function &getQuestionsOfPass($active_id, $pass)
4797  {
4798  global $ilDB;
4799  if ($this->isRandomTest())
4800  {
4801  $ilDB->setLimit($this->getQuestionCount(), 0);
4802  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " .
4803  "qpl_questions.points " .
4804  "FROM tst_test_rnd_qst, qpl_questions " .
4805  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4806  "AND tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.pass = %s " .
4807  "ORDER BY tst_test_rnd_qst.sequence",
4808  array('integer', 'integer'),
4809  array($active_id, $pass)
4810  );
4811  }
4812  else
4813  {
4814  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4815  "qpl_questions.points " .
4816  "FROM tst_test_question, tst_active, qpl_questions " .
4817  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4818  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi",
4819  array('integer'),
4820  array($active_id)
4821  );
4822  }
4823  $qpass = array();
4824  if ($result->numRows())
4825  {
4826  while ($row = $ilDB->fetchAssoc($result))
4827  {
4828  array_push($qpass, $row);
4829  }
4830  }
4831  return $qpass;
4832  }
4833 
4835  {
4836  global $ilDB;
4837  include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php";
4838  include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php";
4839  include_once "./Modules/Test/classes/class.ilTestEvaluationData.php";
4840  $data = new ilTestEvaluationData($this);
4841  $result = $ilDB->queryF("SELECT tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
4842  "qpl_questions.points maxpoints " .
4843  "FROM tst_test_result, qpl_questions, tst_active " .
4844  "WHERE tst_active.active_id = tst_test_result.active_fi " .
4845  "AND qpl_questions.question_id = tst_test_result.question_fi " .
4846  "AND tst_active.test_fi = %s " .
4847  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
4848  array('integer'),
4849  array($this->getTestId())
4850  );
4851  $pass = NULL;
4852  $checked = array();
4853  $datasets = 0;
4854  while ($row = $ilDB->fetchAssoc($result))
4855  {
4856  $data->getParticipant($row["active_fi"])->getPass($row["pass"])->addAnsweredQuestion($row["question_fi"], $row["maxpoints"], $row["points"]);
4857  }
4858 
4859  foreach (array_keys($data->getParticipants()) as $active_id)
4860  {
4861  if ($this->isRandomTest())
4862  {
4863  for ($testpass = 0; $testpass <= $data->getParticipant($active_id)->getLastPass(); $testpass++)
4864  {
4865  $ilDB->setLimit($this->getQuestionCount(), 0);
4866  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, qpl_questions.original_id, " .
4867  "tst_test_rnd_qst.pass, qpl_questions.points, qpl_questions.title " .
4868  "FROM tst_test_rnd_qst, qpl_questions " .
4869  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4870  "AND tst_test_rnd_qst.pass = %s " .
4871  "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence",
4872  array('integer','integer'),
4873  array($testpass, $active_id)
4874  );
4875  if ($result->numRows())
4876  {
4877  while ($row = $ilDB->fetchAssoc($result))
4878  {
4879  $tpass = array_key_exists("pass", $row) ? $row["pass"] : 0;
4880  $data->getParticipant($active_id)->addQuestion($row["original_id"], $row["question_fi"], $row["points"], $row["sequence"], $tpass);
4881  $data->addQuestionTitle($row["question_fi"], $row["title"]);
4882  }
4883  }
4884  }
4885  }
4886  else
4887  {
4888  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4889  "qpl_questions.points, qpl_questions.title, qpl_questions.original_id " .
4890  "FROM tst_test_question, tst_active, qpl_questions " .
4891  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4892  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi ORDER BY tst_test_question.sequence",
4893  array('integer'),
4894  array($active_id)
4895  );
4896  if ($result->numRows())
4897  {
4898  $questionsbysequence = array();
4899  while ($row = $ilDB->fetchAssoc($result))
4900  {
4901  $questionsbysequence[$row["sequence"]] = $row;
4902  }
4903  $seqresult = $ilDB->queryF("SELECT * FROM tst_sequence WHERE active_fi = %s",
4904  array('integer'),
4905  array($active_id)
4906  );
4907  while ($seqrow = $ilDB->fetchAssoc($seqresult))
4908  {
4909  $questionsequence = unserialize($seqrow["sequence"]);
4910  foreach ($questionsequence as $sidx => $seq)
4911  {
4912  $data->getParticipant($active_id)->addQuestion($questionsbysequence[$seq]["original_id"], $questionsbysequence[$seq]["question_fi"], $questionsbysequence[$seq]["points"], $sidx + 1, $seqrow["pass"]);
4913  $data->addQuestionTitle($questionsbysequence[$seq]["question_fi"], $questionsbysequence[$seq]["title"]);
4914  }
4915  }
4916  }
4917  }
4918  }
4919 
4920  if ($this->ects_output)
4921  {
4922  $passed_array =& $this->getTotalPointsPassedArray();
4923  }
4924  foreach (array_keys($data->getParticipants()) as $active_id)
4925  {
4926  $percentage = $data->getParticipant($active_id)->getReachedPointsInPercent();
4927  $mark = $this->mark_schema->getMatchingMark($percentage);
4928  if (is_object($mark))
4929  {
4930  $data->getParticipant($active_id)->setMark($mark->getShortName());
4931  $data->getParticipant($active_id)->setMarkOfficial($mark->getOfficialName());
4932  $data->getParticipant($active_id)->setPassed($mark->getPassed());
4933  }
4934  if ($this->ects_output)
4935  {
4936  $ects_mark = $this->getECTSGrade($passed_array, $data->getParticipant($active_id)->getReached(), $data->getParticipant($active_id)->getMaxPoints());
4937  $data->getParticipant($active_id)->setECTSMark($ects_mark);
4938  }
4939  $visitingTime =& $this->getVisitTimeOfParticipant($active_id);
4940  $data->getParticipant($active_id)->setFirstVisit($visitingTime["firstvisit"]);
4941  $data->getParticipant($active_id)->setLastVisit($visitingTime["lastvisit"]);
4942  }
4943  return $data;
4944  }
4945 
4947  {
4948  global $ilDB;
4949  $random = ilObjTest::_lookupRandomTestFromActiveId($active_id);
4950  if ($random)
4951  {
4952  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.pass, COUNT(tst_test_rnd_qst.question_fi) qcount, " .
4953  "SUM(qpl_questions.points) qsum FROM tst_test_rnd_qst, qpl_questions " .
4954  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id AND " .
4955  "tst_test_rnd_qst.active_fi = %s and pass = %s GROUP BY tst_test_rnd_qst.active_fi, " .
4956  "tst_test_rnd_qst.pass",
4957  array('integer', 'integer'),
4958  array($active_id, $pass)
4959  );
4960  }
4961  else
4962  {
4963  $result = $ilDB->queryF("SELECT COUNT(tst_test_question.question_fi) qcount, " .
4964  "SUM(qpl_questions.points) qsum FROM tst_test_question, qpl_questions, tst_active " .
4965  "WHERE tst_test_question.question_fi = qpl_questions.question_id AND tst_test_question.test_fi = tst_active.test_fi AND " .
4966  "tst_active.active_id = %s GROUP BY tst_test_question.test_fi",
4967  array('integer'),
4968  array($active_id)
4969  );
4970  }
4971  if ($result->numRows())
4972  {
4973  $row = $ilDB->fetchAssoc($result);
4974  return array("count" => $row["qcount"], "points" => $row["qsum"]);
4975  }
4976  else
4977  {
4978  return array("count" => 0, "points" => 0);
4979  }
4980  }
4981 
4982  function &getCompleteEvaluationData($withStatistics = TRUE, $filterby = "", $filtertext = "")
4983  {
4984  include_once "./Modules/Test/classes/class.ilTestEvaluationData.php";
4985  include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php";
4986  include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php";
4988  if ($withStatistics)
4989  {
4990  $data->calculateStatistics();
4991  }
4992  $data->setFilter($filterby, $filtertext);
4993  return $data;
4994  }
4995 
5003  {
5004  return $this->_evalResultsOverview($this->getTestId());
5005  }
5006 
5014  {
5015  global $ilDB;
5016 
5017  $result = $ilDB->queryF("SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " .
5018  "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
5019  "qpl_questions.points maxpoints " .
5020  "FROM tst_test_result, qpl_questions, tst_active " .
5021  "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " .
5022  "WHERE tst_active.active_id = tst_test_result.active_fi " .
5023  "AND qpl_questions.question_id = tst_test_result.question_fi " .
5024  "AND tst_active.test_fi = %s " .
5025  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
5026  array('integer'),
5027  array($test_id)
5028  );
5029  $overview = array();
5030  while ($row = $ilDB->fetchAssoc($result))
5031  {
5032  if (!array_key_exists($row["active_fi"], $overview))
5033  {
5034  $overview[$row["active_fi"]] = array();
5035  $overview[$row["active_fi"]]["firstname"] = $row["firstname"];
5036  $overview[$row["active_fi"]]["lastname"] = $row["lastname"];
5037  $overview[$row["active_fi"]]["title"] = $row["title"];
5038  $overview[$row["active_fi"]]["login"] = $row["login"];
5039  $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"];
5040  $overview[$row["active_fi"]]["started"] = $row["started"];
5041  $overview[$row["active_fi"]]["finished"] = $row["finished"];
5042  }
5043  if (!array_key_exists($row["pass"], $overview[$row["active_fi"]]))
5044  {
5045  $overview[$row["active_fi"]][$row["pass"]] = array();
5046  $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0;
5047  $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"];
5048  }
5049  array_push($overview[$row["active_fi"]][$row["pass"]], $row);
5050  $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"];
5051  }
5052  return $overview;
5053  }
5054 
5062  function &evalResultsOverviewOfParticipant($active_id)
5063  {
5064  global $ilDB;
5065 
5066  $result = $ilDB->queryF("SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " .
5067  "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
5068  "qpl_questions.points maxpoints " .
5069  "FROM tst_test_result, qpl_questions, tst_active " .
5070  "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " .
5071  "WHERE tst_active.active_id = tst_test_result.active_fi " .
5072  "AND qpl_questions.question_id = tst_test_result.question_fi " .
5073  "AND tst_active.test_fi = %s AND tst_active.active_id = %s" .
5074  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
5075  array('integer', 'integer'),
5076  array($this->getTestId(), $active_id)
5077  );
5078  $overview = array();
5079  while ($row = $ilDB->fetchAssoc($result))
5080  {
5081  if (!array_key_exists($row["active_fi"], $overview))
5082  {
5083  $overview[$row["active_fi"]] = array();
5084  $overview[$row["active_fi"]]["firstname"] = $row["firstname"];
5085  $overview[$row["active_fi"]]["lastname"] = $row["lastname"];
5086  $overview[$row["active_fi"]]["title"] = $row["title"];
5087  $overview[$row["active_fi"]]["login"] = $row["login"];
5088  $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"];
5089  $overview[$row["active_fi"]]["started"] = $row["started"];
5090  $overview[$row["active_fi"]]["finished"] = $row["finished"];
5091  }
5092  if (!array_key_exists($row["pass"], $overview[$row["active_fi"]]))
5093  {
5094  $overview[$row["active_fi"]][$row["pass"]] = array();
5095  $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0;
5096  $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"];
5097  }
5098  array_push($overview[$row["active_fi"]][$row["pass"]], $row);
5099  $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"];
5100  }
5101  return $overview;
5102  }
5103 
5115  function buildName($user_id, $firstname, $lastname, $title)
5116  {
5117  $name = "";
5118  if (strlen($firstname.$lastname.$title) == 0)
5119  {
5120  $name = $this->lng->txt("deleted_user");
5121  }
5122  else
5123  {
5124  if ($user_id == ANONYMOUS_USER_ID)
5125  {
5126  $name = $lastname;
5127  }
5128  else
5129  {
5130  $name = trim($lastname . ", " . $firstname . " " . $title);
5131  }
5132  if ($this->getAnonymity())
5133  {
5134  $name = $this->lng->txt("anonymous");
5135  }
5136  }
5137  return $name;
5138  }
5139 
5152  function _buildName($is_anonymous, $user_id, $firstname, $lastname, $title)
5153  {
5154  global $lng;
5155  $name = "";
5156  if (strlen($firstname.$lastname.$title) == 0)
5157  {
5158  $name = $lng->txt("deleted_user");
5159  }
5160  else
5161  {
5162  if ($user_id == ANONYMOUS_USER_ID)
5163  {
5164  $name = $lastname;
5165  }
5166  else
5167  {
5168  $name = trim($lastname . ", " . $firstname . " " . $title);
5169  }
5170  if ($is_anonymous)
5171  {
5172  $name = $lng->txt("anonymous");
5173  }
5174  }
5175  return $name;
5176  }
5177 
5185  {
5186  global $ilDB;
5187 
5188  $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",
5189  array('integer'),
5190  array($this->getTestId())
5191  );
5192  $times = array();
5193  while ($row = $ilDB->fetchObject($result))
5194  {
5195  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
5196  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5197  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
5198  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5199  $times[$row->active_fi] += ($epoch_2 - $epoch_1);
5200  }
5201  $max_time = 0;
5202  $counter = 0;
5203  foreach ($times as $key => $value)
5204  {
5205  $max_time += $value;
5206  $counter++;
5207  }
5208  if ($counter)
5209  {
5210  $average_time = round($max_time / $counter);
5211  }
5212  else
5213  {
5214  $average_time = 0;
5215  }
5216  return $average_time;
5217  }
5218 
5225  function &getAvailableQuestionpools($use_object_id = false, $equal_points = false, $could_be_offline = false, $show_path = FALSE, $with_questioncount = FALSE, $permission = "read")
5226  {
5227  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5228  return ilObjQuestionPool::_getAvailableQuestionpools($use_object_id, $equal_points, $could_be_offline, $show_path, $with_questioncount, $permission);
5229  }
5230 
5238  {
5239  $time_in_seconds = 0;
5240  foreach ($this->questions as $question_id)
5241  {
5242  $question =& ilObjTest::_instanciateQuestion($question_id);
5243  $est_time = $question->getEstimatedWorkingTime();
5244  $time_in_seconds += $est_time["h"] * 3600 + $est_time["m"] * 60 + $est_time["s"];
5245  }
5246  $hours = (int)($time_in_seconds / 3600) ;
5247  $time_in_seconds = $time_in_seconds - ($hours * 3600);
5248  $minutes = (int)($time_in_seconds / 60);
5249  $time_in_seconds = $time_in_seconds - ($minutes * 60);
5250  $result = array("hh" => $hours, "mm" => $minutes, "ss" => $time_in_seconds);
5251  return $result;
5252  }
5253 
5262  public function generateRandomPass($nr, $qpls, $pass = NULL)
5263  {
5264  global $ilDB;
5265  $qplids = array();
5266  foreach ($qpls as $arr) array_push($qplids, $arr['qpl']);
5267  $result = $ilDB->queryF('SELECT * FROM tst_rnd_cpy WHERE tst_fi = %s AND ' . $ilDB->in('qpl_fi', $qplids, false, 'integer'),
5268  array('integer'),
5269  array($this->getTestId())
5270  );
5271  if ($result->numRows())
5272  {
5273  $ids = array();
5274  while ($row = $ilDB->fetchAssoc($result)) array_push($ids, $row['qst_fi']);
5275  $nr = ($nr > count($ids)) ? count($ids) : $nr;
5276  if ($nr == 0) return array();
5277  $rand_keys = array_rand($ids, $nr);
5278  $selection = array();
5279  if (is_array($rand_keys))
5280  {
5281  foreach ($rand_keys as $key)
5282  {
5283  $selection[$ids[$key]] = $ids[$key];
5284  }
5285  }
5286  else
5287  {
5288  $selection[$ids[$rand_keys]] = $ids[$rand_keys];
5289  }
5290  return $selection;
5291  }
5292  else
5293  {
5294  // old style random questions
5295  return $this->randomSelectQuestions($nr, 0, 1, $qplids, $pass);
5296  }
5297  }
5298 
5309  function randomSelectQuestions($nr_of_questions, $questionpool, $use_obj_id = 0, $qpls = "", $pass = NULL)
5310  {
5311  global $rbacsystem;
5312  global $ilDB;
5313 
5314  // retrieve object id instead of ref id if necessary
5315  if (($questionpool != 0) && (!$use_obj_id)) $questionpool = ilObject::_lookupObjId($questionpool);
5316 
5317  // get original ids of all existing questions in the test
5318  $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",
5319  array("integer"),
5320  array($this->getTestId())
5321  );
5322  $original_ids = array();
5323  $paramtypes = array();
5324  $paramvalues = array();
5325  while ($row = $ilDB->fetchAssoc($result))
5326  {
5327  array_push($original_ids, $row['original_id']);
5328  }
5329 
5330  $available = "";
5331  // get a list of all available questionpools
5332  if (($questionpool == 0) && (!is_array($qpls)))
5333  {
5334  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5335  $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())));
5336  if (count($available_pools))
5337  {
5338  $available = " AND " . $ilDB->in('obj_fi', $available_pools, false, 'integer');
5339  }
5340  else
5341  {
5342  return array();
5343  }
5344  }
5345 
5346  $constraint_qpls = "";
5347  $result_array = array();
5348  if ($questionpool == 0)
5349  {
5350  if (is_array($qpls))
5351  {
5352  if (count($qpls) > 0)
5353  {
5354  $constraint_qpls = " AND " . $ilDB->in('obj_fi', $qpls, false, 'integer');
5355  }
5356  }
5357  }
5358 
5359  $original_clause = "";
5360  if (count($original_ids))
5361  {
5362  $original_clause = " AND " . $ilDB->in('question_id', $original_ids, true, 'integer');
5363  }
5364 
5365  if ($questionpool == 0)
5366  {
5367  $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",
5368  array('integer', 'text'),
5369  array(0, "1")
5370  );
5371  }
5372  else
5373  {
5374  $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",
5375  array('integer','integer', 'text'),
5376  array($questionpool, 0, "1")
5377  );
5378  }
5379  $found_ids = array();
5380  while ($row = $ilDB->fetchAssoc($result))
5381  {
5382  array_push($found_ids, $row['question_id']);
5383  }
5384  $nr_of_questions = ($nr_of_questions > count($found_ids)) ? count($found_ids) : $nr_of_questions;
5385  if ($nr_of_questions == 0) return array();
5386  $rand_keys = array_rand($found_ids, $nr_of_questions);
5387  $result = array();
5388  if (is_array($rand_keys))
5389  {
5390  foreach ($rand_keys as $key)
5391  {
5392  $result[$found_ids[$key]] = $found_ids[$key];
5393  }
5394  }
5395  else
5396  {
5397  $result[$found_ids[$rand_keys]] = $found_ids[$rand_keys];
5398  }
5399  return $result;
5400  }
5401 
5408  function getImagePath()
5409  {
5410  return CLIENT_WEB_DIR . "/assessment/" . $this->getId() . "/images/";
5411  }
5412 
5419  function getImagePathWeb()
5420  {
5421  include_once "./Services/Utilities/classes/class.ilUtil.php";
5422  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/" . $this->getId() . "/images/";
5424  }
5425 
5434  function &createQuestionGUI($question_type, $question_id = -1)
5435  {
5436  if ((!$question_type) and ($question_id > 0))
5437  {
5438  $question_type = $this->getQuestionType($question_id);
5439  }
5440  if (!strlen($question_type)) return null;
5441  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
5442  assQuestion::_includeClass($question_type, 1);
5443  $question_type_gui = $question_type . "GUI";
5444  $question = new $question_type_gui();
5445  if ($question_id > 0)
5446  {
5447  $question->object->loadFromDb($question_id);
5448  }
5449  return $question;
5450  }
5451 
5459  function &_instanciateQuestion($question_id)
5460  {
5461  if (strcmp($question_id, "") != 0)
5462  {
5463  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
5464  return assQuestion::_instanciateQuestion($question_id);
5465  }
5466  }
5467 
5476  function moveQuestions($move_questions, $target_index, $insert_mode)
5477  {
5478  $this->questions = array_values($this->questions);
5479  $array_pos = array_search($target_index, $this->questions);
5480  if ($insert_mode == 0)
5481  {
5482  $part1 = array_slice($this->questions, 0, $array_pos);
5483  $part2 = array_slice($this->questions, $array_pos);
5484  }
5485  else if ($insert_mode == 1)
5486  {
5487  $part1 = array_slice($this->questions, 0, $array_pos + 1);
5488  $part2 = array_slice($this->questions, $array_pos + 1);
5489  }
5490  foreach ($move_questions as $question_id)
5491  {
5492  if (!(array_search($question_id, $part1) === FALSE))
5493  {
5494  unset($part1[array_search($question_id, $part1)]);
5495  }
5496  if (!(array_search($question_id, $part2) === FALSE))
5497  {
5498  unset($part2[array_search($question_id, $part2)]);
5499  }
5500  }
5501  $part1 = array_values($part1);
5502  $part2 = array_values($part2);
5503  $new_array = array_values(array_merge($part1, $move_questions, $part2));
5504  $this->questions = array();
5505  $counter = 1;
5506  foreach ($new_array as $question_id)
5507  {
5508  $this->questions[$counter] = $question_id;
5509  $counter++;
5510  }
5511  $this->saveQuestionsToDb();
5512  }
5513 
5514 
5523  {
5524  if ($this->getStartingTime())
5525  {
5526  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getStartingTime(), $matches))
5527  {
5528  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5529  $now = mktime();
5530  if ($now < $epoch_time)
5531  {
5532  // starting time not reached
5533  return false;
5534  }
5535  }
5536  }
5537  return true;
5538  }
5539 
5548  {
5549  if ($this->getEndingTime())
5550  {
5551  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndingTime(), $matches))
5552  {
5553  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5554  $now = mktime();
5555  if ($now > $epoch_time)
5556  {
5557  // ending time reached
5558  return true;
5559  }
5560  }
5561  }
5562  return false;
5563  }
5564 
5570  function getAvailableQuestions($arrFilter, $completeonly = 0)
5571  {
5572  global $ilUser;
5573  global $ilDB;
5574 
5575  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5576  $available_pools = array_keys(ilObjQuestionPool::_getAvailableQuestionpools($use_object_id = TRUE, $equal_points = FALSE, $could_be_offline = FALSE, $showPath = FALSE, $with_questioncount = FALSE));
5577  $available = "";
5578  if (count($available_pools))
5579  {
5580  $available = " AND " . $ilDB->in('qpl_questions.obj_fi', $available_pools, false, 'integer');
5581  }
5582  else
5583  {
5584  return array();
5585  }
5586  if ($completeonly)
5587  {
5588  $available .= " AND qpl_questions.complete = " . $ilDB->quote("1", 'text');
5589  }
5590 
5591  $where = "";
5592  if (is_array($arrFilter))
5593  {
5594  if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title']))
5595  {
5596  $where .= " AND " . $ilDB->like('qpl_questions.title', 'text', "%%" . $arrFilter['title'] . "%%");
5597  }
5598  if (array_key_exists('description', $arrFilter) && strlen($arrFilter['description']))
5599  {
5600  $where .= " AND " . $ilDB->like('qpl_questions.description', 'text', "%%" . $arrFilter['description'] . "%%");
5601  }
5602  if (array_key_exists('author', $arrFilter) && strlen($arrFilter['author']))
5603  {
5604  $where .= " AND " . $ilDB->like('qpl_questions.author', 'text', "%%" . $arrFilter['author'] . "%%");
5605  }
5606  if (array_key_exists('type', $arrFilter) && strlen($arrFilter['type']))
5607  {
5608  $where .= " AND qpl_qst_type.type_tag = " . $ilDB->quote($arrFilter['type'], 'text');
5609  }
5610  if (array_key_exists('qpl', $arrFilter) && strlen($arrFilter['qpl']))
5611  {
5612  $where .= " AND " . $ilDB->like('object_data.title', 'text', "%%" . $arrFilter['qpl'] . "%%");
5613  }
5614  }
5615 
5616  $original_ids =& $this->getExistingQuestions();
5617  $original_clause = " qpl_questions.original_id IS NULL";
5618  if (count($original_ids))
5619  {
5620  $original_clause = " qpl_questions.original_id IS NULL AND " . $ilDB->in('qpl_questions.question_id', $original_ids, true, 'integer');
5621  }
5622 
5623  $query_result = $ilDB->query(
5624  "SELECT qpl_questions.*, qpl_questions.tstamp, qpl_qst_type.type_tag, qpl_qst_type.plugin, object_data.title qpl " .
5625  "FROM qpl_questions, qpl_qst_type, object_data WHERE $original_clause $available AND " .
5626  "object_data.obj_id = qpl_questions.obj_fi AND qpl_questions.tstamp > 0 AND " .
5627  "qpl_questions.question_type_fi = qpl_qst_type.question_type_id$where");
5628  $rows = array();
5629  $types = $this->getQuestionTypeTranslations();
5630  if ($query_result->numRows())
5631  {
5632  while ($row = $ilDB->fetchAssoc($query_result))
5633  {
5634  $row['ttype'] = $types[$row['type_tag']];
5635  if ($row["plugin"])
5636  {
5637  if ($this->isPluginActive($row["type_tag"]))
5638  {
5639  array_push($rows, $row);
5640  }
5641  }
5642  else
5643  {
5644  array_push($rows, $row);
5645  }
5646  }
5647  }
5648  return $rows;
5649  }
5650 
5651  public function &getQuestionTypeTranslations()
5652  {
5653  global $ilDB;
5654  global $lng;
5655  global $ilLog;
5656  global $ilPluginAdmin;
5657 
5658  $lng->loadLanguageModule("assessment");
5659  $result = $ilDB->query("SELECT * FROM qpl_qst_type");
5660  $types = array();
5661  while ($row = $ilDB->fetchAssoc($result))
5662  {
5663  if ($row["plugin"] == 0)
5664  {
5665  $types[$row['type_tag']] = $lng->txt($row["type_tag"]);
5666  }
5667  else
5668  {
5669  $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
5670  foreach ($pl_names as $pl_name)
5671  {
5672  $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
5673  if (strcmp($pl->getQuestionType(), $row["type_tag"]) == 0)
5674  {
5675  $types[$row['type_tag']] = $pl->getQuestionTypeTranslation();
5676  }
5677  }
5678  }
5679  }
5680  ksort($types);
5681  return $types;
5682  }
5683 
5690  function fromXML(&$assessment)
5691  {
5692  unset($_SESSION["import_mob_xhtml"]);
5693 
5694  $this->setDescription($assessment->getComment());
5695  $this->setTitle($assessment->getTitle());
5696 
5697  foreach ($assessment->objectives as $objectives)
5698  {
5699  foreach ($objectives->materials as $material)
5700  {
5701  $this->setIntroduction($this->QTIMaterialToString($material));
5702  }
5703  }
5704  if ($assessment->getPresentationMaterial())
5705  {
5706  $this->setFinalStatement($this->QTIMaterialToString($assessment->getPresentationMaterial()->getMaterial(0)));
5707  }
5708 
5709  foreach ($assessment->assessmentcontrol as $assessmentcontrol)
5710  {
5711  switch ($assessmentcontrol->getSolutionswitch())
5712  {
5713  case "Yes":
5714  $this->setInstantFeedbackSolution(1);
5715  break;
5716  default:
5717  $this->setInstantFeedbackSolution(0);
5718  break;
5719  }
5720  }
5721 
5722  foreach ($assessment->qtimetadata as $metadata)
5723  {
5724  switch ($metadata["label"])
5725  {
5726  case "test_type":
5727  // for old tests with a test type
5728  $type = $metadata["entry"];
5729  switch ($type)
5730  {
5731  case 1:
5732  // assessment
5733  $this->setAnonymity(1);
5734  break;
5735  case 2:
5736  // self assessment
5737  break;
5738  case 4:
5739  // online exam
5740  $this->setFixedParticipants(1);
5741  $this->setListOfQuestionsSettings(7);
5742  $this->setShowSolutionPrintview(1);
5743  break;
5744  case 5:
5745  // varying random test
5746  break;
5747  }
5748  break;
5749  case "sequence_settings":
5750  $this->setSequenceSettings($metadata["entry"]);
5751  break;
5752  case "author":
5753  $this->setAuthor($metadata["entry"]);
5754  break;
5755  case "nr_of_tries":
5756  $this->setNrOfTries($metadata["entry"]);
5757  break;
5758  case "kiosk":
5759  $this->setKiosk($metadata["entry"]);
5760  break;
5761  case "showfinalstatement":
5762  $this->setShowFinalStatement($metadata["entry"]);
5763  break;
5764  case "showinfo":
5765  $this->setShowInfo($metadata["entry"]);
5766  break;
5767  case "forcejs":
5768  $this->setForceJS($metadata["entry"]);
5769  break;
5770  case "customstyle":
5771  $this->setCustomStyle($metadata["entry"]);
5772  break;
5773  case "hide_previous_results":
5774  if ($metadata["entry"] == 0)
5775  {
5776  $this->setUsePreviousAnswers(1);
5777  }
5778  else
5779  {
5780  $this->setUsePreviousAnswers(0);
5781  }
5782  break;
5783  case "use_previous_answers":
5784  $this->setUsePreviousAnswers($metadata["entry"]);
5785  break;
5786  case "answer_feedback":
5787  $this->setAnswerFeedback($metadata["entry"]);
5788  break;
5789  case "hide_title_points":
5790  $this->setTitleOutput($metadata["entry"]);
5791  break;
5792  case "title_output":
5793  $this->setTitleOutput($metadata["entry"]);
5794  break;
5795  case "random_test":
5796  $this->setRandomTest($metadata["entry"]);
5797  break;
5798  case "random_question_count":
5799  $this->setRandomQuestionCount($metadata["entry"]);
5800  break;
5801  case "results_presentation":
5802  $this->setResultsPresentation($metadata["entry"]);
5803  break;
5804  case "reset_processing_time":
5805  $this->setResetProcessingTime($metadata["entry"]);
5806  break;
5807  case "instant_verification":
5808  $this->setInstantFeedbackSolution($metadata["entry"]);
5809  break;
5810  case "answer_feedback_points":
5811  $this->setAnswerFeedbackPoints($metadata["entry"]);
5812  break;
5813  case "anonymity":
5814  $this->setAnonymity($metadata["entry"]);
5815  break;
5816  case "show_cancel":
5817  $this->setShowCancel($metadata["entry"]);
5818  break;
5819  case "show_marker":
5820  $this->setShowMarker($metadata["entry"]);
5821  break;
5822  case "fixed_participants":
5823  $this->setFixedParticipants($metadata["entry"]);
5824  break;
5825  case "score_reporting":
5826  $this->setScoreReporting($metadata["entry"]);
5827  break;
5828  case "shuffle_questions":
5829  $this->setShuffleQuestions($metadata["entry"]);
5830  break;
5831  case "count_system":
5832  $this->setCountSystem($metadata["entry"]);
5833  break;
5834  case "mc_scoring":
5835  $this->setMCScoring($metadata["entry"]);
5836  break;
5837  case "mailnotification":
5838  $this->setMailNotification($metadata["entry"]);
5839  break;
5840  case "mailnottype":
5841  $this->setMailNotificationType($metadata["entry"]);
5842  break;
5843  case "exportsettings":
5844  $this->setExportSettings($metadata['exportsettings']);
5845  break;
5846  case "score_cutting":
5847  $this->setScoreCutting($metadata["entry"]);
5848  break;
5849  case "password":
5850  $this->setPassword($metadata["entry"]);
5851  break;
5852  case "allowedUsers":
5853  $this->setAllowedUsers($metadata["entry"]);
5854  break;
5855  case "allowedUsersTimeGap":
5856  $this->setAllowedUsersTimeGap($metadata["entry"]);
5857  break;
5858  case "pass_scoring":
5859  $this->setPassScoring($metadata["entry"]);
5860  break;
5861  case "show_summary":
5862  $this->setListOfQuestionsSettings($metadata["entry"]);
5863  break;
5864  case "reporting_date":
5865  $iso8601period = $metadata["entry"];
5866  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
5867  {
5868  $this->setReportingDate(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
5869  }
5870  break;
5871  case "processing_time":
5872  $this->setProcessingTime($metadata['entry']);
5873  break;
5874  case "starting_time":
5875  $iso8601period = $metadata["entry"];
5876  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
5877  {
5878  $this->setStartingTime(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
5879  }
5880  break;
5881  case "ending_time":
5882  $iso8601period = $metadata["entry"];
5883  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
5884  {
5885  $this->setEndingTime(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
5886  }
5887  break;
5888  }
5889  if (preg_match("/mark_step_\d+/", $metadata["label"]))
5890  {
5891  $xmlmark = $metadata["entry"];
5892  preg_match("/<short>(.*?)<\/short>/", $xmlmark, $matches);
5893  $mark_short = $matches[1];
5894  preg_match("/<official>(.*?)<\/official>/", $xmlmark, $matches);
5895  $mark_official = $matches[1];
5896  preg_match("/<percentage>(.*?)<\/percentage>/", $xmlmark, $matches);
5897  $mark_percentage = $matches[1];
5898  preg_match("/<passed>(.*?)<\/passed>/", $xmlmark, $matches);
5899  $mark_passed = $matches[1];
5900  $this->mark_schema->addMarkStep($mark_short, $mark_official, $mark_percentage, $mark_passed);
5901  }
5902  }
5903  // handle the import of media objects in XHTML code
5904  if (is_array($_SESSION["import_mob_xhtml"]))
5905  {
5906  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
5907  include_once "./Services/RTE/classes/class.ilRTE.php";
5908  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5909  foreach ($_SESSION["import_mob_xhtml"] as $mob)
5910  {
5911  $importfile = ilObjTest::_getImportDirectory() . '/' . $mob["uri"];
5912  if (file_exists($importfile))
5913  {
5914  $media_object =& ilObjMediaObject::_saveTempFileAsMediaObject(basename($importfile), $importfile, FALSE);
5915  ilObjMediaObject::_saveUsage($media_object->getId(), "tst:html", $this->getId());
5916  $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getIntroduction()), 1));
5917  $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getFinalStatement()), 1));
5918  }
5919  else
5920  {
5921  global $ilLog;
5922  $ilLog->write("Error: Could not open XHTML mob file for test introduction during test import. File $importfile does not exist!");
5923  }
5924  }
5925  $this->saveToDb();
5926  }
5927  }
5928 
5935  function toXML()
5936  {
5937  include_once("./Services/Xml/classes/class.ilXmlWriter.php");
5938  $a_xml_writer = new ilXmlWriter;
5939  // set xml header
5940  $a_xml_writer->xmlHeader();
5941  $a_xml_writer->xmlSetDtdDef("<!DOCTYPE questestinterop SYSTEM \"ims_qtiasiv1p2p1.dtd\">");
5942  $a_xml_writer->xmlStartTag("questestinterop");
5943 
5944  $attrs = array(
5945  "ident" => "il_".IL_INST_ID."_tst_".$this->getTestId(),
5946  "title" => $this->getTitle()
5947  );
5948  $a_xml_writer->xmlStartTag("assessment", $attrs);
5949  // add qti comment
5950  $a_xml_writer->xmlElement("qticomment", NULL, $this->getDescription());
5951 
5952  // add qti duration
5953  if ($this->enable_processing_time)
5954  {
5955  preg_match("/(\d+):(\d+):(\d+)/", $this->processing_time, $matches);
5956  $a_xml_writer->xmlElement("duration", NULL, sprintf("P0Y0M0DT%dH%dM%dS", $matches[1], $matches[2], $matches[3]));
5957  }
5958 
5959  // add the rest of the preferences in qtimetadata tags, because there is no correspondent definition in QTI
5960  $a_xml_writer->xmlStartTag("qtimetadata");
5961  $a_xml_writer->xmlStartTag("qtimetadatafield");
5962  $a_xml_writer->xmlElement("fieldlabel", NULL, "ILIAS_VERSION");
5963  $a_xml_writer->xmlElement("fieldentry", NULL, $this->ilias->getSetting("ilias_version"));
5964  $a_xml_writer->xmlEndTag("qtimetadatafield");
5965 
5966  // anonymity
5967  $a_xml_writer->xmlStartTag("qtimetadatafield");
5968  $a_xml_writer->xmlElement("fieldlabel", NULL, "anonymity");
5969  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnonymity()));
5970  $a_xml_writer->xmlEndTag("qtimetadatafield");
5971 
5972  // random test
5973  $a_xml_writer->xmlStartTag("qtimetadatafield");
5974  $a_xml_writer->xmlElement("fieldlabel", NULL, "random_test");
5975  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->isRandomTest()));
5976  $a_xml_writer->xmlEndTag("qtimetadatafield");
5977 
5978  // sequence settings
5979  $a_xml_writer->xmlStartTag("qtimetadatafield");
5980  $a_xml_writer->xmlElement("fieldlabel", NULL, "sequence_settings");
5981  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getSequenceSettings());
5982  $a_xml_writer->xmlEndTag("qtimetadatafield");
5983 
5984  // author
5985  $a_xml_writer->xmlStartTag("qtimetadatafield");
5986  $a_xml_writer->xmlElement("fieldlabel", NULL, "author");
5987  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAuthor());
5988  $a_xml_writer->xmlEndTag("qtimetadatafield");
5989 
5990  // reset processing time
5991  $a_xml_writer->xmlStartTag("qtimetadatafield");
5992  $a_xml_writer->xmlElement("fieldlabel", NULL, "reset_processing_time");
5993  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getResetProcessingTime());
5994  $a_xml_writer->xmlEndTag("qtimetadatafield");
5995 
5996  // count system
5997  $a_xml_writer->xmlStartTag("qtimetadatafield");
5998  $a_xml_writer->xmlElement("fieldlabel", NULL, "count_system");
5999  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getCountSystem());
6000  $a_xml_writer->xmlEndTag("qtimetadatafield");
6001 
6002  // multiple choice scoring
6003  $a_xml_writer->xmlStartTag("qtimetadatafield");
6004  $a_xml_writer->xmlElement("fieldlabel", NULL, "mc_scoring");
6005  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMCScoring());
6006  $a_xml_writer->xmlEndTag("qtimetadatafield");
6007 
6008  // multiple choice scoring
6009  $a_xml_writer->xmlStartTag("qtimetadatafield");
6010  $a_xml_writer->xmlElement("fieldlabel", NULL, "score_cutting");
6011  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getScoreCutting());
6012  $a_xml_writer->xmlEndTag("qtimetadatafield");
6013 
6014  // multiple choice scoring
6015  $a_xml_writer->xmlStartTag("qtimetadatafield");
6016  $a_xml_writer->xmlElement("fieldlabel", NULL, "password");
6017  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getPassword());
6018  $a_xml_writer->xmlEndTag("qtimetadatafield");
6019 
6020  // allowed users
6021  $a_xml_writer->xmlStartTag("qtimetadatafield");
6022  $a_xml_writer->xmlElement("fieldlabel", NULL, "allowedUsers");
6023  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAllowedUsers());
6024  $a_xml_writer->xmlEndTag("qtimetadatafield");
6025 
6026  // allowed users time gap
6027  $a_xml_writer->xmlStartTag("qtimetadatafield");
6028  $a_xml_writer->xmlElement("fieldlabel", NULL, "allowedUsersTimeGap");
6029  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAllowedUsersTimeGap());
6030  $a_xml_writer->xmlEndTag("qtimetadatafield");
6031 
6032  // pass scoring
6033  $a_xml_writer->xmlStartTag("qtimetadatafield");
6034  $a_xml_writer->xmlElement("fieldlabel", NULL, "pass_scoring");
6035  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getPassScoring());
6036  $a_xml_writer->xmlEndTag("qtimetadatafield");
6037 
6038  // score reporting date
6039  if ($this->getReportingDate())
6040  {
6041  $a_xml_writer->xmlStartTag("qtimetadatafield");
6042  $a_xml_writer->xmlElement("fieldlabel", NULL, "reporting_date");
6043  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->reporting_date, $matches);
6044  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("P%dY%dM%dDT%dH%dM%dS", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
6045  $a_xml_writer->xmlEndTag("qtimetadatafield");
6046  }
6047  // number of tries
6048  $a_xml_writer->xmlStartTag("qtimetadatafield");
6049  $a_xml_writer->xmlElement("fieldlabel", NULL, "nr_of_tries");
6050  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getNrOfTries()));
6051  $a_xml_writer->xmlEndTag("qtimetadatafield");
6052 
6053  // kiosk
6054  $a_xml_writer->xmlStartTag("qtimetadatafield");
6055  $a_xml_writer->xmlElement("fieldlabel", NULL, "kiosk");
6056  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getKiosk()));
6057  $a_xml_writer->xmlEndTag("qtimetadatafield");
6058 
6059  // use previous answers
6060  $a_xml_writer->xmlStartTag("qtimetadatafield");
6061  $a_xml_writer->xmlElement("fieldlabel", NULL, "use_previous_answers");
6062  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getUsePreviousAnswers());
6063  $a_xml_writer->xmlEndTag("qtimetadatafield");
6064 
6065  // hide title points
6066  $a_xml_writer->xmlStartTag("qtimetadatafield");
6067  $a_xml_writer->xmlElement("fieldlabel", NULL, "title_output");
6068  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getTitleOutput()));
6069  $a_xml_writer->xmlEndTag("qtimetadatafield");
6070 
6071  // random question count
6072  $a_xml_writer->xmlStartTag("qtimetadatafield");
6073  $a_xml_writer->xmlElement("fieldlabel", NULL, "random_question_count");
6074  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getRandomQuestionCount()));
6075  $a_xml_writer->xmlEndTag("qtimetadatafield");
6076 
6077  // results presentation
6078  $a_xml_writer->xmlStartTag("qtimetadatafield");
6079  $a_xml_writer->xmlElement("fieldlabel", NULL, "results_presentation");
6080  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getResultsPresentation()));
6081  $a_xml_writer->xmlEndTag("qtimetadatafield");
6082 
6083  // solution details
6084  $a_xml_writer->xmlStartTag("qtimetadatafield");
6085  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_summary");
6086  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getListOfQuestionsSettings()));
6087  $a_xml_writer->xmlEndTag("qtimetadatafield");
6088 
6089  // solution details
6090  $a_xml_writer->xmlStartTag("qtimetadatafield");
6091  $a_xml_writer->xmlElement("fieldlabel", NULL, "score_reporting");
6092  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getScoreReporting()));
6093  $a_xml_writer->xmlEndTag("qtimetadatafield");
6094 
6095  // solution details
6096  $a_xml_writer->xmlStartTag("qtimetadatafield");
6097  $a_xml_writer->xmlElement("fieldlabel", NULL, "instant_verification");
6098  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getInstantFeedbackSolution()));
6099  $a_xml_writer->xmlEndTag("qtimetadatafield");
6100 
6101  // answer specific feedback
6102  $a_xml_writer->xmlStartTag("qtimetadatafield");
6103  $a_xml_writer->xmlElement("fieldlabel", NULL, "answer_feedback");
6104  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnswerFeedback()));
6105  $a_xml_writer->xmlEndTag("qtimetadatafield");
6106 
6107  // answer specific feedback of reached points
6108  $a_xml_writer->xmlStartTag("qtimetadatafield");
6109  $a_xml_writer->xmlElement("fieldlabel", NULL, "answer_feedback_points");
6110  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnswerFeedbackPoints()));
6111  $a_xml_writer->xmlEndTag("qtimetadatafield");
6112 
6113  // show cancel
6114  $a_xml_writer->xmlStartTag("qtimetadatafield");
6115  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_cancel");
6116  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShowCancel()));
6117  $a_xml_writer->xmlEndTag("qtimetadatafield");
6118 
6119  // show marker
6120  $a_xml_writer->xmlStartTag("qtimetadatafield");
6121  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_marker");
6122  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShowMarker()));
6123  $a_xml_writer->xmlEndTag("qtimetadatafield");
6124 
6125  // fixed participants
6126  $a_xml_writer->xmlStartTag("qtimetadatafield");
6127  $a_xml_writer->xmlElement("fieldlabel", NULL, "fixed_participants");
6128  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getFixedParticipants()));
6129  $a_xml_writer->xmlEndTag("qtimetadatafield");
6130 
6131  // show final statement
6132  $a_xml_writer->xmlStartTag("qtimetadatafield");
6133  $a_xml_writer->xmlElement("fieldlabel", NULL, "showfinalstatement");
6134  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getShowFinalStatement()) ? "1" : "0")));
6135  $a_xml_writer->xmlEndTag("qtimetadatafield");
6136 
6137  // show introduction only
6138  $a_xml_writer->xmlStartTag("qtimetadatafield");
6139  $a_xml_writer->xmlElement("fieldlabel", NULL, "showinfo");
6140  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getShowInfo()) ? "1" : "0")));
6141  $a_xml_writer->xmlEndTag("qtimetadatafield");
6142 
6143  // mail notification
6144  $a_xml_writer->xmlStartTag("qtimetadatafield");
6145  $a_xml_writer->xmlElement("fieldlabel", NULL, "mailnotification");
6146  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMailNotification());
6147  $a_xml_writer->xmlEndTag("qtimetadatafield");
6148 
6149  // mail notification type
6150  $a_xml_writer->xmlStartTag("qtimetadatafield");
6151  $a_xml_writer->xmlElement("fieldlabel", NULL, "mailnottype");
6152  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMailNotificationType());
6153  $a_xml_writer->xmlEndTag("qtimetadatafield");
6154 
6155  // export settings
6156  $a_xml_writer->xmlStartTag("qtimetadatafield");
6157  $a_xml_writer->xmlElement("fieldlabel", NULL, "exportsettings");
6158  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getExportSettings());
6159  $a_xml_writer->xmlEndTag("qtimetadatafield");
6160 
6161  // force JavaScript
6162  $a_xml_writer->xmlStartTag("qtimetadatafield");
6163  $a_xml_writer->xmlElement("fieldlabel", NULL, "forcejs");
6164  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getForceJS()) ? "1" : "0")));
6165  $a_xml_writer->xmlEndTag("qtimetadatafield");
6166 
6167  // custom style
6168  $a_xml_writer->xmlStartTag("qtimetadatafield");
6169  $a_xml_writer->xmlElement("fieldlabel", NULL, "customstyle");
6170  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getCustomStyle());
6171  $a_xml_writer->xmlEndTag("qtimetadatafield");
6172 
6173  // shuffle questions
6174  $a_xml_writer->xmlStartTag("qtimetadatafield");
6175  $a_xml_writer->xmlElement("fieldlabel", NULL, "shuffle_questions");
6176  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShuffleQuestions()));
6177  $a_xml_writer->xmlEndTag("qtimetadatafield");
6178 
6179  // processing time
6180  $a_xml_writer->xmlStartTag("qtimetadatafield");
6181  $a_xml_writer->xmlElement("fieldlabel", NULL, "processing_time");
6182  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getProcessingTime());
6183  $a_xml_writer->xmlEndTag("qtimetadatafield");
6184 
6185  // starting time
6186  if ($this->getStartingTime())
6187  {
6188  $a_xml_writer->xmlStartTag("qtimetadatafield");
6189  $a_xml_writer->xmlElement("fieldlabel", NULL, "starting_time");
6190  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->starting_time, $matches);
6191  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("P%dY%dM%dDT%dH%dM%dS", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
6192  $a_xml_writer->xmlEndTag("qtimetadatafield");
6193  }
6194  // ending time
6195  if ($this->getEndingTime())
6196  {
6197  $a_xml_writer->xmlStartTag("qtimetadatafield");
6198  $a_xml_writer->xmlElement("fieldlabel", NULL, "ending_time");
6199  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->ending_time, $matches);
6200  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("P%dY%dM%dDT%dH%dM%dS", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
6201  $a_xml_writer->xmlEndTag("qtimetadatafield");
6202  }
6203  foreach ($this->mark_schema->mark_steps as $index => $mark)
6204  {
6205  // mark steps
6206  $a_xml_writer->xmlStartTag("qtimetadatafield");
6207  $a_xml_writer->xmlElement("fieldlabel", NULL, "mark_step_$index");
6208  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("<short>%s</short><official>%s</official><percentage>%.2f</percentage><passed>%d</passed>", $mark->getShortName(), $mark->getOfficialName(), $mark->getMinimumLevel(), $mark->getPassed()));
6209  $a_xml_writer->xmlEndTag("qtimetadatafield");
6210  }
6211  $a_xml_writer->xmlEndTag("qtimetadata");
6212 
6213  // add qti objectives
6214  $a_xml_writer->xmlStartTag("objectives");
6215  $this->addQTIMaterial($a_xml_writer, $this->getIntroduction());
6216  $a_xml_writer->xmlEndTag("objectives");
6217 
6218  // add qti assessmentcontrol
6219  if ($this->getInstantFeedbackSolution() == 1)
6220  {
6221  $attrs = array(
6222  "solutionswitch" => "Yes"
6223  );
6224  }
6225  else
6226  {
6227  $attrs = NULL;
6228  }
6229  $a_xml_writer->xmlElement("assessmentcontrol", $attrs, NULL);
6230 
6231  if (strlen($this->getFinalStatement()))
6232  {
6233  // add qti presentation_material
6234  $a_xml_writer->xmlStartTag("presentation_material");
6235  $a_xml_writer->xmlStartTag("flow_mat");
6236  $this->addQTIMaterial($a_xml_writer, $this->getFinalStatement());
6237  $a_xml_writer->xmlEndTag("flow_mat");
6238  $a_xml_writer->xmlEndTag("presentation_material");
6239  }
6240 
6241  $attrs = array(
6242  "ident" => "1"
6243  );
6244  $a_xml_writer->xmlElement("section", $attrs, NULL);
6245  $a_xml_writer->xmlEndTag("assessment");
6246  $a_xml_writer->xmlEndTag("questestinterop");
6247 
6248  $xml = $a_xml_writer->xmlDumpMem(FALSE);
6249 
6250  foreach ($this->questions as $question_id)
6251  {
6252  $question =& ilObjTest::_instanciateQuestion($question_id);
6253  $qti_question = $question->toXML(false);
6254  $qti_question = preg_replace("/<questestinterop>/", "", $qti_question);
6255  $qti_question = preg_replace("/<\/questestinterop>/", "", $qti_question);
6256  if (strpos($xml, "</section>") !== false)
6257  {
6258  $xml = str_replace("</section>", "$qti_question</section>", $xml);
6259  }
6260  else
6261  {
6262  $xml = str_replace("<section ident=\"1\"/>", "<section ident=\"1\">\n$qti_question</section>", $xml);
6263  }
6264  }
6265  return $xml;
6266  }
6267 
6274  function exportPagesXML(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
6275  {
6276  global $ilBench;
6277 
6278  $this->mob_ids = array();
6279  $this->file_ids = array();
6280 
6281  $attrs = array();
6282  $attrs["Type"] = "Test";
6283  $a_xml_writer->xmlStartTag("ContentObject", $attrs);
6284 
6285  // MetaData
6286  $this->exportXMLMetaData($a_xml_writer);
6287 
6288  // PageObjects
6289  $expLog->write(date("[y-m-d H:i:s] ")."Start Export Page Objects");
6290  $ilBench->start("ContentObjectExport", "exportPageObjects");
6291  $this->exportXMLPageObjects($a_xml_writer, $a_inst, $expLog);
6292  $ilBench->stop("ContentObjectExport", "exportPageObjects");
6293  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export Page Objects");
6294 
6295  // MediaObjects
6296  $expLog->write(date("[y-m-d H:i:s] ")."Start Export Media Objects");
6297  $ilBench->start("ContentObjectExport", "exportMediaObjects");
6298  $this->exportXMLMediaObjects($a_xml_writer, $a_inst, $a_target_dir, $expLog);
6299  $ilBench->stop("ContentObjectExport", "exportMediaObjects");
6300  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export Media Objects");
6301 
6302  // FileItems
6303  $expLog->write(date("[y-m-d H:i:s] ")."Start Export File Items");
6304  $ilBench->start("ContentObjectExport", "exportFileItems");
6305  $this->exportFileItems($a_target_dir, $expLog);
6306  $ilBench->stop("ContentObjectExport", "exportFileItems");
6307  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export File Items");
6308 
6309  $a_xml_writer->xmlEndTag("ContentObject");
6310  }
6311 
6318  function exportXMLMetaData(&$a_xml_writer)
6319  {
6320  include_once "./Services/MetaData/classes/class.ilMD2XML.php";
6321  $md2xml = new ilMD2XML($this->getId(), 0, $this->getType());
6322  $md2xml->setExportMode(true);
6323  $md2xml->startExport();
6324  $a_xml_writer->appendXML($md2xml->getXML());
6325  }
6326 
6332  function modifyExportIdentifier($a_tag, $a_param, $a_value)
6333  {
6334  if ($a_tag == "Identifier" && $a_param == "Entry")
6335  {
6336  include_once "./Services/Utilities/classes/class.ilUtil.php";
6337  $a_value = ilUtil::insertInstIntoID($a_value);
6338  }
6339 
6340  return $a_value;
6341  }
6342 
6343 
6350  function exportXMLPageObjects(&$a_xml_writer, $a_inst, &$expLog)
6351  {
6352  global $ilBench;
6353 
6354  include_once "./Modules/LearningModule/classes/class.ilLMPageObject.php";
6355 
6356  foreach ($this->questions as $question_id)
6357  {
6358  $ilBench->start("ContentObjectExport", "exportPageObject");
6359  $expLog->write(date("[y-m-d H:i:s] ")."Page Object ".$question_id);
6360 
6361  $attrs = array();
6362  $a_xml_writer->xmlStartTag("PageObject", $attrs);
6363 
6364 
6365  // export xml to writer object
6366  $ilBench->start("ContentObjectExport", "exportPageObject_XML");
6367  $page_object = new ilPageObject("qpl", $question_id);
6368  $page_object->buildDom();
6369  $page_object->insertInstIntoIDs($a_inst);
6370  $mob_ids = $page_object->collectMediaObjects(false);
6371  $file_ids = $page_object->collectFileItems();
6372  $xml = $page_object->getXMLFromDom(false, false, false, "", true);
6373  $xml = str_replace("&","&amp;", $xml);
6374  $a_xml_writer->appendXML($xml);
6375  $page_object->freeDom();
6376  unset ($page_object);
6377 
6378  $ilBench->stop("ContentObjectExport", "exportPageObject_XML");
6379 
6380  // collect media objects
6381  $ilBench->start("ContentObjectExport", "exportPageObject_CollectMedia");
6382  //$mob_ids = $page_obj->getMediaObjectIDs();
6383  foreach($mob_ids as $mob_id)
6384  {
6385  $this->mob_ids[$mob_id] = $mob_id;
6386  }
6387  $ilBench->stop("ContentObjectExport", "exportPageObject_CollectMedia");
6388 
6389  // collect all file items
6390  $ilBench->start("ContentObjectExport", "exportPageObject_CollectFileItems");
6391  //$file_ids = $page_obj->getFileItemIds();
6392  foreach($file_ids as $file_id)
6393  {
6394  $this->file_ids[$file_id] = $file_id;
6395  }
6396  $ilBench->stop("ContentObjectExport", "exportPageObject_CollectFileItems");
6397 
6398  $a_xml_writer->xmlEndTag("PageObject");
6399  //unset($page_obj);
6400 
6401  $ilBench->stop("ContentObjectExport", "exportPageObject");
6402 
6403 
6404  }
6405  }
6406 
6413  function exportXMLMediaObjects(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
6414  {
6415  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
6416 
6417  foreach ($this->mob_ids as $mob_id)
6418  {
6419  $expLog->write(date("[y-m-d H:i:s] ")."Media Object ".$mob_id);
6420  if (ilObjMediaObject::_exists($mob_id))
6421  {
6422  $media_obj = new ilObjMediaObject($mob_id);
6423  $media_obj->exportXML($a_xml_writer, $a_inst);
6424  $media_obj->exportFiles($a_target_dir);
6425  unset($media_obj);
6426  }
6427  }
6428  }
6429 
6434  function exportFileItems($a_target_dir, &$expLog)
6435  {
6436  include_once "./Modules/File/classes/class.ilObjFile.php";
6437 
6438  foreach ($this->file_ids as $file_id)
6439  {
6440  $expLog->write(date("[y-m-d H:i:s] ")."File Item ".$file_id);
6441  $file_obj = new ilObjFile($file_id, false);
6442  $file_obj->export($a_target_dir);
6443  unset($file_obj);
6444  }
6445  }
6446 
6451  function getImportMapping()
6452  {
6453  if (!is_array($this->import_mapping))
6454  {
6455  return array();
6456  }
6457  else
6458  {
6459  return $this->import_mapping;
6460  }
6461  }
6462 
6472  function getECTSGrade($passed_array, $reached_points, $max_points)
6473  {
6474  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);
6475  }
6476 
6485  function _getECTSGrade($points_passed, $reached_points, $max_points, $a, $b, $c, $d, $e, $fx)
6486  {
6487  include_once "./classes/class.ilStatistics.php";
6488  // calculate the median
6489  $passed_statistics = new ilStatistics();
6490  $passed_statistics->setData($points_passed);
6491  $ects_percentiles = array
6492  (
6493  "A" => $passed_statistics->quantile($a),
6494  "B" => $passed_statistics->quantile($b),
6495  "C" => $passed_statistics->quantile($c),
6496  "D" => $passed_statistics->quantile($d),
6497  "E" => $passed_statistics->quantile($e)
6498  );
6499  if (count($points_passed) && ($reached_points >= $ects_percentiles["A"]))
6500  {
6501  return "A";
6502  }
6503  else if (count($points_passed) && ($reached_points >= $ects_percentiles["B"]))
6504  {
6505  return "B";
6506  }
6507  else if (count($points_passed) && ($reached_points >= $ects_percentiles["C"]))
6508  {
6509  return "C";
6510  }
6511  else if (count($points_passed) && ($reached_points >= $ects_percentiles["D"]))
6512  {
6513  return "D";
6514  }
6515  else if (count($points_passed) && ($reached_points >= $ects_percentiles["E"]))
6516  {
6517  return "E";
6518  }
6519  else if (strcmp($fx, "") != 0)
6520  {
6521  if ($max_points > 0)
6522  {
6523  $percentage = ($reached_points / $max_points) * 100.0;
6524  if ($percentage < 0) $percentage = 0.0;
6525  }
6526  else
6527  {
6528  $percentage = 0.0;
6529  }
6530  if ($percentage >= $fx)
6531  {
6532  return "FX";
6533  }
6534  else
6535  {
6536  return "F";
6537  }
6538  }
6539  else
6540  {
6541  return "F";
6542  }
6543  }
6544 
6545  function checkMarks()
6546  {
6547  return $this->mark_schema->checkMarks();
6548  }
6549 
6550  function getMarkSchema()
6551  {
6552  return $this->mark_schema;
6553  }
6554 
6562  function setAuthor($author = "")
6563  {
6564  $this->author = $author;
6565  }
6566 
6576  function saveAuthorToMetadata($a_author = "")
6577  {
6578  $md =& new ilMD($this->getId(), 0, $this->getType());
6579  $md_life =& $md->getLifecycle();
6580  if (!$md_life)
6581  {
6582  if (strlen($a_author) == 0)
6583  {
6584  global $ilUser;
6585  $a_author = $ilUser->getFullname();
6586  }
6587 
6588  $md_life =& $md->addLifecycle();
6589  $md_life->save();
6590  $con =& $md_life->addContribute();
6591  $con->setRole("Author");
6592  $con->save();
6593  $ent =& $con->addEntity();
6594  $ent->setEntity($a_author);
6595  $ent->save();
6596  }
6597  }
6598 
6604  function createMetaData()
6605  {
6607  $this->saveAuthorToMetadata();
6608  }
6609 
6617  function getAuthor()
6618  {
6619  $author = array();
6620  include_once "./Services/MetaData/classes/class.ilMD.php";
6621  $md =& new ilMD($this->getId(), 0, $this->getType());
6622  $md_life =& $md->getLifecycle();
6623  if ($md_life)
6624  {
6625  $ids =& $md_life->getContributeIds();
6626  foreach ($ids as $id)
6627  {
6628  $md_cont =& $md_life->getContribute($id);
6629  if (strcmp($md_cont->getRole(), "Author") == 0)
6630  {
6631  $entids =& $md_cont->getEntityIds();
6632  foreach ($entids as $entid)
6633  {
6634  $md_ent =& $md_cont->getEntity($entid);
6635  array_push($author, $md_ent->getEntity());
6636  }
6637  }
6638  }
6639  }
6640  return join($author, ",");
6641  }
6642 
6650  function _lookupAuthor($obj_id)
6651  {
6652  $author = array();
6653  include_once "./Services/MetaData/classes/class.ilMD.php";
6654  $md =& new ilMD($obj_id, 0, "tst");
6655  $md_life =& $md->getLifecycle();
6656  if ($md_life)
6657  {
6658  $ids =& $md_life->getContributeIds();
6659  foreach ($ids as $id)
6660  {
6661  $md_cont =& $md_life->getContribute($id);
6662  if (strcmp($md_cont->getRole(), "Author") == 0)
6663  {
6664  $entids =& $md_cont->getEntityIds();
6665  foreach ($entids as $entid)
6666  {
6667  $md_ent =& $md_cont->getEntity($entid);
6668  array_push($author, $md_ent->getEntity());
6669  }
6670  }
6671  }
6672  }
6673  return join($author, ",");
6674  }
6675 
6682  function &_getAvailableTests($use_object_id = FALSE)
6683  {
6684  global $ilUser;
6685  global $ilDB;
6686 
6687  $result_array = array();
6688  $tests = ilUtil::_getObjectsByOperations("tst","write", $ilUser->getId(), -1);
6689  if (count($tests))
6690  {
6691  $titles = ilObject::_prepareCloneSelection($tests, "tst");
6692  foreach ($tests as $ref_id)
6693  {
6694  if ($use_object_id)
6695  {
6696  $obj_id = ilObject::_lookupObjId($ref_id);
6697  $result_array[$obj_id] = $titles[$ref_id];
6698  }
6699  else
6700  {
6701  $result_array[$ref_id] = $titles[$ref_id];
6702  }
6703  }
6704  }
6705  return $result_array;
6706  }
6707 
6714  function cloneRandomQuestions($new_id)
6715  {
6716  global $ilDB;
6717 
6718  if ($new_id > 0)
6719  {
6720  $result = $ilDB->queryF("SELECT * FROM tst_test_random WHERE test_fi = %s ORDER BY sequence, test_random_id",
6721  array('integer'),
6722  array($this->getTestId())
6723  );
6724  if ($result->numRows())
6725  {
6726  while ($row = $ilDB->fetchAssoc($result))
6727  {
6728  $next_id = $ilDB->nextId('tst_test_random');
6729  $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)",
6730  array('integer', 'integer', 'integer', 'integer', 'integer', 'integer'),
6731  array($next_id, $new_id, $row["questionpool_fi"], $row["num_of_q"], time(), $row['sequence'])
6732  );
6733  }
6734  }
6735  }
6736  }
6737 
6738 
6747  public function cloneObject($a_target_id,$a_copy_id = 0)
6748  {
6749  global $ilDB,$ilLog;
6750 
6751  $this->loadFromDb();
6752 
6753  // Copy settings
6754  $newObj = parent::cloneObject($a_target_id,$a_copy_id);
6755  $this->cloneMetaData($newObj);
6756  $newObj->setAnonymity($this->getAnonymity());
6757  $newObj->setAnswerFeedback($this->getAnswerFeedback());
6758  $newObj->setAnswerFeedbackPoints($this->getAnswerFeedbackPoints());
6759  $newObj->setAuthor($this->getAuthor());
6760  $newObj->setCountSystem($this->getCountSystem());
6761  $newObj->setECTSFX($this->getECTSFX());
6762  $newObj->setECTSGrades($this->getECTSGrades());
6763  $newObj->setECTSOutput($this->getECTSOutput());
6764  $newObj->setEnableProcessingTime($this->getEnableProcessingTime());
6765  $newObj->setEndingTime($this->getEndingTime());
6766  $newObj->setFixedParticipants($this->getFixedParticipants());
6767  $newObj->setInstantFeedbackSolution($this->getInstantFeedbackSolution());
6768  $newObj->setIntroduction($this->getIntroduction());
6769  $newObj->setFinalStatement($this->getFinalStatement());
6770  $newObj->setShowInfo($this->getShowInfo());
6771  $newObj->setForceJS($this->getForceJS());
6772  $newObj->setCustomStyle($this->getCustomStyle());
6773  $newObj->setKiosk($this->getKiosk());
6774  $newObj->setShowFinalStatement($this->getShowFinalStatement());
6775  $newObj->setListOfQuestionsSettings($this->getListOfQuestionsSettings());
6776  $newObj->setMCScoring($this->getMCScoring());
6777  $newObj->setMailNotification($this->getMailNotification());
6778  $newObj->setMailNotificationType($this->getMailNotificationType());
6779  $newObj->setNrOfTries($this->getNrOfTries());
6780  $newObj->setPassScoring($this->getPassScoring());
6781  $newObj->setPassword($this->getPassword());
6782  $newObj->setProcessingTime($this->getProcessingTime());
6783  $newObj->setRandomQuestionCount($this->getRandomQuestionCount());
6784  $newObj->setRandomTest($this->isRandomTest());
6785  $newObj->setReportingDate($this->getReportingDate());
6786  $newObj->setResetProcessingTime($this->getResetProcessingTime());
6787  $newObj->setResultsPresentation($this->getResultsPresentation());
6788  $newObj->setScoreCutting($this->getScoreCutting());
6789  $newObj->setScoreReporting($this->getScoreReporting());
6790  $newObj->setSequenceSettings($this->getSequenceSettings());
6791  $newObj->setShowCancel($this->getShowCancel());
6792  $newObj->setShowMarker($this->getShowMarker());
6793  $newObj->setShuffleQuestions($this->getShuffleQuestions());
6794  $newObj->setStartingTime($this->getStartingTime());
6795  $newObj->setTitleOutput($this->getTitleOutput());
6796  $newObj->setUsePreviousAnswers($this->getUsePreviousAnswers());
6797  $newObj->setCertificateVisibility($this->getCertificateVisibility());
6798  $newObj->mark_schema = clone $this->mark_schema;
6799  $newObj->setEnabledViewMode($this->getEnabledViewMode());
6800  $newObj->setTemplate($this->getTemplate());
6801  $newObj->setPoolUsage($this->getPoolUsage());
6802 
6803  $newObj->saveToDb();
6804 
6805  // clone certificate
6806  include_once "./Services/Certificate/classes/class.ilCertificate.php";
6807  include_once "./Modules/Test/classes/class.ilTestCertificateAdapter.php";
6808  $cert = new ilCertificate(new ilTestCertificateAdapter($this));
6809  $newcert = new ilCertificate(new ilTestCertificateAdapter($newObj));
6810  $cert->cloneCertificate($newcert);
6811 
6812  if ($this->isRandomTest())
6813  {
6814  $newObj->saveRandomQuestionCount($newObj->getRandomQuestionCount());
6815  $this->cloneRandomQuestions($newObj->getTestId());
6816  }
6817  else
6818  {
6819  include_once("./Services/CopyWizard/classes/class.ilCopyWizardOptions.php");
6820  $cwo = ilCopyWizardOptions::_getInstance($a_copy_id);
6821 
6822  // clone the questions
6823  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
6824  foreach ($this->questions as $key => $question_id)
6825  {
6826  $question = ilObjTest::_instanciateQuestion($question_id);
6827  $newObj->questions[$key] = $question->duplicate(true, null, null, null, $newObj->getId());
6828  $original_id = assQuestion::_getOriginalId($question_id);
6829  $question = ilObjTest::_instanciateQuestion($newObj->questions[$key]);
6830  $question->saveToDb($original_id);
6831 
6832  // Save the mapping of old question id <-> new question id
6833  // This will be used in class.ilObjCourse::cloneDependencies to copy learning objectives
6834  $cwo->appendMapping($this->getRefId().'_'.$question_id,$newObj->getRefId().'_'.$newObj->questions[$key]);
6835  $ilLog->write(__METHOD__.': Added mapping '.$this->getRefId().'_'.$question_id.' <-> ' .
6836  $newObj->getRefId().'_'.$newObj->questions[$key]);
6837  }
6838  }
6839  $newObj->saveToDb();
6840  return $newObj;
6841  }
6842 
6849  function getQuestionCount()
6850  {
6851  $num = 0;
6852 
6853  if ($this->isRandomTest())
6854  {
6855  if ($this->getRandomQuestionCount())
6856  {
6857  $num = $this->getRandomQuestionCount();
6858  $qpls =& $this->getRandomQuestionpools();
6859  $maxcount = 0;
6860  foreach ($qpls as $data)
6861  {
6862  $maxcount += $data["contains"];
6863  }
6864  if ($num > $maxcount) $num = $maxcount;
6865  }
6866  else
6867  {
6868  $qpls =& $this->getRandomQuestionpools();
6869  foreach ($qpls as $data)
6870  {
6871  $add = ($data["count"] <= $data["contains"]) ? $data["count"] : $data["contains"];
6872  $num += $add;
6873  }
6874  }
6875  }
6876  else
6877  {
6878  $num = count($this->questions);
6879  }
6880  return $num;
6881  }
6882 
6890  {
6891  global $ilDB;
6892 
6893  $num = 0;
6894 
6895  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
6896  array('integer'),
6897  array($test_id)
6898  );
6899  if (!$result->numRows())
6900  {
6901  return 0;
6902  }
6903  $test = $ilDB->fetchAssoc($result);
6904 
6905  if ($test["random_test"] == 1)
6906  {
6907  $qpls = array();
6908  $counter = 0;
6909  $result = $ilDB->queryF("SELECT * FROM tst_test_random WHERE test_fi = %s ORDER BY sequence, test_random_id",
6910  array('integer'),
6911  array($test_id)
6912  );
6913  if ($result->numRows())
6914  {
6915  while ($row = $ilDB->fetchAssoc($result))
6916  {
6917  $countresult = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE obj_fi = %s AND qpl_questions.tstamp > 0 AND original_id IS NULL",
6918  array('integer'),
6919  $row["questionpool_fi"]
6920  );
6921  $contains = $countresult->numRows();
6922  $qpls[$counter] = array(
6923  "index" => $counter,
6924  "count" => $row["num_of_q"],
6925  "qpl" => $row["questionpool_fi"],
6926  "contains" => $contains
6927  );
6928  $counter++;
6929  }
6930  }
6931  if ($test["random_question_count"] > 0)
6932  {
6933  $num = $test["random_question_count"];
6934  $maxcount = 0;
6935  foreach ($qpls as $data)
6936  {
6937  $maxcount += $data["contains"];
6938  }
6939  if ($num > $maxcount) $num = $maxcount;
6940  }
6941  else
6942  {
6943  $num = 0;
6944  foreach ($qpls as $data)
6945  {
6946  $add = ($data["count"] <= $data["contains"]) ? $data["count"] : $data["contains"];
6947  $num += $add;
6948  }
6949  }
6950  }
6951  else
6952  {
6953  $result = $ilDB->queryF("SELECT test_question_id FROM tst_test_question WHERE test_fi = %s",
6954  array('integer'),
6955  array($test_id)
6956  );
6957  $num = $result->numRows();
6958  }
6959  return $num;
6960  }
6961 
6968  {
6969  global $ilDB;
6970 
6971  // delete eventually set questions of a previous non-random test
6972  $this->removeAllTestEditings();
6973  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
6974  array('integer'),
6975  array($this->getTestId())
6976  );
6977  $this->questions = array();
6978  $this->saveCompleteStatus();
6979  }
6980 
6987  {
6988  global $ilDB;
6989  // delete eventually set random question pools of a previous random test
6990  $this->removeAllTestEditings();
6991  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
6992  array('integer'),
6993  array($this->getTestId())
6994  );
6995  $this->questions = array();
6996  $this->saveCompleteStatus();
6997  }
6998 
7006  function logAction($logtext = "", $question_id = "")
7007  {
7008  global $ilUser;
7009 
7010  $original_id = "";
7011  if (strcmp($question_id, "") != 0)
7012  {
7013  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7014  $original_id = assQuestion::_getOriginalId($question_id);
7015  }
7016  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
7017  ilObjAssessmentFolder::_addLog($ilUser->getId(), $this->getId(), $logtext, $question_id, $original_id, TRUE, $this->getRefId());
7018  }
7019 
7028  {
7029  global $ilDB;
7030  $object_id = FALSE;
7031  $result = $ilDB->queryF("SELECT obj_fi FROM tst_tests WHERE test_id = %s",
7032  array('integer'),
7033  array($test_id)
7034  );
7035  if ($result->numRows())
7036  {
7037  $row = $ilDB->fetchAssoc($result);
7038  $object_id = $row["obj_fi"];
7039  }
7040  return $object_id;
7041  }
7042 
7050  function _getObjectIDFromActiveID($active_id)
7051  {
7052  global $ilDB;
7053  $object_id = FALSE;
7054  $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",
7055  array('integer'),
7056  array($active_id)
7057  );
7058  if ($result->numRows())
7059  {
7060  $row = $ilDB->fetchAssoc($result);
7061  $object_id = $row["obj_fi"];
7062  }
7063  return $object_id;
7064  }
7065 
7073  function _getTestIDFromObjectID($object_id)
7074  {
7075  global $ilDB;
7076  $test_id = FALSE;
7077  $result = $ilDB->queryF("SELECT test_id FROM tst_tests WHERE obj_fi = %s",
7078  array('integer'),
7079  array($object_id)
7080  );
7081  if ($result->numRows())
7082  {
7083  $row = $ilDB->fetchAssoc($result);
7084  $test_id = $row["test_id"];
7085  }
7086  return $test_id;
7087  }
7088 
7097  function getTextAnswer($active_id, $question_id, $pass = NULL)
7098  {
7099  global $ilDB;
7100 
7101  $res = "";
7102  if (($active_id) && ($question_id))
7103  {
7104  if (is_null($pass))
7105  {
7106  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7107  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
7108  }
7109  $result = $ilDB->queryF("SELECT value1 FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
7110  array('integer', 'integer', 'integer'),
7111  array($active_id, $question_id, $pass)
7112  );
7113  if ($result->numRows() == 1)
7114  {
7115  $row = $ilDB->fetchAssoc($result);
7116  $res = $row["value1"];
7117  }
7118  }
7119  return $res;
7120  }
7121 
7129  function getQuestiontext($question_id)
7130  {
7131  global $ilDB;
7132 
7133  $res = "";
7134  if ($question_id)
7135  {
7136  $result = $ilDB->queryF("SELECT question_text FROM qpl_questions WHERE question_id = %s",
7137  array('integer'),
7138  array($question_id)
7139  );
7140  if ($result->numRows() == 1)
7141  {
7142  $row = $ilDB->fetchAssoc($result);
7143  $res = $row["question_text"];
7144  }
7145  }
7146  return $res;
7147  }
7148 
7155  function &getInvitedUsers($user_id="", $order="login, lastname, firstname")
7156  {
7157  global $ilDB;
7158 
7159  $result_array = array();
7160 
7161  if ($this->getAnonymity())
7162  {
7163  if (is_numeric($user_id))
7164  {
7165  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " .
7166  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7167  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7168  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
7169  "ORDER BY $order",
7170  array('text', 'text', 'text', 'integer', 'integer'),
7171  array("", $this->lng->txt("unknown"), "", $this->getTestId(), $user_id)
7172  );
7173  }
7174  else
7175  {
7176  $result = $ilDB->queryF("SELECT tst_active.active_id, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " .
7177  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7178  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7179  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
7180  "ORDER BY $order",
7181  array('text', 'text', 'text', 'integer'),
7182  array("", $this->lng->txt("unknown"), "", $this->getTestId())
7183  );
7184  }
7185  }
7186  else
7187  {
7188  if (is_numeric($user_id))
7189  {
7190  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " .
7191  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7192  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7193  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
7194  "ORDER BY $order",
7195  array('integer', 'integer'),
7196  array($this->getTestId(), $user_id)
7197  );
7198  }
7199  else
7200  {
7201  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " .
7202  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7203  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7204  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
7205  "ORDER BY $order",
7206  array('integer'),
7207  array($this->getTestId())
7208  );
7209  }
7210  }
7211  $result_array = array();
7212  while ($row = $ilDB->fetchAssoc($result))
7213  {
7214  $result_array[$row['usr_id']]= $row;
7215  }
7216  return $result_array;
7217  }
7218 
7226  {
7227  global $ilDB;
7228 
7229  if ($this->getAnonymity())
7230  {
7231  $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 ".
7232  "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),
7233  array('text', 'text', 'text', 'integer'),
7234  array("", $this->lng->txt("unknown"), "", $this->getTestId())
7235  );
7236  }
7237  else
7238  {
7239  $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 ".
7240  "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),
7241  array('integer'),
7242  array($this->getTestId())
7243  );
7244  }
7245  $data = array();
7246  while ($row = $ilDB->fetchAssoc($result))
7247  {
7248  $data[$row['active_id']] = $row;
7249  }
7250  foreach ($data as $index => $participant)
7251  {
7252  if (strlen(trim($participant["firstname"].$participant["lastname"])) == 0)
7253  {
7254  $data[$index]["lastname"] = $this->lng->txt("deleted_user");
7255  }
7256  }
7257  return $data;
7258  }
7259 
7260  public function getTestParticipantsForManualScoring($filter = NULL)
7261  {
7262  global $ilDB;
7263 
7264  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
7266  if (count($scoring) == 0) return array();
7267 
7268  $participants =& $this->getTestParticipants();
7269  $filtered_participants = array();
7270  foreach ($participants as $active_id => $participant)
7271  {
7272  $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",
7273  array("integer"),
7274  array($active_id)
7275  );
7276  $count = $result->numRows();
7277  if ($count > 0)
7278  {
7279  switch ($filter)
7280  {
7281  case 1: // only active users
7282  if ($participant->active) $filtered_participants[$active_id] = $participant;
7283  break;
7284  case 2: // only inactive users
7285  if (!$participant->active) $filtered_participants[$active_id] = $participant;
7286  break;
7287  case 3: // all users
7288  $filtered_participants[$active_id] = $participant;
7289  break;
7290  case 4:
7291  // already scored participants
7292  //$found = 0;
7293  //while ($row = $ilDB->fetchAssoc($result))
7294  //{
7295  // if ($row["manual"]) $found++;
7296  //}
7297  //if ($found == $count)
7298  //{
7299  //$filtered_participants[$active_id] = $participant;
7300  //}
7301  //else
7302  //{
7303  $assessmentSetting = new ilSetting("assessment");
7304  $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id);
7305  if ($manscoring_done) $filtered_participants[$active_id] = $participant;
7306  //}
7307  break;
7308  case 5:
7309  // unscored participants
7310  //$found = 0;
7311  //while ($row = $ilDB->fetchAssoc($result))
7312  //{
7313  // if ($row["manual"]) $found++;
7314  //}
7315  //if ($found == 0)
7316  //{
7317  $assessmentSetting = new ilSetting("assessment");
7318  $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id);
7319  if (!$manscoring_done) $filtered_participants[$active_id] = $participant;
7320  //}
7321  break;
7322  case 6:
7323  // partially scored participants
7324  $found = 0;
7325  while ($row = $ilDB->fetchAssoc($result))
7326  {
7327  if ($row["manual"]) $found++;
7328  }
7329  if (($found > 0) && ($found < $count)) $filtered_participants[$active_id] = $participant;
7330  break;
7331  default:
7332  $filtered_participants[$active_id] = $participant;
7333  break;
7334  }
7335  }
7336  }
7337  return $filtered_participants;
7338  }
7339 
7347  function &getUserData($ids)
7348  {
7349  global $ilDB;
7350 
7351  if (!is_array($ids) || count($ids) ==0) return array();
7352 
7353  if ($this->getAnonymity())
7354  {
7355  $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",
7356  array('text', 'text', 'text'),
7357  array("", $this->lng->txt("unknown"), "")
7358  );
7359  }
7360  else
7361  {
7362  $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");
7363  }
7364 
7365  $result_array = array();
7366  while ($row = $ilDB->fetchAssoc($result))
7367  {
7368  $result_array[$row["usr_id"]]= $row;
7369  }
7370  return $result_array;
7371  }
7372 
7373  function &getGroupData($ids)
7374  {
7375  if (!is_array($ids) || count($ids) ==0) return array();
7376  $result = array();
7377  foreach ($ids as $ref_id)
7378  {
7379  $obj_id = ilObject::_lookupObjId($ref_id);
7380  $result[$ref_id] = array("ref_id" => $ref_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id));
7381  }
7382  return $result;
7383  }
7384 
7385  function &getRoleData($ids)
7386  {
7387  if (!is_array($ids) || count($ids) ==0) return array();
7388  $result = array();
7389  foreach ($ids as $obj_id)
7390  {
7391  $result[$obj_id] = array("obj_id" => $obj_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id));
7392  }
7393  return $result;
7394  }
7395 
7396 
7403  function inviteGroup($group_id)
7404  {
7405  include_once "./Modules/Group/classes/class.ilObjGroup.php";
7406  $group = new ilObjGroup($group_id);
7407  $members = $group->getGroupMemberIds();
7408  include_once './Services/User/classes/class.ilObjUser.php';
7409  foreach ($members as $user_id)
7410  {
7411  $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id));
7412  }
7413  }
7414 
7421  function inviteRole($role_id)
7422  {
7423  global $rbacreview;
7424  $members = $rbacreview->assignedUsers($role_id,"usr_id");
7425  include_once './Services/User/classes/class.ilObjUser.php';
7426  foreach ($members as $user_id)
7427  {
7428  $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id));
7429  }
7430  }
7431 
7432 
7433 
7440  function disinviteUser($user_id)
7441  {
7442  global $ilDB;
7443 
7444  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
7445  array('integer', 'integer'),
7446  array($this->getTestId(), $user_id)
7447  );
7448  }
7449 
7456  function inviteUser($user_id, $client_ip="")
7457  {
7458  global $ilDB;
7459 
7460  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
7461  array('integer', 'integer'),
7462  array($this->getTestId(), $user_id)
7463  );
7464  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_invited_user (test_fi, user_fi, clientip, tstamp) VALUES (%s, %s, %s, %s)",
7465  array('integer', 'integer', 'text', 'integer'),
7466  array($this->getTestId(), $user_id, (strlen($client_ip)) ? $client_ip : NULL, time())
7467  );
7468  }
7469 
7470 
7471  function setClientIP($user_id, $client_ip)
7472  {
7473  global $ilDB;
7474 
7475  $affectedRows = $ilDB->manipulateF("UPDATE tst_invited_user SET clientip = %s, tstamp = %s WHERE test_fi=%s and user_fi=%s",
7476  array('text', 'integer', 'integer', 'integer'),
7477  array((strlen($client_ip)) ? $client_ip : NULL, time(), $this->getTestId(), $user_id)
7478  );
7479  }
7480 
7486  function _getSolvedQuestions($active_id, $question_fi = null)
7487  {
7488  global $ilDB;
7489  if (is_numeric($question_fi))
7490  {
7491  $result = $ilDB->queryF("SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s AND question_fi=%s",
7492  array('integer', 'integer'),
7493  array($active_id, $question_fi)
7494  );
7495  }
7496  else
7497  {
7498  $result = $ilDB->queryF("SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s",
7499  array('integer'),
7500  array($active_id)
7501  );
7502  }
7503  $result_array = array();
7504  while ($row = $ilDB->fetchAssoc($result))
7505  {
7506  $result_array[$row["question_fi"]]= $row;
7507  }
7508  return $result_array;
7509  }
7510 
7511 
7515  function setQuestionSetSolved($value, $question_id, $user_id)
7516  {
7517  global $ilDB;
7518 
7519  $active_id = $this->getActiveIdOfUser($user_id);
7520  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s AND question_fi = %s",
7521  array('integer', 'integer'),
7522  array($active_id, $question_id)
7523  );
7524  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_qst_solved (solved, question_fi, active_fi) VALUES (%s, %s, %s)",
7525  array('integer', 'integer', 'integer'),
7526  array($value, $question_id, $active_id)
7527  );
7528  }
7529 
7530 
7534  function setActiveTestSubmitted($user_id)
7535  {
7536  global $ilDB, $ilLog;
7537 
7538  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s, tstamp = %s WHERE test_fi = %s AND user_fi = %s",
7539  array('integer', 'timestamp', 'integer', 'integer', 'integer'),
7540  array(1, date('Y-m-d H:i:s'), time(), $this->getTestId(), $user_id)
7541  );
7542  $this->testSession = NULL;
7543  }
7544 
7548  function isTestFinished($active_id)
7549  {
7550  global $ilDB;
7551 
7552  $result = $ilDB->queryF("SELECT submitted FROM tst_active WHERE active_id=%s AND submitted=%s",
7553  array('integer', 'integer'),
7554  array($active_id, 1)
7555  );
7556  return $result->numRows() == 1;
7557  }
7558 
7562  function isActiveTestSubmitted($user_id = null)
7563  {
7564  global $ilUser;
7565  global $ilDB;
7566 
7567  if (!is_numeric($user_id))
7568  $user_id = $ilUser->getId();
7569 
7570  $result = $ilDB->queryF("SELECT submitted FROM tst_active WHERE test_fi=%s AND user_fi=%s AND submitted=%s",
7571  array('integer', 'integer', 'integer'),
7572  array($this->getTestId(), $user_id, 1)
7573  );
7574  return $result->numRows() == 1;
7575  }
7576 
7581  {
7582  return $this->getNrOfTries() != 0;
7583  }
7584 
7585 
7590  function isNrOfTriesReached($tries)
7591  {
7592  return $tries >= (int) $this->getNrOfTries();
7593  }
7594 
7595 
7604  function getAllTestResults($participants, $prepareForCSV = true)
7605  {
7606  $results = array();
7607  $row = array(
7608  "user_id" => $this->lng->txt("user_id"),
7609  "matriculation" => $this->lng->txt("matriculation"),
7610  "lastname" => $this->lng->txt("lastname"),
7611  "firstname" => $this->lng->txt("firstname"),
7612  "login" =>$this->lng->txt("login"),
7613  "reached_points" => $this->lng->txt("tst_reached_points"),
7614  "max_points" => $this->lng->txt("tst_maximum_points"),
7615  "percent_value" => $this->lng->txt("tst_percent_solved"),
7616  "mark" => $this->lng->txt("tst_mark"),
7617  "ects" => $this->lng->txt("ects_grade")
7618  );
7619  $results[] = $row;
7620  if (count($participants))
7621  {
7622  if ($this->ects_output)
7623  {
7624  $passed_array =& $this->getTotalPointsPassedArray();
7625  }
7626  foreach ($participants as $active_id => $user_rec)
7627  {
7628  $row = array();
7629  $reached_points = 0;
7630  $max_points = 0;
7631  foreach ($this->questions as $value)
7632  {
7633  $question =& ilObjTest::_instanciateQuestion($value);
7634  if (is_object($question))
7635  {
7636  $max_points += $question->getMaximumPoints();
7637  $reached_points += $question->getReachedPoints($active_id);
7638  }
7639  }
7640  if ($max_points > 0)
7641  {
7642  $percentvalue = $reached_points / $max_points;
7643  if ($percentvalue < 0) $percentvalue = 0.0;
7644  }
7645  else
7646  {
7647  $percentvalue = 0;
7648  }
7649  $mark_obj = $this->mark_schema->getMatchingMark($percentvalue * 100);
7650  $passed = "";
7651  if ($mark_obj)
7652  {
7653  $mark = $mark_obj->getOfficialName();
7654  $ects_mark = $this->getECTSGrade($passed_array, $reached_points, $max_points);
7655  }
7656  if ($this->getAnonymity())
7657  {
7658  $user_rec['firstname'] = "";
7659  $user_rec['lastname'] = $this->lng->txt("unknown");
7660  }
7661  $row = array(
7662  "user_id"=>$user_rec['usr_id'],
7663  "matriculation" => $user_rec['matriculation'],
7664  "lastname" => $user_rec['lastname'],
7665  "firstname" => $user_rec['firstname'],
7666  "login"=>$user_rec['login'],
7667  "reached_points" => $reached_points,
7668  "max_points" => $max_points,
7669  "percent_value" => $percentvalue,
7670  "mark" => $mark,
7671  "ects" => $ects_mark
7672  );
7673  $results[] = $prepareForCSV ? $this->processCSVRow ($row, true) : $row;
7674  }
7675  }
7676  return $results;
7677  }
7678 
7689  function &processCSVRow($row, $quoteAll = FALSE, $separator = ";")
7690  {
7691  $resultarray = array();
7692  foreach ($row as $rowindex => $entry)
7693  {
7694  $surround = FALSE;
7695  if ($quoteAll)
7696  {
7697  $surround = TRUE;
7698  }
7699  if (strpos($entry, "\"") !== FALSE)
7700  {
7701  $entry = str_replace("\"", "\"\"", $entry);
7702  $surround = TRUE;
7703  }
7704  if (strpos($entry, $separator) !== FALSE)
7705  {
7706  $surround = TRUE;
7707  }
7708  // replace all CR LF with LF (for Excel for Windows compatibility
7709  $entry = str_replace(chr(13).chr(10), chr(10), $entry);
7710  if ($surround)
7711  {
7712  $resultarray[$rowindex] = utf8_decode("\"" . $entry . "\"");
7713  }
7714  else
7715  {
7716  $resultarray[$rowindex] = utf8_decode($entry);
7717  }
7718  }
7719  return $resultarray;
7720  }
7721 
7730  function _getPass($active_id)
7731  {
7732  global $ilDB;
7733  $result = $ilDB->queryF("SELECT tries FROM tst_active WHERE active_id = %s",
7734  array('integer'),
7735  array($active_id)
7736  );
7737  if ($result->numRows())
7738  {
7739  $row = $ilDB->fetchAssoc($result);
7740  return $row["tries"];
7741  }
7742  else
7743  {
7744  return 0;
7745  }
7746  }
7747 
7757  function _getMaxPass($active_id)
7758  {
7759  global $ilDB;
7760  $result = $ilDB->queryF("SELECT MAX(pass) maxpass FROM tst_test_result WHERE active_fi = %s",
7761  array('integer'),
7762  array($active_id)
7763  );
7764  if ($result->numRows())
7765  {
7766  $row = $ilDB->fetchAssoc($result);
7767  $max = $row["maxpass"];
7768  }
7769  else
7770  {
7771  $max = NULL;
7772  }
7773  return $max;
7774  }
7775 
7786  function _getBestPass($active_id)
7787  {
7788  global $ilDB;
7789 
7790  $result = $ilDB->queryF("SELECT * FROM tst_pass_result WHERE active_fi = %s",
7791  array('integer'),
7792  array($active_id)
7793  );
7794  if ($result->numRows())
7795  {
7796  $bestrow = null;
7797  $bestfactor = 0;
7798  while ($row = $ilDB->fetchAssoc($result))
7799  {
7800  if($row["maxpoints"] > 0)
7801  {
7802  $factor = $row["points"] / $row["maxpoints"];
7803  }
7804  else
7805  {
7806  $factor = 0;
7807  }
7808 
7809  if($factor > $bestfactor)
7810  {
7811  $bestrow = $row;
7812  $bestfactor = $factor;
7813  }
7814  }
7815  if (is_array($bestrow))
7816  {
7817  return $bestrow["pass"];
7818  }
7819  else
7820  {
7821  return 0;
7822  }
7823  }
7824  else
7825  {
7826  return 0;
7827  }
7828  }
7829 
7838  function _getResultPass($active_id)
7839  {
7840  $counted_pass = NULL;
7841  if (ilObjTest::_getPassScoring($active_id) == SCORE_BEST_PASS)
7842  {
7843  $counted_pass = ilObjTest::_getBestPass($active_id);
7844  }
7845  else
7846  {
7847  $counted_pass = ilObjTest::_getMaxPass($active_id);
7848  }
7849  return $counted_pass;
7850  }
7851 
7861  function getAnsweredQuestionCount($active_id, $pass = NULL)
7862  {
7863  if ($this->isRandomTest())
7864  {
7865  $this->loadQuestions($active_id, $pass);
7866  }
7867  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7868  $workedthrough = 0;
7869  foreach ($this->questions as $value)
7870  {
7871  if (assQuestion::_isWorkedThrough($active_id, $value, $pass))
7872  {
7873  $workedthrough += 1;
7874  }
7875  }
7876  return $workedthrough;
7877  }
7878 
7888  function getPassFinishDate($active_id, $pass)
7889  {
7890  global $ilDB;
7891  if (is_null($pass)) $pass = 0;
7892  $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",
7893  array('integer', 'integer'),
7894  array($active_id, $pass)
7895  );
7896  if ($result->numRows())
7897  {
7898  $row = $ilDB->fetchAssoc($result);
7899  return $row["tstamp"];
7900  }
7901  else
7902  {
7903  return 0;
7904  }
7905  }
7906 
7914  function isExecutable($user_id, $allowPassIncrease = FALSE)
7915  {
7916  $result = array(
7917  "executable" => true,
7918  "errormessage" => ""
7919  );
7920  if (!$this->startingTimeReached())
7921  {
7922  $result["executable"] = false;
7923  $result["errormessage"] = sprintf($this->lng->txt("detail_starting_time_not_reached"), ilFormat::ftimestamp2datetimeDB($this->getStartingTime()));
7924  return $result;
7925  }
7926  if ($this->endingTimeReached())
7927  {
7928  $result["executable"] = false;
7929  $result["errormessage"] = sprintf($this->lng->txt("detail_ending_time_reached"), ilFormat::ftimestamp2datetimeDB($this->getEndingTime()));
7930  return $result;
7931  }
7932 
7933  $active_id = $this->getActiveIdOfUser($user_id);
7934 
7935  if ($this->getEnableProcessingTime())
7936  {
7937  if ($active_id > 0)
7938  {
7939  $starting_time = $this->getStartingTimeOfUser($active_id);
7940  if ($starting_time !== FALSE)
7941  {
7943  {
7944  if ($allowPassIncrease && $this->getResetProcessingTime() && (($this->getNrOfTries() == 0) || ($this->getNrOfTries() > ($this->_getPass($active_id)+1))))
7945  {
7946  // a test pass was quitted because the maximum processing time was reached, but the time
7947  // will be resetted for future passes, so if there are more passes allowed, the participant may
7948  // start the test again.
7949  // This code block is only called when $allowPassIncrease is TRUE which only happens when
7950  // the test info page is opened. Otherwise this will lead to unexpected results!
7951  $this->getTestSession()->increasePass();
7952  $this->getTestSession()->setLastSequence(0);
7953  $this->getTestSession()->saveToDb();
7954  }
7955  else
7956  {
7957  $result["executable"] = false;
7958  $result["errormessage"] = $this->lng->txt("detail_max_processing_time_reached");
7959  }
7960  return $result;
7961  }
7962  }
7963  }
7964  }
7965 
7966  if ($this->hasNrOfTriesRestriction() && ($active_id > 0) && $this->isNrOfTriesReached($this->getTestSession($active_id)->getPass()))
7967  {
7968  $result["executable"] = false;
7969  $result["errormessage"] = $this->lng->txt("maximum_nr_of_tries_reached");
7970  return $result;
7971  }
7972 
7973  return $result;
7974  }
7975 
7982  function canViewResults()
7983  {
7984  $result = true;
7985  if ($this->getScoreReporting() == 4) return false;
7986  if ($this->getReportingDate())
7987  {
7988  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches))
7989  {
7990  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
7991  $now = mktime();
7992  if ($now < $epoch_time)
7993  {
7994  $result = false;
7995  }
7996  }
7997  }
7998  return $result;
7999  }
8000 
8001  function canShowTestResults($user_id)
8002  {
8003  $active_id = $this->getActiveIdOfUser($user_id);
8004  if ($active_id > 0)
8005  {
8006  $starting_time = $this->getStartingTimeOfUser($active_id);
8007  }
8008  $notimeleft = FALSE;
8009  if ($starting_time !== FALSE)
8010  {
8012  {
8013  $notimeleft = TRUE;
8014  }
8015  }
8016  $result = TRUE;
8017  if (!$this->isTestFinishedToViewResults($active_id, $this->getTestSession($active_id)->getPass()) && ($this->getScoreReporting() == REPORT_AFTER_TEST))
8018  {
8019  $result = FALSE;
8020  }
8021  if (($this->endingTimeReached()) || $notimeleft) $result = TRUE;
8022  $result = $result & $this->canViewResults();
8023  return $result;
8024  }
8025 
8026  function canEditMarks()
8027  {
8028  $total = $this->evalTotalPersons();
8029  if ($total > 0)
8030  {
8031  if ($this->getReportingDate())
8032  {
8033  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches))
8034  {
8035  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
8036  $now = mktime();
8037  if ($now < $epoch_time)
8038  {
8039  return true;
8040  }
8041  }
8042  }
8043  return false;
8044  }
8045  else
8046  {
8047  return true;
8048  }
8049  }
8050 
8058  function getStartingTimeOfUser($active_id)
8059  {
8060  global $ilDB;
8061 
8062  if ($active_id < 1) return FALSE;
8063  $pass = ($this->getResetProcessingTime()) ? $this->_getPass($active_id) : 0;
8064  $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",
8065  array('integer', 'integer'),
8066  array($active_id, $pass)
8067  );
8068  if ($result->numRows())
8069  {
8070  $row = $ilDB->fetchAssoc($result);
8071  if (preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches))
8072  {
8073  return mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
8074  }
8075  else
8076  {
8077  return mktime();
8078  }
8079  }
8080  else
8081  {
8082  return mktime();
8083  }
8084  }
8085 
8095  {
8096  if ($this->getEnableProcessingTime())
8097  {
8099  $now = mktime();
8100  if ($now > ($starting_time + $processing_time))
8101  {
8102  return TRUE;
8103  }
8104  else
8105  {
8106  return FALSE;
8107  }
8108  }
8109  else
8110  {
8111  return FALSE;
8112  }
8113  }
8114 
8115  function &getTestQuestions()
8116  {
8117  global $ilDB;
8118 
8119  $query = "
8120  SELECT questions.*,
8121  questtypes.type_tag,
8122  tstquest.sequence,
8123  origquest.obj_fi orig_obj_fi
8124 
8125  FROM qpl_questions questions
8126 
8127  INNER JOIN qpl_qst_type questtypes
8128  ON questtypes.question_type_id = questions.question_type_fi
8129 
8130  INNER JOIN tst_test_question tstquest
8131  ON tstquest.question_fi = questions.question_id
8132 
8133  LEFT JOIN qpl_questions origquest
8134  ON origquest.question_id = questions.original_id
8135 
8136  WHERE tstquest.test_fi = %s
8137 
8138  ORDER BY tstquest.sequence
8139  ";
8140 
8141  $query_result = $ilDB->queryF(
8142  $query, array('integer'), array($this->getTestId())
8143  );
8144  $removableQuestions = array();
8145  while ($row = $ilDB->fetchAssoc($query_result))
8146  {
8147  array_push($removableQuestions, $row);
8148  }
8149  return $removableQuestions;
8150  }
8151 
8159  {
8160  return ($this->shuffle_questions) ? 1 : 0;
8161  }
8162 
8169  function setShuffleQuestions($a_shuffle)
8170  {
8171  $this->shuffle_questions = ($a_shuffle) ? 1 : 0;
8172  }
8173 
8187  {
8188  return ($this->show_summary) ? $this->show_summary : 0;
8189  }
8190 
8203  function setListOfQuestionsSettings($a_value = 0)
8204  {
8205  $this->show_summary = $a_value;
8206  }
8207 
8215  {
8216  if (($this->show_summary & 1) > 0)
8217  {
8218  return TRUE;
8219  }
8220  else
8221  {
8222  return FALSE;
8223  }
8224  }
8225 
8232  function setListOfQuestions($a_value = TRUE)
8233  {
8234  if ($a_value)
8235  {
8236  $this->show_summary = 1;
8237  }
8238  else
8239  {
8240  $this->show_summary = 0;
8241  }
8242  }
8243 
8251  {
8252  if (($this->show_summary & 2) > 0)
8253  {
8254  return TRUE;
8255  }
8256  else
8257  {
8258  return FALSE;
8259  }
8260  }
8261 
8268  function setListOfQuestionsStart($a_value = TRUE)
8269  {
8270  if ($a_value && $this->getListOfQuestions())
8271  {
8272  $this->show_summary = $this->show_summary | 2;
8273  }
8274  if (!$a_value && $this->getListOfQuestions())
8275  {
8276  if ($this->getListOfQuestionsStart())
8277  {
8278  $this->show_summary = $this->show_summary ^ 2;
8279  }
8280  }
8281  }
8282 
8290  {
8291  if (($this->show_summary & 4) > 0)
8292  {
8293  return TRUE;
8294  }
8295  else
8296  {
8297  return FALSE;
8298  }
8299  }
8300 
8307  function setListOfQuestionsEnd($a_value = TRUE)
8308  {
8309  if ($a_value && $this->getListOfQuestions())
8310  {
8311  $this->show_summary = $this->show_summary | 4;
8312  }
8313  if (!$a_value && $this->getListOfQuestions())
8314  {
8315  if ($this->getListOfQuestionsEnd())
8316  {
8317  $this->show_summary = $this->show_summary ^ 4;
8318  }
8319  }
8320  }
8321 
8329  {
8330  if (($this->show_summary & 8) > 0)
8331  {
8332  return TRUE;
8333  }
8334  else
8335  {
8336  return FALSE;
8337  }
8338  }
8339 
8346  function setListOfQuestionsDescription($a_value = TRUE)
8347  {
8348  if ($a_value && $this->getListOfQuestions())
8349  {
8350  $this->show_summary = $this->show_summary | 8;
8351  }
8352  if (!$a_value && $this->getListOfQuestions())
8353  {
8354  if ($this->getListOfQuestionsDescription())
8355  {
8356  $this->show_summary = $this->show_summary ^ 8;
8357  }
8358  }
8359  }
8360 
8368  {
8369  return ($this->results_presentation) ? $this->results_presentation : 0;
8370  }
8371 
8379  {
8380  if (($this->results_presentation & 1) > 0)
8381  {
8382  return TRUE;
8383  }
8384  else
8385  {
8386  return FALSE;
8387  }
8388  }
8389 
8397  {
8398  if (($this->results_presentation & 2) > 0)
8399  {
8400  return TRUE;
8401  }
8402  else
8403  {
8404  return FALSE;
8405  }
8406  }
8407 
8415  {
8416  if (($this->results_presentation & 4) > 0)
8417  {
8418  return TRUE;
8419  }
8420  else
8421  {
8422  return FALSE;
8423  }
8424  }
8425 
8433  {
8434  if (($this->results_presentation & 8) > 0)
8435  {
8436  return TRUE;
8437  }
8438  else
8439  {
8440  return FALSE;
8441  }
8442  }
8443 
8451  {
8452  if (($this->results_presentation & 16) > 0)
8453  {
8454  return TRUE;
8455  }
8456  else
8457  {
8458  return FALSE;
8459  }
8460  }
8461 
8469  {
8470  if (($this->results_presentation & 32) > 0)
8471  {
8472  return TRUE;
8473  }
8474  else
8475  {
8476  return FALSE;
8477  }
8478  }
8479 
8485  {
8486  if (($this->results_presentation & 64) > 0)
8487  {
8488  return TRUE;
8489  }
8490  else
8491  {
8492  return FALSE;
8493  }
8494  }
8495 
8502  function setResultsPresentation($a_results_presentation = 3)
8503  {
8504  $this->results_presentation = $a_results_presentation;
8505  }
8506 
8515  function setShowPassDetails($a_details = 1)
8516  {
8517  if ($a_details)
8518  {
8519  $this->results_presentation = $this->results_presentation | 1;
8520  }
8521  else
8522  {
8523  if ($this->getShowPassDetails())
8524  {
8525  $this->results_presentation = $this->results_presentation ^ 1;
8526  }
8527  }
8528  }
8529 
8536  function setShowSolutionDetails($a_details = 1)
8537  {
8538  if ($a_details)
8539  {
8540  $this->results_presentation = $this->results_presentation | 2;
8541  }
8542  else
8543  {
8544  if ($this->getShowSolutionDetails())
8545  {
8546  $this->results_presentation = $this->results_presentation ^ 2;
8547  }
8548  }
8549  }
8550 
8557  function canShowSolutionPrintview($user_id = NULL)
8558  {
8559  return $this->getShowSolutionPrintview();
8560  }
8561 
8568  function setShowSolutionPrintview($a_printview = 1)
8569  {
8570  if ($a_printview)
8571  {
8572  $this->results_presentation = $this->results_presentation | 4;
8573  }
8574  else
8575  {
8576  if ($this->getShowSolutionPrintview())
8577  {
8578  $this->results_presentation = $this->results_presentation ^ 4;
8579  }
8580  }
8581  }
8582 
8589  function setShowSolutionFeedback($a_feedback = TRUE)
8590  {
8591  if ($a_feedback)
8592  {
8593  $this->results_presentation = $this->results_presentation | 8;
8594  }
8595  else
8596  {
8597  if ($this->getShowSolutionFeedback())
8598  {
8599  $this->results_presentation = $this->results_presentation ^ 8;
8600  }
8601  }
8602  }
8603 
8610  function setShowSolutionAnswersOnly($a_full = TRUE)
8611  {
8612  if ($a_full)
8613  {
8614  $this->results_presentation = $this->results_presentation | 16;
8615  }
8616  else
8617  {
8618  if ($this->getShowSolutionAnswersOnly())
8619  {
8620  $this->results_presentation = $this->results_presentation ^ 16;
8621  }
8622  }
8623  }
8624 
8631  function setShowSolutionSignature($a_signature = FALSE)
8632  {
8633  if ($a_signature)
8634  {
8635  $this->results_presentation = $this->results_presentation | 32;
8636  }
8637  else
8638  {
8639  if ($this->getShowSolutionSignature())
8640  {
8641  $this->results_presentation = $this->results_presentation ^ 32;
8642  }
8643  }
8644  }
8645 
8652  function setShowSolutionSuggested($a_solution = FALSE)
8653  {
8654  if ($a_solution)
8655  {
8656  $this->results_presentation = $this->results_presentation | 64;
8657  }
8658  else
8659  {
8660  if ($this->getShowSolutionSuggested())
8661  {
8662  $this->results_presentation = $this->results_presentation ^ 64;
8663  }
8664  }
8665  }
8666 
8673  {
8674  // create a 5 character code
8675  $codestring = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
8676  mt_srand();
8677  $code = "";
8678  for ($i = 1; $i <=5; $i++)
8679  {
8680  $index = mt_rand(0, strlen($codestring)-1);
8681  $code .= substr($codestring, $index, 1);
8682  }
8683  // verify it against the database
8684  while ($this->isAccessCodeUsed($code))
8685  {
8686  $code = $this->createNewAccessCode();
8687  }
8688  return $code;
8689  }
8690 
8691  function isAccessCodeUsed($code)
8692  {
8693  global $ilDB;
8694 
8695  $result = $ilDB->queryF("SELECT anonymous_id FROM tst_active WHERE test_fi = %s AND anonymous_id = %s",
8696  array('integer', 'text'),
8697  array($this->getTestId(), $code)
8698  );
8699  return ($result->numRows() > 0) ? true : false;
8700  }
8701 
8702  public static function _getUserIdFromActiveId($active_id)
8703  {
8704  global $ilDB;
8705  $result = $ilDB->queryF("SELECT user_fi FROM tst_active WHERE active_id = %s",
8706  array('integer'),
8707  array($active_id)
8708  );
8709  if ($result->numRows())
8710  {
8711  $row = $ilDB->fetchAssoc($result);
8712  return $row["user_fi"];
8713  }
8714  else
8715  {
8716  return -1;
8717  }
8718  }
8719 
8721  {
8722  $id = $this->getTestId();
8723  if (!is_array($_SESSION["tst_access_code"]))
8724  {
8725  return "";
8726  }
8727  else
8728  {
8729  return $_SESSION["tst_access_code"]["$id"];
8730  }
8731  }
8732 
8733  function setAccessCodeSession($access_code)
8734  {
8735  $id = $this->getTestId();
8736  if (!is_array($_SESSION["tst_access_code"]))
8737  {
8738  $_SESSION["tst_access_code"] = array();
8739  }
8740  $_SESSION["tst_access_code"]["$id"] = $access_code;
8741  }
8742 
8744  {
8745  $id = $this->getTestId();
8746  unset($_SESSION["tst_access_code"]["$id"]);
8747  }
8748 
8749  function getAllowedUsers()
8750  {
8751  return ($this->allowedUsers) ? $this->allowedUsers : 0;
8752  }
8753 
8754  function setAllowedUsers($a_allowed_users)
8755  {
8756  $this->allowedUsers = $a_allowed_users;
8757  }
8758 
8760  {
8761  return ($this->allowedUsersTimeGap) ? $this->allowedUsersTimeGap : 0;
8762  }
8763 
8764  function setAllowedUsersTimeGap($a_allowed_users_time_gap)
8765  {
8766  $this->allowedUsersTimeGap = $a_allowed_users_time_gap;
8767  }
8768 
8770  {
8771  global $ilDB;
8772 
8773  $nr_of_users = $this->getAllowedUsers();
8774  $time_gap = ($this->getAllowedUsersTimeGap()) ? $this->getAllowedUsersTimeGap() : 60;
8775  if (($nr_of_users > 0) && ($time_gap > 0))
8776  {
8777  $now = mktime();
8778  $time_border = $now - $time_gap;
8779  $str_time_border = strftime("%Y%m%d%H%M%S", $time_border);
8780  $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",
8781  array('integer', 'integer'),
8782  array($time_border, $this->getTestId())
8783  );
8784  if ($result->numRows() >= $nr_of_users)
8785  {
8786  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
8788  {
8789  $this->logAction($this->lng->txtlng("assessment", "log_could_not_enter_test_due_to_simultaneous_users", ilObjAssessmentFolder::_getLogLanguage()));
8790  }
8791  return FALSE;
8792  }
8793  else
8794  {
8795  return TRUE;
8796  }
8797  }
8798  return TRUE;
8799  }
8800 
8801  function _getLastAccess($active_id)
8802  {
8803  global $ilDB;
8804 
8805  $result = $ilDB->queryF("SELECT finished FROM tst_times WHERE active_fi = %s ORDER BY finished DESC",
8806  array('integer'),
8807  array($active_id)
8808  );
8809  if ($result->numRows())
8810  {
8811  $row = $ilDB->fetchAssoc($result);
8812  return $row["finished"];
8813  }
8814  return "";
8815  }
8816 
8824  function isHTML($a_text)
8825  {
8826  if (preg_match("/<[^>]*?>/", $a_text))
8827  {
8828  return TRUE;
8829  }
8830  else
8831  {
8832  return FALSE;
8833  }
8834  }
8835 
8843  function QTIMaterialToString($a_material)
8844  {
8845  $result = "";
8846  for ($i = 0; $i < $a_material->getMaterialCount(); $i++)
8847  {
8848  $material = $a_material->getMaterial($i);
8849  if (strcmp($material["type"], "mattext") == 0)
8850  {
8851  $result .= $material["material"]->getContent();
8852  }
8853  if (strcmp($material["type"], "matimage") == 0)
8854  {
8855  $matimage = $material["material"];
8856  if (preg_match("/(il_([0-9]+)_mob_([0-9]+))/", $matimage->getLabel(), $matches))
8857  {
8858  // import an mediaobject which was inserted using tiny mce
8859  if (!is_array($_SESSION["import_mob_xhtml"])) $_SESSION["import_mob_xhtml"] = array();
8860  array_push($_SESSION["import_mob_xhtml"], array("mob" => $matimage->getLabel(), "uri" => $matimage->getUri()));
8861  }
8862  }
8863  }
8864  global $ilLog;
8865  $ilLog->write(print_r($_SESSION["import_mob_xhtml"], true));
8866  return $result;
8867  }
8868 
8877  function addQTIMaterial(&$a_xml_writer, $a_material)
8878  {
8879  include_once "./Services/RTE/classes/class.ilRTE.php";
8880  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
8881 
8882  $a_xml_writer->xmlStartTag("material");
8883  $attrs = array(
8884  "texttype" => "text/plain"
8885  );
8886  if ($this->isHTML($a_material))
8887  {
8888  $attrs["texttype"] = "text/xhtml";
8889  }
8890  $a_xml_writer->xmlElement("mattext", $attrs, ilRTE::_replaceMediaObjectImageSrc($a_material, 0));
8891 
8892  $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId());
8893  foreach ($mobs as $mob)
8894  {
8895  $moblabel = "il_" . IL_INST_ID . "_mob_" . $mob;
8896  if (strpos($a_material, "mm_$mob") !== FALSE)
8897  {
8898  if (ilObjMediaObject::_exists($mob))
8899  {
8900  $mob_obj =& new ilObjMediaObject($mob);
8901  $imgattrs = array(
8902  "label" => $moblabel,
8903  "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle()
8904  );
8905  }
8906  $a_xml_writer->xmlElement("matimage", $imgattrs, NULL);
8907  }
8908  }
8909  $a_xml_writer->xmlEndTag("material");
8910  }
8911 
8918  function prepareTextareaOutput($txt_output, $prepare_for_latex_output = FALSE)
8919  {
8920  include_once "./Services/Utilities/classes/class.ilUtil.php";
8921  return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output);
8922  }
8923 
8930  function saveCertificateVisibility($a_value)
8931  {
8932  global $ilDB;
8933 
8934  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET certificate_visibility = %s, tstamp = %s WHERE test_id = %s",
8935  array('text', 'integer', 'integer'),
8936  array($a_value, time(), $this->getTestId())
8937  );
8938  }
8939 
8947  {
8948  return (strlen($this->certificate_visibility)) ? $this->certificate_visibility : 0;
8949  }
8950 
8957  function setCertificateVisibility($a_value)
8958  {
8959  $this->certificate_visibility = $a_value;
8960  }
8961 
8968  function getAnonymity()
8969  {
8970  return ($this->anonymity) ? 1 : 0;
8971  }
8972 
8979  function setAnonymity($a_value = 0)
8980  {
8981  switch ($a_value)
8982  {
8983  case 1:
8984  $this->anonymity = 1;
8985  break;
8986  default:
8987  $this->anonymity = 0;
8988  break;
8989  }
8990  }
8991 
8998  function getShowCancel()
8999  {
9000  return ($this->show_cancel) ? 1 : 0;
9001  }
9002 
9009  function setShowCancel($a_value = 1)
9010  {
9011  switch ($a_value)
9012  {
9013  case 1:
9014  $this->show_cancel = 1;
9015  break;
9016  default:
9017  $this->show_cancel = 0;
9018  break;
9019  }
9020  }
9021 
9028  function getShowMarker()
9029  {
9030  return ($this->show_marker) ? 1 : 0;
9031  }
9032 
9039  function setShowMarker($a_value = 1)
9040  {
9041  switch ($a_value)
9042  {
9043  case 1:
9044  $this->show_marker = 1;
9045  break;
9046  default:
9047  $this->show_marker = 0;
9048  break;
9049  }
9050  }
9051 
9059  {
9060  return ($this->fixed_participants) ? 1 : 0;
9061  }
9062 
9069  function setFixedParticipants($a_value = 1)
9070  {
9071  switch ($a_value)
9072  {
9073  case 1:
9074  $this->fixed_participants = 1;
9075  break;
9076  default:
9077  $this->fixed_participants = 0;
9078  break;
9079  }
9080  }
9081 
9089  function _lookupAnonymity($a_obj_id)
9090  {
9091  global $ilDB;
9092 
9093  $result = $ilDB->queryF("SELECT anonymity FROM tst_tests WHERE obj_fi = %s",
9094  array('integer'),
9095  array($a_obj_id)
9096  );
9097  while($row = $ilDB->fetchAssoc($result))
9098  {
9099  return $row['anonymity'];
9100  }
9101  return 0;
9102  }
9103 
9111  function _lookupRandomTestFromActiveId($active_id)
9112  {
9113  global $ilDB;
9114 
9115  $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",
9116  array('integer'),
9117  array($active_id)
9118  );
9119  while($row = $ilDB->fetchAssoc($result))
9120  {
9121  return $row['random_test'];
9122  }
9123  return 0;
9124  }
9125 
9134  function userLookupFullName($user_id, $overwrite_anonymity = FALSE, $sorted_order = FALSE, $suffix = "")
9135  {
9136  if ($this->getAnonymity() && !$overwrite_anonymity)
9137  {
9138  return $this->lng->txt("unknown") . $suffix;
9139  }
9140  else
9141  {
9142  include_once './Services/User/classes/class.ilObjUser.php';
9143  $uname = ilObjUser::_lookupName($user_id);
9144  if (strlen($uname["firstname"].$uname["lastname"]) == 0) $uname["firstname"] = $this->lng->txt("deleted_user");
9145  if ($sorted_order)
9146  {
9147  return trim($uname["lastname"] . ", " . $uname["firstname"]) . $suffix;
9148  }
9149  else
9150  {
9151  return trim($uname["firstname"] . " " . $uname["lastname"]) . $suffix;
9152  }
9153  }
9154  }
9155 
9163  function getStartTestLabel($active_id)
9164  {
9165  if ($this->getNrOfTries() == 1)
9166  {
9167  return $this->lng->txt("tst_start_test");
9168  }
9169  $active_pass = $this->_getPass($active_id);
9170  $res = $this->getNrOfResultsForPass($active_id, $active_pass);
9171  if ($res == 0)
9172  {
9173  if ($active_pass == 0)
9174  {
9175  return $this->lng->txt("tst_start_test");
9176  }
9177  else
9178  {
9179  return $this->lng->txt("tst_start_new_test_pass");
9180  }
9181  }
9182  else
9183  {
9184  return $this->lng->txt("tst_resume_test");
9185  }
9186  }
9187 
9196  function &getAvailableDefaults($sortby = "name", $sortorder = "asc")
9197  {
9198  global $ilDB;
9199  global $ilUser;
9200 
9201  $result = $ilDB->queryF("SELECT * FROM tst_test_defaults WHERE user_fi = %s ORDER BY $sortby $sortorder",
9202  array('integer'),
9203  array($ilUser->getId())
9204  );
9205  $defaults = array();
9206  while ($row = $ilDB->fetchAssoc($result))
9207  {
9208  $defaults[$row["test_defaults_id"]] = $row;
9209  }
9210  return $defaults;
9211  }
9212 
9220  function &getTestDefaults($test_defaults_id)
9221  {
9222  global $ilDB;
9223 
9224  $result = $ilDB->queryF("SELECT * FROM tst_test_defaults WHERE test_defaults_id = %s",
9225  array('integer'),
9226  array($test_defaults_id)
9227  );
9228  if ($result->numRows() == 1)
9229  {
9230  $row = $ilDB->fetchAssoc($result);
9231  return $row;
9232  }
9233  else
9234  {
9235  return NULL;
9236  }
9237  }
9238 
9245  function deleteDefaults($test_default_id)
9246  {
9247  global $ilDB;
9248  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_defaults WHERE test_defaults_id = %s",
9249  array('integer'),
9250  array($test_default_id)
9251  );
9252  }
9253 
9260  function addDefaults($a_name)
9261  {
9262  global $ilDB;
9263  global $ilUser;
9264  $testsettings = array(
9265  "TitleOutput" => $this->getTitleOutput(),
9266  "PassScoring" => $this->getPassScoring(),
9267  "Introduction" => $this->getIntroduction(),
9268  "FinalStatement" => $this->getFinalStatement(),
9269  "ShowInfo" => $this->getShowInfo(),
9270  "ForceJS" => $this->getForceJS(),
9271  "CustomStyle" => $this->getCustomStyle(),
9272  "ShowFinalStatement" => $this->getShowFinalStatement(),
9273  "SequenceSettings" => $this->getSequenceSettings(),
9274  "ScoreReporting" => $this->getScoreReporting(),
9275  "InstantFeedbackSolution" => $this->getInstantFeedbackSolution(),
9276  "AnswerFeedback" => $this->getAnswerFeedback(),
9277  "AnswerFeedbackPoints" => $this->getAnswerFeedbackPoints(),
9278  "ResultsPresentation" => $this->getResultsPresentation(),
9279  "Anonymity" => $this->getAnonymity(),
9280  "ShowCancel" => $this->getShowCancel(),
9281  "ShowMarker" => $this->getShowMarker(),
9282  "ReportingDate" => $this->getReportingDate(),
9283  "NrOfTries" => $this->getNrOfTries(),
9284  "Shuffle" => $this->getShuffleQuestions(),
9285  "Kiosk" => $this->getKiosk(),
9286  "UsePreviousAnswers" => $this->getUsePreviousAnswers(),
9287  "ProcessingTime" => $this->getProcessingTime(),
9288  "EnableProcessingTime" => $this->getEnableProcessingTime(),
9289  "ResetProcessingTime" => $this->getResetProcessingTime(),
9290  "StartingTime" => $this->getStartingTime(),
9291  "EndingTime" => $this->getEndingTime(),
9292  "ECTSOutput" => $this->getECTSOutput(),
9293  "ECTSFX" => $this->getECTSFX(),
9294  "ECTSGrades" => $this->getECTSGrades(),
9295  "isRandomTest" => $this->isRandomTest(),
9296  "RandomQuestionCount" => $this->getRandomQuestionCount(),
9297  "CountSystem" => $this->getCountSystem(),
9298  "MCScoring" => $this->getMCScoring(),
9299  "mailnotification" => $this->getMailNotification(),
9300  "mailnottype" => $this->getMailNotificationType(),
9301  "exportsettings" => $this->getExportSettings(),
9302  "ListOfQuestionsSettings" => $this->getListOfQuestionsSettings()
9303  );
9304  $next_id = $ilDB->nextId('tst_test_defaults');
9305  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_defaults (test_defaults_id, name, user_fi, defaults, marks, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
9306  array('integer', 'text', 'integer', 'text', 'text', 'integer'),
9307  array($next_id, $a_name, $ilUser->getId(), serialize($testsettings), serialize($this->mark_schema), time())
9308  );
9309  }
9310 
9318  function applyDefaults($test_defaults_id)
9319  {
9320  $total = $this->evalTotalPersons();
9321  $result = FALSE;
9322  if (($this->getQuestionCount() == 0) && ($total == 0))
9323  {
9324  // only apply if there are no questions added and not user datasets exist
9325  $defaults =& $this->getTestDefaults($test_defaults_id);
9326  $testsettings = unserialize($defaults["defaults"]);
9327  include_once "./Modules/Test/classes/class.assMarkSchema.php";
9328  $this->mark_schema = unserialize($defaults["marks"]);
9329  $this->setTitleOutput($testsettings["TitleOutput"]);
9330  $this->setPassScoring($testsettings["PassScoring"]);
9331  $this->setIntroduction($testsettings["Introduction"]);
9332  $this->setFinalStatement($testsettings["FinalStatement"]);
9333  $this->setShowInfo($testsettings["ShowInfo"]);
9334  $this->setForceJS($testsettings["ForceJS"]);
9335  $this->setCustomStyle($testsettings["CustomStyle"]);
9336  $this->setShowFinalStatement($testsettings["ShowFinalStatement"]);
9337  $this->setSequenceSettings($testsettings["SequenceSettings"]);
9338  $this->setScoreReporting($testsettings["ScoreReporting"]);
9339  $this->setInstantFeedbackSolution($testsettings["InstantFeedbackSolution"]);
9340  $this->setAnswerFeedback($testsettings["AnswerFeedback"]);
9341  $this->setAnswerFeedbackPoints($testsettings["AnswerFeedbackPoints"]);
9342  $this->setResultsPresentation($testsettings["ResultsPresentation"]);
9343  $this->setAnonymity($testsettings["Anonymity"]);
9344  $this->setShowCancel($testsettings["ShowCancel"]);
9345  $this->setShuffleQuestions($testsettings["Shuffle"]);
9346  $this->setShowMarker($testsettings["ShowMarker"]);
9347  $this->setReportingDate($testsettings["ReportingDate"]);
9348  $this->setNrOfTries($testsettings["NrOfTries"]);
9349  $this->setUsePreviousAnswers($testsettings["UsePreviousAnswers"]);
9350  $this->setProcessingTime($testsettings["ProcessingTime"]);
9351  $this->setResetProcessingTime($testsettings["ResetProcessingTime"]);
9352  $this->setEnableProcessingTime($testsettings["EnableProcessingTime"]);
9353  $this->setStartingTime($testsettings["StartingTime"]);
9354  $this->setKiosk($testsettings["Kiosk"]);
9355  $this->setEndingTime($testsettings["EndingTime"]);
9356  $this->setECTSOutput($testsettings["ECTSOutput"]);
9357  $this->setECTSFX($testsettings["ECTSFX"]);
9358  $this->setECTSGrades($testsettings["ECTSGrades"]);
9359  $this->setRandomTest($testsettings["isRandomTest"]);
9360  $this->setRandomQuestionCount($testsettings["RandomQuestionCount"]);
9361  $this->setCountSystem($testsettings["CountSystem"]);
9362  $this->setMCScoring($testsettings["MCScoring"]);
9363  $this->setMailNotification($testsettings["mailnotification"]);
9364  $this->setMailNotificationType($testsettings["mailnottype"]);
9365  $this->setExportSettings($testsettings['exportsettings']);
9366  $this->setListOfQuestionsSettings($testsettings["ListOfQuestionsSettings"]);
9367  $this->saveToDb();
9368  $result = TRUE;
9369  }
9370  return $result;
9371  }
9372 
9380  function processPrintoutput2FO($print_output)
9381  {
9382  if (extension_loaded("tidy"))
9383  {
9384  $config = array(
9385  "indent" => false,
9386  "output-xml" => true,
9387  "numeric-entities" => true
9388  );
9389  $tidy = new tidy();
9390  $tidy->parseString($print_output, $config, 'utf8');
9391  $tidy->cleanRepair();
9392  $print_output = tidy_get_output($tidy);
9393  $print_output = preg_replace("/^.*?(<html)/", "\\1", $print_output);
9394  }
9395  else
9396  {
9397  $print_output = str_replace("&nbsp;", "&#160;", $print_output);
9398  $print_output = str_replace("&otimes;", "X", $print_output);
9399  }
9400  $xsl = file_get_contents("./Modules/Test/xml/question2fo.xsl");
9401  $args = array( '/_xml' => $print_output, '/_xsl' => $xsl );
9402  $xh = xslt_create();
9403  $params = array();
9404  $output = xslt_process($xh, "arg:/_xml", "arg:/_xsl", NULL, $args, $params);
9405  xslt_error($xh);
9406  xslt_free($xh);
9407  return $output;
9408  }
9409 
9416  public function deliverPDFfromHTML($content, $title = NULL)
9417  {
9418  $content = preg_replace("/href=\".*?\"/", "", $content);
9419  $printbody = new ilTemplate("tpl.il_as_tst_print_body.html", TRUE, TRUE, "Modules/Test");
9420  $printbody->setVariable("TITLE", ilUtil::prepareFormOutput($this->getTitle()));
9421  $printbody->setVariable("ADM_CONTENT", $content);
9422  $printbody->setCurrentBlock("css_file");
9423  $printbody->setVariable("CSS_FILE", $this->getTestStyleLocation("filesystem"));
9424  $printbody->parseCurrentBlock();
9425  $printbody->setCurrentBlock("css_file");
9426  $printbody->setVariable("CSS_FILE", ilUtil::getStyleSheetLocation("filesystem", "delos.css"));
9427  $printbody->parseCurrentBlock();
9428  $printoutput = $printbody->get();
9429  $html = str_replace("href=\"./", "href=\"" . ILIAS_HTTP_PATH . "/", $printoutput);
9430  $html = preg_replace("/<div id=\"dontprint\">.*?<\\/div>/ims", "", $html);
9431  if (extension_loaded("tidy"))
9432  {
9433  $config = array(
9434  "indent" => false,
9435  "output-xml" => true,
9436  "numeric-entities" => true
9437  );
9438  $tidy = new tidy();
9439  $tidy->parseString($html, $config, 'utf8');
9440  $tidy->cleanRepair();
9441  $html = tidy_get_output($tidy);
9442  $html = preg_replace("/^.*?(<html)/", "\\1", $html);
9443  }
9444  else
9445  {
9446  $html = str_replace("&nbsp;", "&#160;", $html);
9447  $html = str_replace("&otimes;", "X", $html);
9448  }
9449  $html = preg_replace("/src=\".\\//ims", "src=\"" . ILIAS_HTTP_PATH . "/", $html);
9450  $this->deliverPDFfromFO($this->processPrintoutput2FO($html), $title);
9451  }
9452 
9459  public function deliverPDFfromFO($fo, $title = null)
9460  {
9461  global $ilLog;
9462 
9463  include_once "./Services/Utilities/classes/class.ilUtil.php";
9464  $fo_file = ilUtil::ilTempnam() . ".fo";
9465  $fp = fopen($fo_file, "w"); fwrite($fp, $fo); fclose($fp);
9466 
9467  include_once './Services/WebServices/RPC/classes/class.ilRpcClientFactory.php';
9468  try
9469  {
9470  $pdf_base64 = ilRpcClientFactory::factory('RPCTransformationHandler')->ilFO2PDF($fo);
9471  $filename = (strlen($title)) ? $title : $this->getTitle();
9472  ilUtil::deliverData($pdf_base64->scalar, ilUtil::getASCIIFilename($filename) . ".pdf", "application/pdf", false, true);
9473  return true;
9474  }
9475  catch(XML_RPC2_FaultException $e)
9476  {
9477  $ilLog->write(__METHOD__.': '.$e->getMessage());
9478  return false;
9479  }
9480  catch(Exception $e)
9481  {
9482  $ilLog->write(__METHOD__.': '.$e->getMessage());
9483  return false;
9484  }
9485 
9486  /*
9487  include_once "./Services/Transformation/classes/class.ilFO2PDF.php";
9488  $fo2pdf = new ilFO2PDF();
9489  $fo2pdf->setFOString($fo);
9490  $result = $fo2pdf->send();
9491  $filename = (strlen($title)) ? $title : $this->getTitle();
9492  ilUtil::deliverData($result, ilUtil::getASCIIFilename($filename) . ".pdf", "application/pdf", false, true);
9493  */
9494  }
9495 
9505  static function getManualFeedback($active_id, $question_id, $pass)
9506  {
9507  global $ilDB;
9508  $feedback = "";
9509  $result = $ilDB->queryF("SELECT feedback FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
9510  array('integer', 'integer', 'integer'),
9511  array($active_id, $question_id, $pass)
9512  );
9513  if ($result->numRows())
9514  {
9515  $row = $ilDB->fetchAssoc($result);
9516  include_once("./Services/RTE/classes/class.ilRTE.php");
9517  $feedback = ilRTE::_replaceMediaObjectImageSrc($row["feedback"], 1);
9518  }
9519  return $feedback;
9520  }
9521 
9532  function saveManualFeedback($active_id, $question_id, $pass, $feedback)
9533  {
9534  global $ilDB;
9535 
9536  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
9537  array('integer', 'integer', 'integer'),
9538  array($active_id, $question_id, $pass)
9539  );
9540 
9541  if (strlen($feedback))
9542  {
9543  $next_id = $ilDB->nextId('tst_manual_fb');
9545  $result = $ilDB->insert('tst_manual_fb', array(
9546  'manual_feedback_id' => array( 'integer', $next_id ),
9547  'active_fi' => array( 'integer', $active_id ),
9548  'question_fi' => array( 'integer', $question_id ),
9549  'pass' => array( 'integer', $pass),
9550  'feedback' => array( 'clob', ilRTE::_replaceMediaObjectImageSrc( $feedback, 0) ),
9551  'tstamp' => array( 'integer', time() ),
9552  )
9553  );
9554 
9555  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
9557  {
9558  global $lng, $ilUser;
9559  include_once "./Modules/Test/classes/class.ilObjTestAccess.php";
9560  $username = ilObjTestAccess::_getParticipantData($active_id);
9561  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
9562  $this->logAction(sprintf($lng->txtlng("assessment", "log_manual_feedback", ilObjAssessmentFolder::_getLogLanguage()), $ilUser->getFullname() . " (" . $ilUser->getLogin() . ")", $username, assQuestion::_getQuestionTitle($question_id), $feedback));
9563  }
9564  }
9565  if (PEAR::isError($result))
9566  {
9567  global $ilias;
9568  $ilias->raiseError($result->getMessage());
9569  }
9570  else
9571  {
9572  return TRUE;
9573  }
9574  }
9575 
9584  {
9585  global $ilUser;
9586  if (strcmp($_GET["tst_javascript"], "0") == 0) return FALSE;
9587  if ($this->getForceJS()) return TRUE;
9588  $assessmentSetting = new ilSetting("assessment");
9589  return ($ilUser->getPref("tst_javascript") === FALSE) ? $assessmentSetting->get("use_javascript") : $ilUser->getPref("tst_javascript");
9590  }
9591 
9598  function &createTestSession()
9599  {
9600  global $ilUser;
9601 
9602  include_once "./Modules/Test/classes/class.ilTestSession.php";
9603  $testSession = FALSE;
9604  $testSession = new ilTestSession();
9605  $testSession->setRefId($this->getRefId());
9606  $testSession->setTestId($this->getTestId());
9607  $testSession->setUserId($ilUser->getId());
9608  $testSession->setAnonymousId($_SESSION["tst_access_code"][$this->getTestId()]);
9609  $testSession->saveToDb();
9610  $this->testSession =& $testSession;
9611  return $this->testSession;
9612  }
9613 
9619  public function setTestId($a_id)
9620  {
9621  $this->test_id = $a_id;
9622  }
9623 
9631  function &setTestSession($active_id = "")
9632  {
9633  if (is_object($this->testSession) && ($this->testSession->getActiveId() > 0)) return $this->testSession;
9634 
9635  global $ilUser;
9636 
9637  include_once "./Modules/Test/classes/class.ilTestSession.php";
9638  $testSession = FALSE;
9639  if ($active_id > 0)
9640  {
9641  $testSession = new ilTestSession($active_id);
9642  $testSession->setRefId($this->getRefId());
9643  }
9644  else
9645  {
9646  $testSession = new ilTestSession();
9647  $testSession->setRefId($this->getRefId());
9648  $testSession->loadTestSession($this->getTestId(), $ilUser->getId(), $_SESSION["tst_access_code"][$this->getTestId()]);
9649  }
9650  $this->testSession =& $testSession;
9651  return $this->testSession;
9652  }
9653 
9660  function &getTestSession($active_id = "")
9661  {
9662  if (is_object($this->testSession) && ($this->testSession->getActiveId() > 0)) return $this->testSession;
9663  return $this->setTestSession($active_id);
9664  }
9665 
9666  function &createTestSequence($active_id, $pass, $shuffle)
9667  {
9668  include_once "./Modules/Test/classes/class.ilTestSequence.php";
9669  $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
9670  if (!$this->testSequence->hasSequence())
9671  {
9672  $this->testSequence->createNewSequence($this->getQuestionCount(), $shuffle);
9673  $this->testSequence->saveToDb();
9674  }
9675  }
9676 
9677  function &getTestSequence($active_id = "", $pass = "")
9678  {
9679  if (is_object($this->testSequence) && ($this->testSequence->getActiveId() > 0)) return $this->testSequence;
9680 
9681  include_once "./Modules/Test/classes/class.ilTestSequence.php";
9682  if (($active_id > 0) && (strlen($pass)))
9683  {
9684  $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
9685  }
9686  else
9687  {
9688  $this->testSequence = new ilTestSequence($this->getTestSession()->getActiveId(), $this->getTestSession()->getPass(), $this->isRandomTest());
9689  }
9690  return $this->testSequence;
9691  }
9692 
9694  {
9695  if ($this->getTestSession()->getActiveId() > 0)
9696  {
9697  $result = $this->getTestResult($this->getTestSession()->getActiveId(), $this->getTestSession()->getPass(), TRUE);
9698  foreach ($result as $sequence => $question)
9699  {
9700  if (is_numeric($sequence))
9701  {
9702  if ($question["reached"] == $question["max"])
9703  {
9704  $this->getTestSequence()->hideQuestion($question["qid"]);
9705  }
9706  }
9707  }
9708  $this->getTestSequence()->saveToDb();
9709  }
9710  }
9711 
9720  function getDetailedTestResults($participants)
9721  {
9722  $results = array();
9723  if (count($participants))
9724  {
9725  foreach ($participants as $active_id => $user_rec)
9726  {
9727  $row = array();
9728  $reached_points = 0;
9729  $max_points = 0;
9730  foreach ($this->questions as $value)
9731  {
9732  $question =& ilObjTest::_instanciateQuestion($value);
9733  if (is_object($question))
9734  {
9735  $max_points += $question->getMaximumPoints();
9736  $reached_points += $question->getReachedPoints($active_id);
9737  if ($max_points > 0)
9738  {
9739  $percentvalue = $reached_points / $max_points;
9740  if ($percentvalue < 0) $percentvalue = 0.0;
9741  }
9742  else
9743  {
9744  $percentvalue = 0;
9745  }
9746  if ($this->getAnonymity())
9747  {
9748  $user_rec['firstname'] = "";
9749  $user_rec['lastname'] = $this->lng->txt("unknown");
9750  }
9751  $row = array(
9752  "user_id"=>$user_rec['usr_id'],
9753  "matriculation" => $user_rec['matriculation'],
9754  "lastname" => $user_rec['lastname'],
9755  "firstname" => $user_rec['firstname'],
9756  "login"=>$user_rec['login'],
9757  "question_id" => $question->getId(),
9758  "question_title" => $question->getTitle(),
9759  "reached_points" => $reached_points,
9760  "max_points" => $max_points
9761  );
9762  $results[] = $row;
9763  }
9764  }
9765  }
9766  }
9767  return $results;
9768  }
9769 
9774  {
9775  global $ilDB;
9776 
9777  $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",
9778  array('integer'),
9779  array($a_q_id)
9780  );
9781  $rec = $ilDB->fetchAssoc($result);
9782  return $rec["obj_id"];
9783  }
9784 
9791  function isPluginActive($a_pname)
9792  {
9793  global $ilPluginAdmin;
9794  if ($ilPluginAdmin->isActive(IL_COMP_MODULE, "TestQuestionPool", "qst", $a_pname))
9795  {
9796  return TRUE;
9797  }
9798  else
9799  {
9800  return FALSE;
9801  }
9802  }
9803 
9804  protected function getPassed($active_id)
9805  {
9806  global $ilDB;
9807 
9808  $result = $ilDB->queryF("SELECT passed FROM tst_result_cache WHERE active_fi = %s",
9809  array('integer'),
9810  array($active_id)
9811  );
9812  if ($result->numRows())
9813  {
9814  $row = $ilDB->fetchAssoc($result);
9815  return $row['passed'];
9816  }
9817  else
9818  {
9819  $counted_pass = ilObjTest::_getResultPass($active_id);
9820  $result_array =& $this->getTestResult($active_id, $counted_pass);
9821  return $result_array["test"]["passed"];
9822  }
9823  }
9824 
9830  function canShowCertificate($user_id, $active_id)
9831  {
9832  if ($this->canShowTestResults($user_id))
9833  {
9834  include_once "./Services/Certificate/classes/class.ilCertificate.php";
9835  include_once "./Modules/Test/classes/class.ilTestCertificateAdapter.php";
9836  $cert = new ilCertificate(new ilTestCertificateAdapter($this));
9837  if ($cert->isComplete())
9838  {
9839  $vis = $this->getCertificateVisibility();
9840  $showcert = FALSE;
9841  switch ($vis)
9842  {
9843  case 0:
9844  $showcert = TRUE;
9845  break;
9846  case 1:
9847  if ($this->getPassed($active_id))
9848  {
9849  $showcert = TRUE;
9850  }
9851  break;
9852  case 2:
9853  $showcert = FALSE;
9854  break;
9855  }
9856  if ($showcert)
9857  {
9858  return TRUE;
9859  }
9860  else
9861  {
9862  return FALSE;
9863  }
9864  }
9865  else
9866  {
9867  return FALSE;
9868  }
9869  }
9870  else
9871  {
9872  return FALSE;
9873  }
9874  }
9875 
9882  {
9883  global $ilDB;
9884  $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.question_id = %s",
9885  array('integer', 'integer'),
9886  array($test_id, $question_id)
9887  );
9888  $foundusers = array();
9889  while ($row = $ilDB->fetchAssoc($result))
9890  {
9891  if (!array_key_exists($row["active_fi"], $foundusers))
9892  {
9893  $foundusers[$row["active_fi"]] = array();
9894  }
9895  array_push($foundusers[$row["active_fi"]], array("pass" => $row["pass"], "qid" => $row["question_fi"]));
9896  }
9897  return $foundusers;
9898  }
9899 
9905  public function hasPDFProcessing()
9906  {
9907  global $ilias;
9908 
9909  include_once './Services/WebServices/RPC/classes/class.ilRPCServerSettings.php';
9910  if(ilRPCServerSettings::getInstance()->isEnabled())
9911  {
9912  return TRUE;
9913  }
9914  else
9915  {
9916  return FALSE;
9917  }
9918  }
9919 
9925  public function getAggregatedResultsData()
9926  {
9927  $data =& $this->getCompleteEvaluationData();
9928  $foundParticipants =& $data->getParticipants();
9929  $results = array("overview" => array(), "questions" => array());
9930  if (count($foundParticipants))
9931  {
9932  $results["overview"][$this->lng->txt("tst_eval_total_persons")] = count($foundParticipants);
9933  $total_finished = $this->evalTotalFinished();
9934  $results["overview"][$this->lng->txt("tst_eval_total_finished")] = $total_finished;
9935  $average_time = $this->evalTotalStartedAverageTime();
9936  $diff_seconds = $average_time;
9937  $diff_hours = floor($diff_seconds/3600);
9938  $diff_seconds -= $diff_hours * 3600;
9939  $diff_minutes = floor($diff_seconds/60);
9940  $diff_seconds -= $diff_minutes * 60;
9941  $results["overview"][$this->lng->txt("tst_eval_total_finished_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds);
9942  $total_passed = 0;
9943  $total_passed_reached = 0;
9944  $total_passed_max = 0;
9945  $total_passed_time = 0;
9946  foreach ($foundParticipants as $userdata)
9947  {
9948  if ($userdata->getPassed())
9949  {
9950  $total_passed++;
9951  $total_passed_reached += $userdata->getReached();
9952  $total_passed_max += $userdata->getMaxpoints();
9953  $total_passed_time += $userdata->getTimeOfWork();
9954  }
9955  }
9956  $average_passed_reached = $total_passed ? $total_passed_reached / $total_passed : 0;
9957  $average_passed_max = $total_passed ? $total_passed_max / $total_passed : 0;
9958  $average_passed_time = $total_passed ? $total_passed_time / $total_passed : 0;
9959  $results["overview"][$this->lng->txt("tst_eval_total_passed")] = $total_passed;
9960  $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);
9961  $average_time = $average_passed_time;
9962  $diff_seconds = $average_time;
9963  $diff_hours = floor($diff_seconds/3600);
9964  $diff_seconds -= $diff_hours * 3600;
9965  $diff_minutes = floor($diff_seconds/60);
9966  $diff_seconds -= $diff_minutes * 60;
9967  $results["overview"][$this->lng->txt("tst_eval_total_passed_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds);
9968  }
9969 
9970  foreach ($data->getQuestionTitles() as $question_id => $question_title)
9971  {
9972  $answered = 0;
9973  $reached = 0;
9974  $max = 0;
9975  foreach ($foundParticipants as $userdata)
9976  {
9977  for ($i = 0; $i <= $userdata->getLastPass(); $i++)
9978  {
9979  if (is_object($userdata->getPass($i)))
9980  {
9981  $question =& $userdata->getPass($i)->getAnsweredQuestionByQuestionId($question_id);
9982  if (is_array($question))
9983  {
9984  $answered++;
9985  $reached += $question["reached"];
9986  $max += $question["points"];
9987  }
9988  }
9989  }
9990  }
9991  $percent = $max ? $reached/$max * 100.0 : 0;
9992  $counter++;
9993  $results["questions"][$question_id] = array(
9994  $question_title,
9995  sprintf("%.2f", $answered ? $reached / $answered : 0) . " " . strtolower($this->lng->txt("of")) . " " . sprintf("%.2f", $answered ? $max / $answered : 0),
9996  sprintf("%.2f", $percent) . "%",
9997  $answered,
9998  sprintf("%.2f", $answered ? $reached / $answered : 0),
9999  sprintf("%.2f", $answered ? $max / $answered : 0),
10000  $percent / 100.0
10001  );
10002  }
10003  return $results;
10004  }
10005 
10009  function getXMLZip()
10010  {
10011  include_once("./Modules/Test/classes/class.ilTestExport.php");
10012  $test_exp = new ilTestExport($this, "xml");
10013  return $test_exp->buildExportFile();
10014  }
10015 
10019  public function getMailNotification()
10020  {
10021  return $this->mailnotification;
10022  }
10023 
10029  public function setMailNotification($a_notification)
10030  {
10031  $this->mailnotification = $a_notification;
10032  }
10033 
10034  public function sendSimpleNotification($active_id)
10035  {
10036  include_once "./Services/Mail/classes/class.ilMail.php";
10037  $mail = new ilMail(ANONYMOUS_USER_ID);
10038 
10039  $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id));
10040  $message = new ilTemplate("tpl.il_as_tst_finish_notification_simple.html", TRUE, TRUE, "Modules/Test");
10041  $message->setVariable('TEXT_TEST_TITLE', $this->lng->txt('title'));
10042  $message->setVariable('VALUE_TEST_TITLE', $this->getTitle());
10043  $message->setVariable('TEXT_USER_NAME', $this->lng->txt('username'));
10044  $message->setVariable('VALUE_USER_NAME', $usr_data);
10045  $message->setVariable('TEXT_FINISH_TIME', $this->lng->txt('tst_finished'));
10047  $message->setVariable('VALUE_FINISH_TIME', ilDatePresentation::formatDate(new ilDateTime(time(), IL_CAL_UNIX)));
10048 
10049  $res = $mail->sendMail(
10050  ilObjUser::_lookupLogin($this->getOwner()), // to
10051  "", // cc
10052  "", // bcc
10053  sprintf($this->lng->txt('tst_user_finished_test'), $this->getTitle()), // subject
10054  $message->get(), // message
10055  array(), // attachments
10056  array('normal') // type
10057  );
10058  global $ilLog; $ilLog->write("sending mail: " . $res);
10059  }
10060 
10067  {
10068  include_once "./Modules/Test/classes/class.ilObjTestGUI.php";
10069  include_once "./Modules/Test/classes/tables/class.ilEvaluationAllTableGUI.php";
10070  $table_gui = new ilEvaluationAllTableGUI(new ilObjTestGUI(), 'outEvaluation', $this->getAnonymity());
10071  return $table_gui->getSelectedColumns();
10072  }
10073 
10074  public function sendAdvancedNotification($active_id)
10075  {
10076  include_once "./Services/Mail/classes/class.ilMail.php";
10077  $mail = new ilMail(ANONYMOUS_USER_ID);
10078 
10079  $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id));
10080  $message = new ilTemplate("tpl.il_as_tst_finish_notification_simple.html", TRUE, TRUE, "Modules/Test");
10081  $message->setVariable('TEXT_TEST_TITLE', $this->lng->txt('title'));
10082  $message->setVariable('VALUE_TEST_TITLE', $this->getTitle());
10083  $message->setVariable('TEXT_USER_NAME', $this->lng->txt('username'));
10084  $message->setVariable('VALUE_USER_NAME', $usr_data);
10085  $message->setVariable('TEXT_FINISH_TIME', $this->lng->txt('tst_finished'));
10087  $message->setVariable('VALUE_FINISH_TIME', ilDatePresentation::formatDate(new ilDateTime(time(), IL_CAL_UNIX)));
10088 
10089  include_once "./Modules/Test/classes/class.ilTestExport.php";
10090  $exportObj = new ilTestExport($this, "results");
10091  $file = $exportObj->exportToExcel($deliver = FALSE, 'active_id', $active_id, $passedonly = FALSE);
10092  include_once "./classes/class.ilFileDataMail.php";
10093  $fd = new ilFileDataMail(ANONYMOUS_USER_ID);
10094  $fd->copyAttachmentFile($file, "result_" . $active_id . ".xls");
10095  $file_names[] = "result_" . $active_id . ".xls";
10096  $result = $mail->sendMail(
10097  ilObjUser::_lookupLogin($this->getOwner()), // to
10098  "", // cc
10099  "", // bcc
10100  sprintf($this->lng->txt('tst_user_finished_test'), $this->getTitle()), // subject
10101  $message->get(), // message
10102  count($file_names) ? $file_names : array(), // attachments
10103  array('normal') // type
10104  );
10105  if(count($file_names))
10106  {
10107  $fd->unlinkFiles($file_names);
10108  unset($fd);
10109  @unlink($file);
10110  }
10111  }
10112 
10113  function createRandomSolutions($number)
10114  {
10115  global $ilDB;
10116 
10117  // 1. get a user
10118  $query = "SELECT usr_id FROM usr_data";
10119  $result = $ilDB->query($query);
10120  while ($data = $ilDB->fetchAssoc($result))
10121  {
10122  $activequery = sprintf("SELECT user_fi FROM tst_active WHERE test_fi = %s AND user_fi = %s",
10123  $ilDB->quote($this->getTestId()),
10124  $ilDB->quote($data['usr_id'])
10125  );
10126  $activeresult = $ilDB->query($activequery);
10127  if ($activeresult->numRows() == 0)
10128  {
10129  $user_id = $data['usr_id'];
10130  if ($user_id != 13)
10131  {
10132  include_once "./Modules/Test/classes/class.ilTestSession.php";
10133  $testSession = FALSE;
10134  $testSession = new ilTestSession();
10135  $testSession->setRefId($this->getRefId());
10136  $testSession->setTestId($this->getTestId());
10137  $testSession->setUserId($user_id);
10138  $testSession->saveToDb();
10139  $passes = ($this->getNrOfTries()) ? $this->getNrOfTries() : 10;
10140  $nr_of_passes = rand(1, $passes);
10141  $active_id = $testSession->getActiveId();
10142  for ($pass = 0; $pass < $nr_of_passes; $pass++)
10143  {
10144  include_once "./Modules/Test/classes/class.ilTestSequence.php";
10145  $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
10146  if (!$testSequence->hasSequence())
10147  {
10148  $testSequence->createNewSequence($this->getQuestionCount(), $shuffle);
10149  $testSequence->saveToDb();
10150  }
10151  for ($seq = 1; $seq <= count($this->questions); $seq++)
10152  {
10153  $question_id = $testSequence->getQuestionForSequence($seq);
10154  $objQuestion = ilObjTest::_instanciateQuestion($question_id);
10155  $objQuestion->createRandomSolution($testSession->getActiveId(), $pass);
10156  }
10157  if ($pass < $nr_of_passes - 1)
10158  {
10159  $testSession->increasePass();
10160  $testSession->setLastSequence(0);
10161  $testSession->saveToDb();
10162  }
10163  else
10164  {
10165  $this->setActiveTestSubmitted($user_id);
10166  }
10167  }
10168  $number--;
10169  if ($number == 0) return;
10170  }
10171  }
10172  }
10173  }
10174 
10175  public function getResultsForActiveId($active_id)
10176  {
10177  global $ilDB;
10178 
10179  $result = $ilDB->queryF("SELECT * FROM tst_result_cache WHERE active_fi = %s",
10180  array('integer'),
10181  array($active_id)
10182  );
10183  if (!$result->numRows())
10184  {
10185  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
10187  $result = $ilDB->queryF("SELECT * FROM tst_result_cache WHERE active_fi = %s",
10188  array('integer'),
10189  array($active_id)
10190  );
10191  }
10192  $row = $ilDB->fetchAssoc($result);
10193  return $row;
10194 
10195  }
10196 
10197  public function getMailNotificationType()
10198  {
10199  if ($this->mailnottype == 1)
10200  {
10201  return $this->mailnottype;
10202  }
10203  else
10204  {
10205  return 0;
10206  }
10207  }
10208 
10209  public function setMailNotificationType($a_type)
10210  {
10211  if ($a_type == 1)
10212  {
10213  $this->mailnottype = 1;
10214  }
10215  else
10216  {
10217  $this->mailnottype = 0;
10218  }
10219  }
10220 
10221  public function getExportSettings()
10222  {
10223  if ($this->exportsettings)
10224  {
10225  return $this->exportsettings;
10226  }
10227  else
10228  {
10229  return 0;
10230  }
10231  }
10232 
10233  public function setExportSettings($a_settings)
10234  {
10235  if ($a_settings)
10236  {
10237  $this->exportsettings = $a_settings;
10238  }
10239  else
10240  {
10241  $this->exportsettings = 0;
10242  }
10243  }
10244 
10246  {
10247  if (($this->exportsettings & 1) > 0)
10248  {
10249  return true;
10250  }
10251  else
10252  {
10253  return false;
10254  }
10255  }
10256 
10257  public function setExportSettingsSingleChoiceShort($a_settings)
10258  {
10259  if ($a_settings)
10260  {
10261  $this->exportsettings = $this->exportsettings | 1;
10262  }
10263  else
10264  {
10266  {
10267  $this->exportsettings = $this->exportsettings ^ 1;
10268  }
10269  }
10270  }
10271 
10272  public function getEnabledViewMode() {
10273  return $this->enabled_view_mode;
10274  }
10275 
10276  public function setEnabledViewMode($mode) {
10277  $this->enabled_view_mode = $mode;
10278  }
10279 
10281  $this->template_id = (int)$template_id;
10282  }
10283 
10284  function getTemplate() {
10285  return $this->template_id;
10286  }
10287 
10288  public function moveQuestionAfterOLD($previous_question_id, $new_question_id) {
10289  $new_array = array();
10290  $position = 1;
10291 
10292  $query = 'SELECT question_fi FROM tst_test_question WHERE test_fi = %s';
10293  $types = array('integer');
10294  $values = array($this->getTestId());
10295 
10296  $new_question_id += 1;
10297 
10298  global $ilDB;
10299  $inserted = false;
10300  $res = $ilDB->queryF($query, $types, $values);
10301  while($row = $ilDB->fetchAssoc($res)) {
10302 
10303  $qid = $row['question_fi'];
10304 
10305  if ($qid == $new_question_id) {
10306  continue;
10307  }
10308  else if ($qid == $previous_question_id) {
10309  $new_array[$position++] = $qid;
10310  $new_array[$position++] = $new_question_id;
10311  $inserted = true;
10312  }
10313  else {
10314  $new_array[$position++] = $qid;
10315  }
10316  }
10317 
10318  $update_query = 'UPDATE tst_test_question SET sequence = %s WHERE test_fi = %s AND question_fi = %s';
10319  $update_types = array('integer', 'integer', 'integer');
10320 
10321  foreach($new_array as $position => $qid) {
10322  $ilDB->manipulateF(
10323  $update_query,
10324  $update_types,
10325  $vals = array(
10326  $position,
10327  $this->getTestId(),
10328  $qid
10329  )
10330  );
10331  }
10332  }
10333 
10334  public function setScoringFeedbackOptionsByArray($options) {
10335  $this->setAnswerFeedback((is_array($options) && in_array('instant_feedback_answer', $options)) ? 1 : 0);
10336  $this->setAnswerFeedbackPoints((is_array($options) && in_array('instant_feedback_points', $options)) ? 1 : 0);
10337  $this->setInstantFeedbackSolution((is_array($options) && in_array('instant_feedback_solution', $options)) ? 1 : 0);
10338  }
10339 
10340  public function setResultsPresentationOptionsByArray($options) {
10341  $setter = array(
10342  'pass_details' => 'setShowPassDetails',
10343  'solution_details' => 'setShowSolutionDetails',
10344  'solution_printview' => 'setShowSolutionPrintview',
10345  'solution_feedback' => 'setShowSolutionFeedback',
10346  'solution_answers_only' => 'setShowSolutionAnswersOnly',
10347  'solution_signature' => 'setShowSolutionSignature',
10348  'solution_suggested' => 'setShowSolutionSuggested',
10349  );
10350  foreach($setter as $key => $setter) {
10351  if (in_array($key, $options)) {
10352  $this->$setter(1);
10353  }
10354  else {
10355  $this->$setter(0);
10356  }
10357  }
10358  }
10359 
10360  public function getPoolUsage() {
10361  return (boolean) $this->poolUsage;
10362  }
10363 
10364  public function setPoolUsage($usage) {
10365  $this->poolUsage = (boolean)$usage;
10366  }
10367 
10368  public function setQuestionOrder($order) {
10369  $positions = asort($order);
10370 
10371  global $ilDB;
10372 
10373  $i = 0;
10374 
10375  foreach($order as $key => $position) {
10376  $id = substr($key, strpos($key, '_') + 1);
10377  $ilDB->manipulateF(
10378  'UPDATE tst_test_question SET sequence = %s WHERE question_fi=%s',
10379  array('integer', 'integer'),
10380  array(++$i, $id)
10381  );
10382  }
10383 
10384  $this->loadQuestions();
10385  }
10386 
10387  public function moveQuestionAfter($question_to_move, $question_before) {
10388  global $ilDB;
10389  //var_dump(func_get_args());
10390  if ($question_before) {
10391  $query = 'SELECT sequence, test_fi FROM tst_test_question WHERE question_fi = %s';
10392  $types = array('integer');
10393  $values = array($question_before);
10394  $rset = $ilDB->queryF($query, $types, $values);
10395  }
10396 
10397  if (!$question_before || ($rset && !($row = $ilDB->fetchAssoc($rset)))) {
10398  $row = array(
10399  'sequence' => 0,
10400  'test_fi' => $this->getTestId(),
10401  );
10402  }
10403 
10404  $update = 'UPDATE tst_test_question SET sequence = sequence + 1 WHERE sequence > %s AND test_fi = %s';
10405  $types = array('integer', 'integer');
10406  $values = array($row['sequence'], $row['test_fi']);
10407  $ilDB->manipulateF($update, $types, $values);
10408 
10409  $update = 'UPDATE tst_test_question SET sequence = %s WHERE question_fi = %s';
10410  $types = array('integer', 'integer');
10411  $values = array($row['sequence'] + 1, $question_to_move);
10412  $ilDB->manipulateF($update, $types, $values);
10413 
10414  }
10415 
10417  global $ilDB;
10419  $query = 'SELECT count(question_id) cnt FROM qpl_questions'.
10420  ' INNER JOIN object_data on obj_fi = obj_id'.
10421  ' WHERE type <> '.$ilDB->quote('qpl', 'text').
10422  ' AND '.$ilDB->in('question_id', array_keys($questions), false, 'integer');
10423 
10424  $rset = $ilDB->query($query);
10425 
10426  if ($row = $ilDB->fetchAssoc($rset)) {
10427  return $row['cnt'] > 0;
10428  }
10429  return false;
10430  }
10431 
10438  public static function _lookupFinishedUserTests($a_user_id)
10439  {
10440  global $ilDB;
10441 
10442  $result = $ilDB->queryF("SELECT test_fi,MAX(pass) AS pass FROM tst_active".
10443  " JOIN tst_pass_result ON (tst_pass_result.active_fi = tst_active.active_id)".
10444  " WHERE user_fi=%s".
10445  " GROUP BY test_fi",
10446  array('integer', 'integer'),
10447  array($a_user_id, 1)
10448  );
10449  $all = array();
10450  while($row = $ilDB->fetchAssoc($result))
10451  {
10452  $obj_id = self::_getObjectIDFromTestID($row["test_fi"]);
10453  $all[$obj_id] = (bool)$row["pass"];
10454  }
10455  return $all;
10456  }
10457  public function getQuestions()
10458  {
10459  return $this->questions;
10460  }
10461 
10462  public function isOnline()
10463  {
10464  return $this->online;
10465  }
10466 
10467  public function setOnline($a_online = true)
10468  {
10469  $this->online = (bool)$a_online;
10470  }
10471 
10472 } // END class.ilObjTest
10473 
10474 ?>