ILIAS  Release_4_0_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilObjTest.php
Go to the documentation of this file.
1 <?php
2 /*
3  +-----------------------------------------------------------------------------+
4  | ILIAS open source |
5  +-----------------------------------------------------------------------------+
6  | Copyright (c) 1998-2009 ILIAS open source, University of Cologne |
7  | |
8  | This program is free software; you can redistribute it and/or |
9  | modify it under the terms of the GNU General Public License |
10  | as published by the Free Software Foundation; either version 2 |
11  | of the License, or (at your option) any later version. |
12  | |
13  | This program is distributed in the hope that it will be useful, |
14  | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16  | GNU General Public License for more details. |
17  | |
18  | You should have received a copy of the GNU General Public License |
19  | along with this program; if not, write to the Free Software |
20  | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21  +-----------------------------------------------------------------------------+
22 */
23 
34 include_once "./classes/class.ilObject.php";
35 include_once "./Modules/Test/classes/inc.AssessmentConstants.php";
36 
37 class ilObjTest extends ilObject
38 {
46  protected $_kiosk;
47 
53  var $test_id;
54 
61 
62 
69  var $author;
70 
76  var $metadata;
77 
84 
92 
99 
108 
119 
128 
135 
145 
152 
160 
168 
178 
185 
192 
199 
206 
213 
220 
226  var $ects_fx;
227 
234 
243 
250 
258 
265 
272 
280 
287 
294 
301 
308 
315 
322 
329 
336 
343 
350 
357 
364 
371 
378 
385 
392 
398  private $_showinfo;
399 
405  private $_forcejs;
406 
412  private $_customStyle;
413 
414  protected $mailnotification;
416 
423  function ilObjTest($a_id = 0,$a_call_by_reference = true)
424  {
425  global $ilUser;
426  $this->type = "tst";
427  include_once "./Modules/Test/classes/class.assMarkSchema.php";
428  $this->mark_schema = new ASS_MarkSchema();
429  $this->test_id = -1;
430  $this->author = $ilUser->fullname;
431  $this->introduction = "";
432  $this->questions = array();
433  $this->sequence_settings = TEST_FIXED_SEQUENCE;
434  $this->score_reporting = REPORT_AFTER_TEST;
435  $this->instant_verification = 0;
436  $this->answer_feedback_points = 0;
437  $this->reporting_date = "";
438  $this->nr_of_tries = 0;
439  $this->_kiosk = 0;
440  $this->use_previous_answers = 1;
441  $this->title_output = 0;
442  $this->starting_time = "";
443  $this->ending_time = "";
444  $this->processing_time = "00:00:00";
445  $this->enable_processing_time = "0";
446  $this->reset_processing_time = 0;
447  $this->ects_output = 0;
448  $this->ects_fx = NULL;
449  $this->random_test = 0;
450  $this->shuffle_questions = FALSE;
451  $this->show_summary = 8;
452  $this->random_question_count = "";
453  $this->count_system = COUNT_PARTIAL_SOLUTIONS;
454  $this->mc_scoring = SCORE_ZERO_POINTS_WHEN_UNANSWERED;
455  $this->score_cutting = SCORE_CUT_QUESTION;
456  $this->pass_scoring = SCORE_LAST_PASS;
457  $this->answer_feedback = 0;
458  $this->password = "";
459  $this->certificate_visibility = 0;
460  $this->allowedUsers = "";
461  $this->_showfinalstatement = FALSE;
462  $this->_finalstatement = "";
463  $this->_showinfo = TRUE;
464  $this->_forcejs = FALSE;
465  $this->_customStyle = "";
466  $this->allowedUsersTimeGap = "";
467  $this->anonymity = 0;
468  $this->show_cancel = 1;
469  $this->show_marker = 0;
470  $this->fixed_participants = 0;
471  $this->setShowPassDetails(TRUE);
472  $this->setShowSolutionDetails(TRUE);
473  $this->setShowSolutionAnswersOnly(FALSE);
474  $this->setShowSolutionSignature(FALSE);
475  $this->testSession = FALSE;
476  $this->testSequence = FALSE;
477  $this->mailnotification = 0;
478  global $lng;
479  $lng->loadLanguageModule("assessment");
480  $this->mark_schema->createSimpleSchema($lng->txt("failed_short"), $lng->txt("failed_official"), 0, 0, $lng->txt("passed_short"), $lng->txt("passed_official"), 50, 1);
481  $this->ects_grades = array(
482  "A" => 90,
483  "B" => 65,
484  "C" => 35,
485  "D" => 10,
486  "E" => 0
487  );
488  $this->ilObject($a_id, $a_call_by_reference);
489  }
490 
494  function create($a_upload = false)
495  {
496  parent::create();
497 
498  // meta data will be created by
499  // import parser
500  if (!$a_upload)
501  {
502  $this->createMetaData();
503  }
504  }
505 
512  function update()
513  {
514  if (!parent::update())
515  {
516  return false;
517  }
518 
519  // put here object specific stuff
520 
521  return true;
522  }
523 
530  function createReference() {
532  $this->saveToDb();
533  return $result;
534  }
535 
541  function read($a_force_db = false)
542  {
543  parent::read($a_force_db);
544  $this->loadFromDb();
545  }
546 
547 
554  function delete()
555  {
556  // always call parent delete function first!!
557  if (!parent::delete())
558  {
559  return false;
560  }
561 
562  // delet meta data
563  $this->deleteMetaData();
564 
565  //put here your module specific stuff
566  $this->deleteTest();
567 
568  return true;
569  }
570 
576  function deleteTest()
577  {
578  global $ilDB;
579 
580  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s",
581  array('integer'),
582  array($this->getTestId())
583  );
584  $active_array = array();
585  while ($row = $ilDB->fetchAssoc($result))
586  {
587  array_push($active_array, $row["active_id"]);
588  }
589 
590  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
591  array('integer'),
592  array($this->getTestId())
593  );
594 
595  if (count($active_array))
596  {
597  foreach ($active_array as $active_id)
598  {
599  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_times WHERE active_fi = %s",
600  array('integer'),
601  array($active_id)
602  );
603 
604  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
605  array('integer'),
606  array($active_id)
607  );
608  }
609  }
610 
611  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_mark WHERE test_fi = %s",
612  array('integer'),
613  array($this->getTestId())
614  );
615 
616  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s",
617  array('integer'),
618  array($this->getTestId())
619  );
620  while ($row = $ilDB->fetchAssoc($result))
621  {
622  $this->removeQuestion($row["question_fi"]);
623  }
624 
625  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_tests WHERE test_id = %s",
626  array('integer'),
627  array($this->getTestId())
628  );
629 
630  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
631  array('integer'),
632  array($this->getTestId())
633  );
634 
635  $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)",
636  array('integer'),
637  array($this->getTestId())
638  );
639 
640  $this->removeAllTestEditings();
641 
642  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
643  array('integer'),
644  array($this->getTestId())
645  );
646 
647  if ($this->isRandomTest())
648  {
650  }
651 
652 
653  // delete export files
654  include_once "./Services/Utilities/classes/class.ilUtil.php";
655  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
656  $directory = $tst_data_dir."/tst_".$this->getId();
657  if (is_dir($directory))
658  {
659  include_once "./Services/Utilities/classes/class.ilUtil.php";
660  ilUtil::delDir($directory);
661  }
662  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
663  $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId());
664  // remaining usages are not in text anymore -> delete them
665  // and media objects (note: delete method of ilObjMediaObject
666  // checks whether object is used in another context; if yes,
667  // the object is not deleted!)
668  foreach($mobs as $mob)
669  {
670  ilObjMediaObject::_removeUsage($mob, "tst:html", $this->getId());
671  if (ilObjMediaObject::_exists($mob))
672  {
673  $mob_obj =& new ilObjMediaObject($mob);
674  $mob_obj->delete();
675  }
676  }
677  }
678 
688  function initDefaultRoles()
689  {
690  global $rbacadmin;
691  return array();
692  }
693 
707  function notify($a_event,$a_ref_id,$a_parent_non_rbac_id,$a_node_id,$a_params = 0)
708  {
709  global $tree;
710 
711  switch ($a_event)
712  {
713  case "link":
714 
715  //var_dump("<pre>",$a_params,"</pre>");
716  //echo "Module name ".$this->getRefId()." triggered by link event. Objects linked into target object ref_id: ".$a_ref_id;
717  //exit;
718  break;
719 
720  case "cut":
721 
722  //echo "Module name ".$this->getRefId()." triggered by cut event. Objects are removed from target object ref_id: ".$a_ref_id;
723  //exit;
724  break;
725 
726  case "copy":
727 
728  //var_dump("<pre>",$a_params,"</pre>");
729  //echo "Module name ".$this->getRefId()." triggered by copy event. Objects are copied into target object ref_id: ".$a_ref_id;
730  //exit;
731  break;
732 
733  case "paste":
734 
735  //echo "Module name ".$this->getRefId()." triggered by paste (cut) event. Objects are pasted into target object ref_id: ".$a_ref_id;
736  //exit;
737  break;
738 
739  case "new":
740 
741  //echo "Module name ".$this->getRefId()." triggered by paste (new) event. Objects are applied to target object ref_id: ".$a_ref_id;
742  //exit;
743  break;
744  }
745 
746  // At the beginning of the recursive process it avoids second call of the notify function with the same parameter
747  if ($a_node_id==$_GET["ref_id"])
748  {
749  $parent_obj =& $this->ilias->obj_factory->getInstanceByRefId($a_node_id);
750  $parent_type = $parent_obj->getType();
751  if ($parent_type == $this->getType())
752  {
753  $a_node_id = (int) $tree->getParentId($a_node_id);
754  }
755  }
756 
757  parent::notify($a_event,$a_ref_id,$a_parent_non_rbac_id,$a_node_id,$a_params);
758  }
759 
766  {
767  include_once "./Services/Utilities/classes/class.ilUtil.php";
768  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
769  ilUtil::makeDir($tst_data_dir);
770  if (!is_writable($tst_data_dir))
771  {
772  $this->ilias->raiseError("Test Data Directory (".$tst_data_dir
773  .") not writeable.",$this->ilias->error_obj->MESSAGE);
774  }
775 
776  // create learning module directory (data_dir/lm_data/lm_<id>)
777  $tst_dir = $tst_data_dir."/tst_".$this->getId();
778  ilUtil::makeDir($tst_dir);
779  if (!@is_dir($tst_dir))
780  {
781  $this->ilias->raiseError("Creation of Test Directory failed.",$this->ilias->error_obj->MESSAGE);
782  }
783  // create Export subdirectory (data_dir/lm_data/lm_<id>/Export)
784  $export_dir = $tst_dir."/export";
785  ilUtil::makeDir($export_dir);
786  if (!@is_dir($export_dir))
787  {
788  $this->ilias->raiseError("Creation of Export Directory failed.",$this->ilias->error_obj->MESSAGE);
789  }
790  }
791 
798  {
799  include_once "./Services/Utilities/classes/class.ilUtil.php";
800  $export_dir = ilUtil::getDataDir()."/tst_data"."/tst_".$this->getId()."/export";
801  return $export_dir;
802  }
803 
811  {
812  // quit if import dir not available
813  if (!@is_dir($dir) or
814  !is_writeable($dir))
815  {
816  return array();
817  }
818 
819  // open directory
820  $dir = dir($dir);
821 
822  // initialize array
823  $file = array();
824 
825  // get files and save the in the array
826  while ($entry = $dir->read())
827  {
828  if ($entry != "." and
829  $entry != ".." and
830  //substr($entry, -4) == ".zip" and
831  ereg("^[0-9]{10}_{2}[0-9]+_{2}(test(__results)?__)*[0-9]+\.[a-z]{1,3}\$", $entry))
832  {
833  $file[] = $entry;
834  }
835  }
836 
837  // close import directory
838  $dir->close();
839 
840  // sort files
841  sort ($file);
842  reset ($file);
843 
844  return $file;
845  }
846 
847 
854  {
855  global $ilias;
856 
857  include_once "./Services/Utilities/classes/class.ilUtil.php";
858  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
859  ilUtil::makeDir($tst_data_dir);
860 
861  if (!is_writable($tst_data_dir))
862  {
863  $ilias->raiseError("Test data directory (".$tst_data_dir
864  .") not writeable.",$ilias->error_obj->FATAL);
865  }
866 
867  // create test directory (data_dir/tst_data/tst_import)
868  $tst_dir = $tst_data_dir."/tst_import";
869  ilUtil::makeDir($tst_dir);
870  if (!@is_dir($tst_dir))
871  {
872  $ilias->raiseError("Creation of test import directory failed.",$ilias->error_obj->FATAL);
873  }
874  }
875 
883  {
884  include_once "./Services/Utilities/classes/class.ilUtil.php";
885  $import_dir = ilUtil::getDataDir()."/tst_data/tst_import";
886  if (@is_dir($import_dir))
887  {
888  return $import_dir;
889  }
890  else
891  {
892  return false;
893  }
894  }
895 
902  {
903  include_once "./Services/Utilities/classes/class.ilUtil.php";
904  $tst_data_dir = ilUtil::getDataDir()."/tst_data";
905  ilUtil::makeDir($tst_data_dir);
906 
907  if (!is_writable($tst_data_dir))
908  {
909  $this->ilias->raiseError("Test Data Directory (".$tst_data_dir
910  .") not writeable.",$this->ilias->error_obj->FATAL);
911  }
912 
913  // create test directory (data_dir/tst_data/tst_import)
914  $tst_dir = $tst_data_dir."/tst_import";
915  ilUtil::makeDir($tst_dir);
916  if (!@is_dir($tst_dir))
917  {
918  $ilias->raiseError("Creation of test import directory failed.",$ilias->error_obj->FATAL);
919  }
920  }
921 
929  {
930  include_once "./Services/Utilities/classes/class.ilUtil.php";
931  $import_dir = ilUtil::getDataDir()."/tst_data/tst_import";
932  if (@is_dir($import_dir))
933  {
934  return $import_dir;
935  }
936  else
937  {
938  return false;
939  }
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->isSingleChoiceTest()) 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",
984  array('integer'),
985  array($this->getTestId())
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, " .
1150  "alloweduserstimegap, certificate_visibility, mailnotification, created, tstamp) " .
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, " .
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)",
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',
1161  'integer', 'text', 'integer', '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->getAllowedUsersTimeGap(),
1212  $this->getCertificateVisibility(),
1213  $this->getMailNotification(),
1214  time(),
1215  time()
1216  )
1217  );
1218  $this->test_id = $next_id;
1219 
1221  {
1222  $this->logAction($this->lng->txtlng("assessment", "log_create_new_test", ilObjAssessmentFolder::_getLogLanguage()));
1223  }
1224  }
1225  else
1226  {
1227  // Modify existing dataset
1228  $oldrow = array();
1230  {
1231  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
1232  array('integer'),
1233  array($this->test_id)
1234  );
1235  if ($result->numRows() == 1)
1236  {
1237  $oldrow = $ilDB->fetchAssoc($result);
1238  }
1239  }
1240 
1241  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET author = %s, introduction = %s, " .
1242  "finalstatement = %s, showinfo = %s, forcejs = %s, customstyle = %s, showfinalstatement = %s, sequence_settings = %s, " .
1243  "score_reporting = %s, instant_verification = %s, answer_feedback_points = %s, answer_feedback = %s, anonymity = %s, show_cancel = %s, show_marker = %s, " .
1244  "fixed_participants = %s, nr_of_tries = %s, kiosk = %s, use_previous_answers = %s, title_output = %s, processing_time = %s, enable_processing_time = %s, " .
1245  "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, " .
1246  "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, " .
1247  "shuffle_questions = %s, results_presentation = %s, show_summary = %s, password = %s, allowedusers = %s, " .
1248  "alloweduserstimegap = %s, certificate_visibility = %s, mailnotification = %s, tstamp = %s WHERE test_id = %s",
1249  array(
1250  'text', 'text',
1251  'text', 'integer', 'integer', 'text', 'integer', 'integer',
1252  'integer', 'text', 'text', 'text', 'text', 'text', 'integer',
1253  'text', 'integer', 'integer', 'text', 'text', 'text', 'text',
1254  'integer', 'text', 'text', 'text', 'text', 'text', 'float', 'float', 'float', 'float',
1255  'float', 'float', 'text', 'integer', 'text', 'text', 'text', 'text',
1256  'text', 'integer', 'integer', 'text', 'integer',
1257  'integer', 'text', 'integer', 'integer', 'integer'
1258  ),
1259  array(
1260  $this->getAuthor(),
1263  $this->getShowInfo(),
1264  $this->getForceJS(),
1265  $this->getCustomStyle(),
1266  $this->getShowFinalStatement(),
1267  $this->getSequenceSettings(),
1268  $this->getScoreReporting(),
1269  $this->getInstantFeedbackSolution(),
1270  $this->getAnswerFeedbackPoints(),
1271  $this->getAnswerFeedback(),
1272  $this->getAnonymity(),
1273  $this->getShowCancel(),
1274  $this->getShowMarker(),
1275  $this->getFixedParticipants(),
1276  $this->getNrOfTries(),
1277  $this->getKiosk(),
1278  $this->getUsePreviousAnswers(),
1279  $this->getTitleOutput(),
1280  $this->getProcessingTime(),
1281  $this->getEnableProcessingTime(),
1282  $this->getResetProcessingTime(),
1283  $this->getReportingDate(),
1284  $this->getStartingTime(),
1285  $this->getEndingTime(),
1286  $this->isComplete(),
1287  $this->getECTSOutput(),
1288  strlen($this->ects_grades["A"]) ? $this->ects_grades["A"] : NULL,
1289  strlen($this->ects_grades["B"]) ? $this->ects_grades["B"] : NULL,
1290  strlen($this->ects_grades["C"]) ? $this->ects_grades["C"] : NULL,
1291  strlen($this->ects_grades["D"]) ? $this->ects_grades["D"] : NULL,
1292  strlen($this->ects_grades["E"]) ? $this->ects_grades["E"] : NULL,
1293  $this->getECTSFX(),
1294  $this->isRandomTest(),
1295  $this->getRandomQuestionCount(),
1296  $this->getCountSystem(),
1297  $this->getMCScoring(),
1298  $this->getScoreCutting(),
1299  $this->getPassScoring(),
1300  $this->getShuffleQuestions(),
1301  $this->getResultsPresentation(),
1302  $this->getListOfQuestionsSettings(),
1303  $this->getPassword(),
1304  $this->getAllowedUsers(),
1305  $this->getAllowedUsersTimeGap(),
1306  $this->getCertificateVisibility(),
1307  $this->getMailNotification(),
1308  time(),
1309  $this->getTestId()
1310  )
1311  );
1312 
1313  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1315  {
1316  $logresult = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
1317  array('integer'),
1318  array($this->getTestId())
1319  );
1320  $newrow = array();
1321  if ($logresult->numRows() == 1)
1322  {
1323  $newrow = $ilDB->fetchAssoc($logresult);
1324  }
1325  $changed_fields = array();
1326  foreach ($oldrow as $key => $value)
1327  {
1328  if (strcmp($oldrow[$key], $newrow[$key]) != 0)
1329  {
1330  array_push($changed_fields, "$key: " . $oldrow[$key] . " => " . $newrow[$key]);
1331  }
1332  }
1333  $changes = join($changed_fields, ", ");
1334  if (count($changed_fields) > 0)
1335  {
1336  $this->logAction($this->lng->txtlng("assessment", "log_modified_test", ilObjAssessmentFolder::_getLogLanguage()) . " [".$changes."]");
1337  }
1338  }
1339  if ($this->evalTotalPersons() > 0)
1340  {
1341  // reset the finished status of participants if the nr of test passes did change
1342  if ($this->getNrOfTries() > 0)
1343  {
1344  // set all unfinished tests with nr of passes >= allowed passes finished
1345  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND tries >= %s AND submitted = %s",
1346  array('integer', 'integer', 'integer'),
1347  array($this->getTestId(), $this->getNrOfTries(), 0)
1348  );
1349  while ($row = $ilDB->fetchAssoc($aresult))
1350  {
1351  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1352  array('integer', 'timestamp', 'integer'),
1353  array(1, date('Y-m-d H:i:s'), $row["active_id"])
1354  );
1355  }
1356 
1357  // set all finished tests with nr of passes < allowed passes not finished
1358  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND tries < %s AND submitted = %s",
1359  array('integer', 'integer', 'integer'),
1360  array($this->getTestId(), $this->getNrOfTries()-1, 1)
1361  );
1362  while ($row = $ilDB->fetchAssoc($aresult))
1363  {
1364  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1365  array('integer', 'timestamp', 'integer'),
1366  array(0, NULL, $row["active_id"])
1367  );
1368  }
1369  }
1370  else
1371  {
1372  // set all finished tests with nr of passes >= allowed passes not finished
1373  $aresult = $ilDB->queryF("SELECT active_id FROM tst_active WHERE test_fi = %s AND submitted = %s",
1374  array('integer', 'integer'),
1375  array($this->getTestId(), 1)
1376  );
1377  while ($row = $ilDB->fetchAssoc($aresult))
1378  {
1379  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s WHERE active_id = %s",
1380  array('integer', 'timestamp', 'integer'),
1381  array(0, NULL, $row["active_id"])
1382  );
1383  }
1384  }
1385  }
1386  }
1387  if (!$this->isRandomTest())
1388  {
1390  }
1391  if (!$properties_only)
1392  {
1393  if (PEAR::isError($result))
1394  {
1395  global $ilias;
1396  $ilias->raiseError($result->getMessage());
1397  }
1398  else
1399  {
1400  if (!$this->isRandomTest())
1401  {
1402  $this->saveQuestionsToDb();
1403  }
1404  $this->mark_schema->saveToDb($this->test_id);
1405  }
1406  }
1407  }
1408 
1416  {
1417  global $ilDB;
1418 
1419  $oldquestions = array();
1420  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
1422  {
1423  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence",
1424  array('integer'),
1425  array($this->getTestId())
1426  );
1427  if ($result->numRows() > 0)
1428  {
1429  while ($row = $ilDB->fetchAssoc($result))
1430  {
1431  array_push($oldquestions, $row["question_fi"]);
1432  }
1433  }
1434  }
1435 
1436  // delete existing category relations
1437  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
1438  array('integer'),
1439  array($this->getTestId())
1440  );
1441  // create new category relations
1442  foreach ($this->questions as $key => $value)
1443  {
1444  $next_id = $ilDB->nextId('tst_test_question');
1445  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
1446  array('integer','integer', 'integer', 'integer', 'integer'),
1447  array($next_id, $this->getTestId(), $value, $key, time())
1448  );
1449  }
1450  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1452  {
1453  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE test_fi = %s ORDER BY sequence",
1454  array('integer'),
1455  array($this->getTestId())
1456  );
1457  $newquestions = array();
1458  if ($result->numRows() > 0)
1459  {
1460  while ($row = $ilDB->fetchAssoc($result))
1461  {
1462  array_push($newquestions, $row["question_fi"]);
1463  }
1464  }
1465  foreach ($oldquestions as $index => $question_id)
1466  {
1467  if (strcmp($newquestions[$index], $question_id) != 0)
1468  {
1469  $pos = array_search($question_id, $newquestions);
1470  if ($pos === FALSE)
1471  {
1472  $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id);
1473  }
1474  else
1475  {
1476  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index+1) . " => " . ($pos+1), $question_id);
1477  }
1478  }
1479  }
1480  foreach ($newquestions as $index => $question_id)
1481  {
1482  if (array_search($question_id, $oldquestions) === FALSE)
1483  {
1484  $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($index+1), $question_id);
1485  }
1486  }
1487  }
1488  }
1489 
1493  protected function isNewRandomTest()
1494  {
1495  global $ilDB;
1496  $result = $ilDB->queryF('SELECT copy_id FROM tst_rnd_cpy WHERE tst_fi = %s',
1497  array('integer'),
1498  array($this->getTestId())
1499  );
1500  return $result->numRows() > 0;
1501  }
1502 
1509  function saveRandomQuestion($active_id, $question_id, $pass = NULL, $maxcount)
1510  {
1511  global $ilUser;
1512  global $ilDB;
1513 
1514  if (is_null($pass)) $pass = 0;
1515  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s",
1516  array('integer','integer'),
1517  array($active_id, $pass)
1518  );
1519  if ($result->numRows() < $maxcount)
1520  {
1521  $duplicate_id = $question_id;
1522  if (!$this->isNewRandomTest())
1523  {
1524  $duplicate_id = $this->getRandomQuestionDuplicate($question_id, $active_id);
1525  if ($duplicate_id === FALSE)
1526  {
1527  $duplicate_id = $this->duplicateQuestionForTest($question_id);
1528  }
1529  }
1530  $next_id = $ilDB->nextId('tst_test_rnd_qst');
1531  $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)",
1532  array('integer','integer','integer','integer','integer','integer'),
1533  array($next_id,$active_id, $duplicate_id, $result->numRows()+1, $pass, time())
1534  );
1535  }
1536  }
1537 
1547  function getRandomQuestionDuplicate($question_id, $active_id)
1548  {
1549  global $ilDB;
1550 
1551  $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",
1552  array('integer', 'integer'),
1553  array($question_id, $active_id)
1554  );
1555  $num = $result->numRows();
1556  if ($num > 0)
1557  {
1558  $row = $ilDB->fetchAssoc($result);
1559  return $row["question_id"];
1560  }
1561  else
1562  {
1563  return FALSE;
1564  }
1565  }
1566 
1572  function getNrOfResultsForPass($active_id, $pass)
1573  {
1574  global $ilDB;
1575 
1576  $result = $ilDB->queryF("SELECT test_result_id FROM tst_test_result WHERE active_fi = %s AND pass = %s",
1577  array('integer','integer'),
1578  array($active_id, $pass)
1579  );
1580  return $result->numRows();
1581  }
1582 
1591  function hasRandomQuestionsForPass($active_id, $pass)
1592  {
1593  global $ilDB;
1594  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE active_fi = %s AND pass = %s",
1595  array('integer','integer'),
1596  array($active_id, $pass)
1597  );
1598  return ($result->numRows() > 0) ? true : false;
1599  }
1600 
1607  function generateRandomQuestions($active_id, $pass = NULL)
1608  {
1609  global $ilUser;
1610  global $ilDB;
1611 
1612  if ($active_id > 0)
1613  {
1614  if ($this->hasRandomQuestionsForPass($active_id, $pass) > 0)
1615  {
1616  // Something went wrong. Maybe the user pressed the start button twice
1617  // Questions already exist so there is no need to create new questions
1618  return;
1619  }
1620  if ($pass > 0)
1621  {
1622  if ($this->getNrOfResultsForPass($active_id, $pass - 1) == 0)
1623  {
1624  // This means that someone maybe reloaded the test submission page
1625  // If there are no existing results for the previous test, it makes
1626  // no sense to create a new set of random questions
1627  return;
1628  }
1629  }
1630  }
1631  else
1632  {
1633  // This may not happen! If it happens, raise a fatal error...
1634  global $ilias, $ilErr;
1635  $ilias->raiseError(sprintf($this->lng->txt("error_random_question_generation"), $ilUser->getId(), $this->getTestId()), $ilErr->FATAL);
1636  }
1637 
1638  $num = $this->getRandomQuestionCount();
1639  if ($num > 0)
1640  {
1641  $qpls =& $this->getRandomQuestionpools();
1642  $rndquestions = $this->generateRandomPass($num, $qpls, $pass);
1643  $allquestions = array();
1644  foreach ($rndquestions as $question_id)
1645  {
1646  array_push($allquestions, $question_id);
1647  }
1648  if ($this->getShuffleQuestions())
1649  {
1650  srand ((float)microtime()*1000000);
1651  shuffle($allquestions);
1652  }
1653 
1654  $maxcount = 0;
1655  foreach ($qpls as $data)
1656  {
1657  $maxcount += $data["contains"];
1658  }
1659  if ($num > $maxcount) $num = $maxcount;
1660  foreach ($allquestions as $question_id)
1661  {
1662  $this->saveRandomQuestion($active_id, $question_id, $pass, $num);
1663  }
1664  }
1665  else
1666  {
1667  $qpls =& $this->getRandomQuestionpools();
1668  $allquestions = array();
1669  $maxcount = 0;
1670  foreach ($qpls as $key => $value)
1671  {
1672  if ($value["count"] > 0)
1673  {
1674  $rndquestions = $this->generateRandomPass($value["count"], array($value), $pass);
1675  foreach ($rndquestions as $question_id)
1676  {
1677  array_push($allquestions, $question_id);
1678  }
1679  }
1680  $add = ($value["count"] <= $value["contains"]) ? $value["count"] : $value["contains"];
1681  $maxcount += $add;
1682  }
1683  if ($this->getShuffleQuestions())
1684  {
1685  srand ((float)microtime()*1000000);
1686  shuffle($allquestions);
1687  }
1688  foreach ($allquestions as $question_id)
1689  {
1690  $this->saveRandomQuestion($active_id, $question_id, $pass, $maxcount);
1691  }
1692  }
1693  }
1694 
1701  function saveRandomQuestionCount($total_questions = NULL)
1702  {
1703  global $ilDB;
1704 
1705  if (strlen($total_questions))
1706  {
1707  $this->setRandomQuestionCount($total_questions);
1708  }
1709  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET random_question_count = %s, tstamp = %s WHERE test_id = %s",
1710  array('integer', 'integer', 'integer'),
1711  array($this->getRandomQuestionCount(), time(), $this->getTestId())
1712  );
1713  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1715  {
1716  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_total_amount_of_questions", ilObjAssessmentFolder::_getLogLanguage()), $this->getRandomQuestionCount()));
1717  }
1718  }
1719 
1728  {
1729  global $ilDB;
1730 
1731  $qpls = array();
1732  $counter = 0;
1733  $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",
1734  array("integer"),
1735  array($this->getTestId())
1736  );
1737  if ($result->numRows())
1738  {
1739  while ($row = $ilDB->fetchAssoc($result))
1740  {
1741  $qpls[$counter] = array(
1742  "index" => $counter,
1743  "count" => $row["num_of_q"],
1744  "qpl" => $row["questionpool_fi"],
1745  "contains" => $row["questioncount"]
1746  );
1747  $counter++;
1748  }
1749  }
1750  return $qpls;
1751  }
1752 
1756  public function saveRandomQuestionpools()
1757  {
1758  global $ilDB;
1759 
1760  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1761  // delete existing random questionpools
1762  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
1763  array('integer'),
1764  array($this->getTestId())
1765  );
1767  {
1768  $this->logAction($this->lng->txtlng("assessment", "log_random_question_pool_deleted", ilObjAssessmentFolder::_getLogLanguage()));
1769  }
1770  // delete existing duplicated questions
1772 
1773  // create new random questionpools
1774  foreach ($this->random_questionpool_data as $idx => $data)
1775  {
1776  if ($data->qpl > 0)
1777  {
1778  // save questionpool information
1779  $next_id = $ilDB->nextId('tst_test_random');
1780  $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)",
1781  array('integer','integer', 'integer', 'integer', 'integer', 'integer'),
1782  array($next_id, $this->getTestId(), $data->qpl, $data->count, time(), $idx)
1783  );
1785  {
1786  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_random_question_pool_added", ilObjAssessmentFolder::_getLogLanguage()), $value["title"] . " (" . $value["qpl"] . ")", $value["count"]));
1787  }
1788  // duplicate all questions of the questionpools
1789  $this->duplicateQuestionpoolForTest($data->qpl);
1790  }
1791  }
1792  }
1793 
1797  protected function removeDuplicatedQuestionpools()
1798  {
1799  global $ilDB;
1800 
1801  $result = $ilDB->queryF('SELECT * FROM tst_rnd_cpy WHERE tst_fi = %s',
1802  array('integer'),
1803  array($this->getTestId())
1804  );
1805  while ($row = $ilDB->fetchAssoc($result))
1806  {
1807  $question =& ilObjTest::_instanciateQuestion($row['qst_fi']);
1808  $question->delete($row['qst_fi']);
1809  }
1810 
1811  $affectedRows = $ilDB->manipulateF('DELETE FROM tst_rnd_cpy WHERE tst_fi = %s',
1812  array('integer'),
1813  array($this->getTestId())
1814  );
1815 
1816  $affectedRows = $ilDB->manipulateF('DELETE FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1817  array('integer'),
1818  array($this->getTestId())
1819  );
1820  }
1821 
1825  public function getUsedRandomQuestionpools()
1826  {
1827  global $ilDB;
1828  if ($this->isNewRandomTest())
1829  {
1830  $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',
1831  array('integer'),
1832  array($this->getTestId())
1833  );
1834  $pools = array();
1835  while ($row = $ilDB->fetchAssoc($result))
1836  {
1837  if (is_array($pools[$row['qpl_fi']]))
1838  {
1839  $pools[$row['qpl_fi']]['count']++;
1840  }
1841  else
1842  {
1843  $pools[$row['qpl_fi']]['count'] = 1;
1844  }
1845  $pools[$row['qpl_fi']]['num_of_q'] = $row['num_of_q'];
1846  }
1847  $result = $ilDB->queryF('SELECT * FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1848  array('integer'),
1849  array($this->getTestId())
1850  );
1851 
1852  while ($row = $ilDB->fetchAssoc($result))
1853  {
1854  $pools[$row['qpl_fi']]['title'] = $row['qpl_title'];
1855  }
1856  return $pools;
1857  }
1858  else
1859  {
1860  $result = $ilDB->queryF('SELECT tst_test_random.* FROM tst_test_random WHERE tst_test_random.test_fi = %s ORDER BY sequence, test_random_id',
1861  array('integer'),
1862  array($this->getTestId())
1863  );
1864  $pools = array();
1865  while ($row = $ilDB->fetchAssoc($result))
1866  {
1867  $pools[$row['questionpool_fi']]['count'] = $row['num_of_q'];
1868  $pools[$row['questionpool_fi']]['num_of_q'] = $row['num_of_q'];
1869  }
1870  $result = $ilDB->queryF('SELECT * FROM tst_rnd_qpl_title WHERE tst_fi = %s',
1871  array('integer'),
1872  array($this->getTestId())
1873  );
1874 
1875  while ($row = $ilDB->fetchAssoc($result))
1876  {
1877  $pools[$row['qpl_fi']]['title'] = $row['qpl_title'];
1878  }
1879  return $pools;
1880  }
1881  }
1882 
1886  protected function duplicateQuestionpoolForTest($questionpool_id)
1887  {
1888  global $ilDB;
1889  $result = $ilDB->queryF('SELECT question_id FROM qpl_questions WHERE obj_fi = %s AND complete = %s AND original_id IS NULL',
1890  array('integer', 'text'),
1891  array($questionpool_id, 1)
1892  );
1893  $saved_titles = array();
1894  while ($row = $ilDB->fetchAssoc($result))
1895  {
1896  $question =& ilObjTest::_instanciateQuestion($row['question_id']);
1897  $duplicate_id = $question->duplicate(true);
1898  if ($duplicate_id > 0)
1899  {
1900  $next_id = $ilDB->nextId('tst_rnd_cpy');
1901  $ilDB->manipulateF('INSERT INTO tst_rnd_cpy (copy_id, tst_fi, qst_fi, qpl_fi) VALUES (%s, %s, %s, %s)',
1902  array('integer', 'integer', 'integer', 'integer'),
1903  array($next_id, $this->getTestId(), $duplicate_id, $questionpool_id)
1904  );
1905  if (!array_key_exists($questionpool_id, $saved_titles))
1906  {
1907  $next_id = $ilDB->nextId('tst_rnd_qpl_title');
1908  $ilDB->manipulateF('INSERT INTO tst_rnd_qpl_title (title_id, tst_fi, qpl_fi, qpl_title) VALUES (%s, %s, %s, %s)',
1909  array('integer', 'integer', 'integer', 'text'),
1910  array($next_id, $this->getTestId(), $questionpool_id, ilObject::_lookupTitle($questionpool_id))
1911  );
1912  $saved_titles[$questionpool_id] = 1;
1913  }
1914  }
1915  }
1916  }
1917 
1918  function addRandomQuestionpoolData($count = 0, $qpl = 0, $position)
1919  {
1920  if (array_key_exists($position, $this->random_questionpool_data))
1921  {
1922  $newitems = array();
1923  for ($i = 0; $i < $position; $i++)
1924  {
1925  array_push($newitems, $this->random_questionpool_data[$i]);
1926  }
1927  array_push($newitems, new ilRandomTestData($count, $qpl));
1928  for ($i = $position; $i < count($this->random_questionpool_data); $i++)
1929  {
1930  array_push($newitems, $this->random_questionpool_data[$i]);
1931  }
1932  $this->random_questionpool_data = $newitems;
1933  }
1934  else
1935  {
1936  array_push($this->random_questionpool_data, new ilRandomTestData($count, $qpl));
1937  }
1938  }
1939 
1940  function removeRandomQuestionpoolData($position)
1941  {
1942  if (array_key_exists($position, $this->random_questionpool_data))
1943  {
1944  unset($this->random_questionpool_data[$position]);
1945  }
1946  }
1947 
1948  function setRandomQuestionpoolData($a_data)
1949  {
1950  $this->random_questionpool_data = $a_data;
1951  }
1952 
1961  {
1962  if (is_array($this->random_questionpool_data) && count($this->random_questionpool_data)) return $this->random_questionpool_data;
1963 
1964  global $ilDB;
1965 
1966  $qpls = array();
1967  $counter = 0;
1968  $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",
1969  array("integer"),
1970  array($this->getTestId())
1971  );
1972  include_once "./Modules/Test/classes/class.ilRandomTestData.php";
1973  if ($result->numRows())
1974  {
1975  while ($row = $ilDB->fetchAssoc($result))
1976  {
1977  array_push($qpls, new ilRandomTestData($row['num_of_q'], $row['questionpool_fi']));
1978  }
1979  }
1980  return $qpls;
1981  }
1982 
1990  function loadFromDb()
1991  {
1992  global $ilDB;
1993 
1994  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE obj_fi = %s",
1995  array('integer'),
1996  array($this->getId())
1997  );
1998  if ($result->numRows() == 1)
1999  {
2000  $data = $ilDB->fetchObject($result);
2001  $this->setTestId($data->test_id);
2002  if (strlen($this->getAuthor()) == 0)
2003  {
2004  $this->saveAuthorToMetadata($data->author);
2005  }
2006  $this->setAuthor($data->author);
2007  include_once("./Services/RTE/classes/class.ilRTE.php");
2008  $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc($data->introduction, 1));
2009  $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc($data->finalstatement, 1));
2010  $this->setShowInfo($data->showinfo);
2011  $this->setForceJS($data->forcejs);
2012  $this->setCustomStyle($data->customstyle);
2013  $this->setShowFinalStatement($data->showfinalstatement);
2014  $this->setSequenceSettings($data->sequence_settings);
2015  $this->setScoreReporting($data->score_reporting);
2016  $this->setInstantFeedbackSolution($data->instant_verification);
2017  $this->setAnswerFeedbackPoints($data->answer_feedback_points);
2018  $this->setAnswerFeedback($data->answer_feedback);
2019  $this->setAnonymity($data->anonymity);
2020  $this->setShowCancel($data->show_cancel);
2021  $this->setShowMarker($data->show_marker);
2022  $this->setFixedParticipants($data->fixed_participants);
2023  $this->setNrOfTries($data->nr_of_tries);
2024  $this->setKiosk($data->kiosk);
2025  $this->setUsePreviousAnswers($data->use_previous_answers);
2026  $this->setTitleOutput($data->title_output);
2027  $this->setProcessingTime($data->processing_time);
2028  $this->setEnableProcessingTime($data->enable_processing_time);
2029  $this->setResetProcessingTime($data->reset_processing_time);
2030  $this->setReportingDate($data->reporting_date);
2031  $this->setShuffleQuestions($data->shuffle_questions);
2032  $this->setResultsPresentation($data->results_presentation);
2033  $this->setListOfQuestionsSettings($data->show_summary);
2034  $this->setStartingTime($data->starting_time);
2035  $this->setEndingTime($data->ending_time);
2036  $this->setECTSOutput($data->ects_output);
2037  $this->setECTSGrades(
2038  array(
2039  "A" => $data->ects_a,
2040  "B" => $data->ects_b,
2041  "C" => $data->ects_c,
2042  "D" => $data->ects_d,
2043  "E" => $data->ects_e
2044  )
2045  );
2046  $this->setECTSFX($data->ects_fx);
2047  $this->setRandomTest($data->random_test);
2048  $this->setRandomQuestionCount($data->random_question_count);
2049  $this->mark_schema->flush();
2050  $this->mark_schema->loadFromDb($this->getTestId());
2051  $this->setCountSystem($data->count_system);
2052  $this->setMCScoring($data->mc_scoring);
2053  $this->setMailNotification($data->mailnotification);
2054  $this->setScoreCutting($data->score_cutting);
2055  $this->setPassword($data->password);
2056  $this->setAllowedUsers($data->allowedusers);
2057  $this->setAllowedUsersTimeGap($data->alloweduserstimegap);
2058  $this->setPassScoring($data->pass_scoring);
2059  $this->setCertificateVisibility($data->certificate_visibility);
2060  $this->loadQuestions();
2061  }
2062  }
2063 
2070 function loadQuestions($active_id = "", $pass = NULL)
2071 {
2072  global $ilUser;
2073  global $ilDB;
2074 
2075  $this->questions = array();
2076  if (strcmp($active_id, "") == 0)
2077  {
2078  $active_id = $this->getActiveIdOfUser($ilUser->getId());
2079  }
2080  if ($this->isRandomTest())
2081  {
2082  if (is_null($pass))
2083  {
2084  $pass = $this->_getPass($active_id);
2085  }
2086  $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",
2087  array('integer', 'integer'),
2088  array($active_id, $pass)
2089  );
2090  // The following is a fix for random tests prior to ILIAS 3.8. If someone started a random test in ILIAS < 3.8, there
2091  // is only one test pass (pass = 0) in tst_test_rnd_qst while with ILIAS 3.8 there are questions for every test pass.
2092  // To prevent problems with tests started in an older version and continued in ILIAS 3.8, the first pass should be taken if
2093  // no questions are present for a newer pass.
2094  if ($result->numRows() == 0)
2095  {
2096  $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",
2097  array('integer'),
2098  array($active_id)
2099  );
2100  }
2101  }
2102  else
2103  {
2104  $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",
2105  array('integer'),
2106  array($this->test_id)
2107  );
2108  }
2109  $index = 1;
2110  while ($data = $ilDB->fetchAssoc($result))
2111  {
2112  $this->questions[$index++] = $data["question_fi"];
2113  }
2114 }
2115 
2124  {
2125  $this->introduction = $introduction;
2126  }
2127 
2135  public function setFinalStatement($a_statement = "")
2136  {
2137  $this->_finalstatement = $a_statement;
2138  }
2139 
2147  public function setShowInfo($a_info = 1)
2148  {
2149  $this->_showinfo = ($a_info) ? 1 : 0;
2150  }
2151 
2159  public function setForceJS($a_js = 1)
2160  {
2161  $this->_forcejs = ($a_js) ? 1 : 0;
2162  }
2163 
2171  public function setCustomStyle($a_customStyle = NULL)
2172  {
2173  $this->_customStyle = $a_customStyle;
2174  }
2175 
2183  public function getCustomStyle()
2184  {
2185  return (strlen($this->_customStyle)) ? $this->_customStyle : NULL;
2186  }
2187 
2195  public function getCustomStyles()
2196  {
2197  $css_path = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test");
2198  $css_path = str_replace("ta.css", "customstyles", $css_path) . "/";
2199  $customstyles = array();
2200  if (is_dir($css_path))
2201  {
2202  $results = array();
2203  include_once "./Services/Utilities/classes/class.ilFileUtils.php";
2205  if (is_array($results["file"]))
2206  {
2207  foreach ($results["file"] as $filename)
2208  {
2209  if (strpos($filename, ".css"))
2210  {
2211  array_push($customstyles, $filename);
2212  }
2213  }
2214  }
2215  }
2216  return $customstyles;
2217  }
2218 
2226  public function getTestStyleLocation($mode = "output")
2227  {
2228  if (strlen($this->getCustomStyle()))
2229  {
2230  $default = ilUtil::getStyleSheetLocation("filesystem", "ta.css", "Modules/Test");
2231  $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $default);
2232  if (file_exists($custom))
2233  {
2234  $custom = ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2235  $custom = str_replace("ta.css", "customstyles/" . $this->getCustomStyle(), $custom);
2236  return $custom;
2237  }
2238  else
2239  {
2240  return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2241  }
2242  }
2243  else
2244  {
2245  return ilUtil::getStyleSheetLocation($mode, "ta.css", "Modules/Test");
2246  }
2247  }
2248 
2256  public function setShowFinalStatement($show = 0)
2257  {
2258  $this->_showfinalstatement = ($show) ? 1 : 0;
2259  }
2260 
2261 
2269  function isRandomTest()
2270  {
2271  return ($this->random_test) ? 1 : 0;
2272  }
2273 
2274 
2280  public static function _lookupRandomTest($a_obj_id)
2281  {
2282  global $ilDB;
2283 
2284  $query = "SELECT random_test FROM tst_tests ".
2285  "WHERE obj_fi = ".$ilDB->quote($a_obj_id,'integer');
2286  $res = $ilDB->query($query);
2287  while($row = $res->fetchRow(DB_FETCHMODE_OBJECT))
2288  {
2289  return $row->random_test ? true : false;
2290  }
2291  return false;
2292  }
2293 
2302  {
2303  return ($this->random_question_count) ? $this->random_question_count : 0;
2304  }
2305 
2312  public function getIntroduction()
2313  {
2314  return (strlen($this->introduction)) ? $this->introduction : NULL;
2315  }
2316 
2323  public function getFinalStatement()
2324  {
2325  return (strlen($this->_finalstatement)) ? $this->_finalstatement : NULL;
2326  }
2327 
2335  public function getShowInfo()
2336  {
2337  return ($this->_showinfo) ? 1 : 0;
2338  }
2339 
2347  public function getForceJS()
2348  {
2349  return ($this->_forcejs) ? 1 : 0;
2350  }
2351 
2359  public function getShowFinalStatement()
2360  {
2361  return ($this->_showfinalstatement) ? 1 : 0;
2362  }
2363 
2371  function getTestId()
2372  {
2373  return $this->test_id;
2374  }
2375 
2383  function getECTSOutput()
2384  {
2385  return ($this->ects_output) ? 1 : 0;
2386  }
2387 
2395  function setECTSOutput($a_ects_output)
2396  {
2397  $this->ects_output = $a_ects_output ? 1 : 0;
2398  }
2399 
2407  function getECTSFX()
2408  {
2409  return (strlen($this->ects_fx)) ? $this->ects_fx : NULL;
2410  }
2411 
2419  function setECTSFX($a_ects_fx)
2420  {
2421  $this->ects_fx = $a_ects_fx;
2422  }
2423 
2431  function &getECTSGrades()
2432  {
2433  return $this->ects_grades;
2434  }
2435 
2443  function setECTSGrades($a_ects_grades)
2444  {
2445  if (is_array($a_ects_grades))
2446  {
2447  $this->ects_grades = $a_ects_grades;
2448  }
2449  }
2450 
2459  {
2460  $this->sequence_settings = $sequence_settings;
2461  }
2462 
2471  {
2472  $this->score_reporting = $score_reporting;
2473  }
2474 
2482  function setInstantFeedbackSolution($instant_feedback = 0)
2483  {
2484  switch ($instant_feedback)
2485  {
2486  case 1:
2487  $this->instant_verification = 1;
2488  break;
2489  default:
2490  $this->instant_verification = 0;
2491  break;
2492  }
2493  }
2494 
2503  {
2504  switch ($answer_feedback)
2505  {
2506  case 1:
2507  $this->answer_feedback = 1;
2508  break;
2509  default:
2510  $this->answer_feedback = 0;
2511  break;
2512  }
2513  }
2514 
2523  {
2524  switch ($answer_feedback_points)
2525  {
2526  case 1:
2527  $this->answer_feedback_points = 1;
2528  break;
2529  default:
2530  $this->answer_feedback_points = 0;
2531  break;
2532  }
2533  }
2534 
2542  function setRandomTest($a_random_test = 0)
2543  {
2544  $this->random_test = $a_random_test;
2545  }
2546 
2554  function setRandomQuestionCount($a_random_question_count = "")
2555  {
2556  $this->random_question_count = $a_random_question_count;
2557  }
2558 
2567  {
2568  if (!$reporting_date)
2569  {
2570  $this->reporting_date = "";
2571  $this->ects_output = 0;
2572  }
2573  else
2574  {
2575  $this->reporting_date = $reporting_date;
2576  }
2577  }
2578 
2587  {
2588  return ($this->sequence_settings) ? $this->sequence_settings : 0;
2589  }
2590 
2599  {
2600  return ($this->score_reporting) ? $this->score_reporting : 0;
2601  }
2602 
2611  {
2612  return ($this->instant_verification) ? $this->instant_verification : 0;
2613  }
2614 
2623  {
2624  return ($this->answer_feedback) ? $this->answer_feedback : 0;
2625  }
2626 
2635  {
2636  return ($this->answer_feedback_points) ? $this->answer_feedback_points : 0;
2637  }
2638 
2646  function getCountSystem()
2647  {
2648  return ($this->count_system) ? $this->count_system : 0;
2649  }
2650 
2658  function _getCountSystem($active_id)
2659  {
2660  global $ilDB;
2661  $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",
2662  array('integer'),
2663  array($active_id)
2664  );
2665  if ($result->numRows())
2666  {
2667  $row = $ilDB->fetchAssoc($result);
2668  return $row["count_system"];
2669  }
2670  return FALSE;
2671  }
2672 
2680  function getMCScoring()
2681  {
2682  return ($this->mc_scoring) ? $this->mc_scoring : 0;
2683  }
2684 
2692  function getScoreCutting()
2693  {
2694  return ($this->score_cutting) ? $this->score_cutting : 0;
2695  }
2696 
2704  function getPassword()
2705  {
2706  return (strlen($this->password)) ? $this->password : NULL;
2707  }
2708 
2716  function getPassScoring()
2717  {
2718  return ($this->pass_scoring) ? $this->pass_scoring : 0;
2719  }
2720 
2728  function _getPassScoring($active_id)
2729  {
2730  global $ilDB;
2731  $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",
2732  array('integer'),
2733  array($active_id)
2734  );
2735  if ($result->numRows())
2736  {
2737  $row = $ilDB->fetchAssoc($result);
2738  return $row["pass_scoring"];
2739  }
2740  return 0;
2741  }
2742 
2750  function _getMCScoring($active_id)
2751  {
2752  global $ilDB;
2753  $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",
2754  array('integer'),
2755  array($active_id)
2756  );
2757  if ($result->numRows())
2758  {
2759  $row = $ilDB->fetchAssoc($result);
2760  return $row["mc_scoring"];
2761  }
2762  return FALSE;
2763  }
2764 
2772  function _getScoreCutting($active_id)
2773  {
2774  global $ilDB;
2775  $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",
2776  array('integer'),
2777  array($active_id)
2778  );
2779  if ($result->numRows())
2780  {
2781  $row = $ilDB->fetchAssoc($result);
2782  return $row["score_cutting"];
2783  }
2784  return FALSE;
2785  }
2786 
2794  function getReportingDate()
2795  {
2796  return (strlen($this->reporting_date)) ? $this->reporting_date : NULL;
2797  }
2798 
2806  function getNrOfTries()
2807  {
2808  return ($this->nr_of_tries) ? $this->nr_of_tries : 0;
2809  }
2810 
2818  function getKiosk()
2819  {
2820  return ($this->_kiosk) ? $this->_kiosk : 0;
2821  }
2822 
2823 
2831  function setKiosk($kiosk = 0)
2832  {
2833  $this->_kiosk = $kiosk;
2834  }
2835 
2843  function getKioskMode()
2844  {
2845  if (($this->_kiosk & 1) > 0)
2846  {
2847  return TRUE;
2848  }
2849  else
2850  {
2851  return FALSE;
2852  }
2853  }
2854 
2862  public function setKioskMode($a_kiosk = FALSE)
2863  {
2864  if ($a_kiosk)
2865  {
2866  $this->_kiosk = $this->_kiosk | 1;
2867  }
2868  else
2869  {
2870  if ($this->getKioskMode())
2871  {
2872  $this->_kiosk = $this->_kiosk ^ 1;
2873  }
2874  }
2875  }
2876 
2884  public function getShowKioskModeTitle()
2885  {
2886  if (($this->_kiosk & 2) > 0)
2887  {
2888  return TRUE;
2889  }
2890  else
2891  {
2892  return FALSE;
2893  }
2894  }
2895 
2902  public function setShowKioskModeTitle($a_title = FALSE)
2903  {
2904  if ($a_title)
2905  {
2906  $this->_kiosk = $this->_kiosk | 2;
2907  }
2908  else
2909  {
2910  if ($this->getShowKioskModeTitle())
2911  {
2912  $this->_kiosk = $this->_kiosk ^ 2;
2913  }
2914  }
2915  }
2916 
2925  {
2926  if (($this->_kiosk & 4) > 0)
2927  {
2928  return TRUE;
2929  }
2930  else
2931  {
2932  return FALSE;
2933  }
2934  }
2935 
2942  public function setShowKioskModeParticipant($a_participant = FALSE)
2943  {
2944  if ($a_participant)
2945  {
2946  $this->_kiosk = $this->_kiosk | 4;
2947  }
2948  else
2949  {
2950  if ($this->getShowKioskModeParticipant())
2951  {
2952  $this->_kiosk = $this->_kiosk ^ 4;
2953  }
2954  }
2955  }
2956 
2965  {
2966  return ($this->use_previous_answers) ? $this->use_previous_answers : 0;
2967  }
2968 
2976  function getTitleOutput()
2977  {
2978  return ($this->title_output) ? $this->title_output : 0;
2979  }
2980 
2989  function _getTitleOutput($active_id)
2990  {
2991  global $ilDB;
2992 
2993  $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",
2994  array('integer'),
2995  array($active_id)
2996  );
2997  if ($result->numRows())
2998  {
2999  $row = $ilDB->fetchAssoc($result);
3000  return $row["title_output"];
3001  }
3002  return 0;
3003  }
3004 
3014  function _getUsePreviousAnswers($active_id, $user_active_user_setting = false)
3015  {
3016  global $ilDB;
3017  global $ilUser;
3018 
3020 
3021  $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",
3022  array("integer"),
3023  array($active_id)
3024  );
3025  if ($result->numRows())
3026  {
3027  $row = $ilDB->fetchAssoc($result);
3028  $use_previous_answers = $row["use_previous_answers"];
3029  }
3030 
3031  if ($use_previous_answers == 1)
3032  {
3033  if ($user_active_user_setting)
3034  {
3035  $res = $ilUser->getPref("tst_use_previous_answers");
3036  if ($res !== FALSE)
3037  {
3039  }
3040  }
3041  }
3042  return $use_previous_answers;
3043  }
3044 
3053  {
3054  return (strlen($this->processing_time)) ? $this->processing_time : NULL;
3055  }
3056 
3063  public function getProcessingTimeAsArray()
3064  {
3065  if (strlen($this->processing_time))
3066  {
3067  if (preg_match("/(\d{2}):(\d{2}):(\d{2})/is", $this->processing_time, $matches))
3068  {
3069  if ((int)$matches[1]+(int)$matches[2]+(int)$matches[3] == 0)
3070  {
3071  return $this->getEstimatedWorkingTime();
3072  }
3073  else
3074  {
3075  return array(
3076  'hh' => $matches[1],
3077  'mm' => $matches[2],
3078  'ss' => $matches[3],
3079  );
3080  }
3081  }
3082  }
3083  return $this->getEstimatedWorkingTime();
3084  }
3085 
3094  {
3095  if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $this->getProcessingTime(), $matches))
3096  {
3097  return ($matches[1] * 3600) + ($matches[2] * 60) + $matches[3];
3098  }
3099  else
3100  {
3101  return 0;
3102  }
3103  }
3104 
3113  {
3114  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndingTime(), $matches))
3115  {
3116  $ending = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
3117  $now = time();
3118  return $ending - $now;
3119  }
3120  else
3121  {
3122  return 0;
3123  }
3124  }
3125 
3134  {
3135  return ($this->enable_processing_time) ? $this->enable_processing_time : 0;
3136  }
3137 
3146  {
3147  return ($this->reset_processing_time) ? $this->reset_processing_time : 0;
3148  }
3149 
3157  function getStartingTime()
3158  {
3159  return (strlen($this->starting_time)) ? $this->starting_time : NULL;
3160  }
3161 
3169  function getEndingTime()
3170  {
3171  return (strlen($this->ending_time)) ? $this->ending_time : NULL;
3172  }
3173 
3182  {
3183  $this->nr_of_tries = $nr_of_tries;
3184  }
3185 
3194  {
3196  {
3197  $this->use_previous_answers = 1;
3198  }
3199  else
3200  {
3201  $this->use_previous_answers = 0;
3202  }
3203  }
3204 
3213  {
3214  switch ($title_output)
3215  {
3216  case 1:
3217  $this->title_output = 1;
3218  break;
3219  case 2:
3220  $this->title_output = 2;
3221  break;
3222  default:
3223  $this->title_output = 0;
3224  break;
3225  }
3226  }
3227 
3235  function setProcessingTime($processing_time = "00:00:00")
3236  {
3237  $this->processing_time = $processing_time;
3238  }
3239 
3247  function setEnableProcessingTime($enable = 0)
3248  {
3249  if ($enable) {
3250  $this->enable_processing_time = "1";
3251  } else {
3252  $this->enable_processing_time = "0";
3253  }
3254  }
3255 
3263  function setResetProcessingTime($reset = 0)
3264  {
3265  if ($reset)
3266  {
3267  $this->reset_processing_time = 1;
3268  }
3269  else
3270  {
3271  $this->reset_processing_time = 0;
3272  }
3273  }
3274 
3283  {
3284  $this->starting_time = $starting_time;
3285  }
3286 
3294  function setEndingTime($ending_time = NULL)
3295  {
3296  $this->ending_time = $ending_time;
3297  }
3298 
3306  function setCountSystem($a_count_system = COUNT_PARTIAL_SOLUTIONS)
3307  {
3308  $this->count_system = $a_count_system;
3309  }
3310 
3318  function setPassword($a_password = NULL)
3319  {
3320  $this->password = $a_password;
3321  }
3322 
3330  function setScoreCutting($a_score_cutting = SCORE_CUT_QUESTION)
3331  {
3332  $this->score_cutting = $a_score_cutting;
3333  }
3334 
3343  {
3344  $this->mc_scoring = $a_mc_scoring;
3345  }
3346 
3354  function setPassScoring($a_pass_scoring = SCORE_LAST_PASS)
3355  {
3356  switch ($a_pass_scoring)
3357  {
3358  case SCORE_BEST_PASS:
3359  $this->pass_scoring = SCORE_BEST_PASS;
3360  break;
3361  default:
3362  $this->pass_scoring = SCORE_LAST_PASS;
3363  break;
3364  }
3365  }
3366 
3374  function removeQuestion($question_id)
3375  {
3376  $question =& ilObjTest::_instanciateQuestion($question_id);
3377  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3379  {
3380  $this->logAction($this->lng->txtlng("assessment", "log_question_removed", ilObjAssessmentFolder::_getLogLanguage()), $question_id);
3381  }
3382  $question->delete($question_id);
3383  $this->removeAllTestEditings($question_id);
3384  $this->loadQuestions();
3385  $this->saveQuestionsToDb();
3386  }
3387 
3395  public function removeAllTestEditings($question_id = "")
3396  {
3397  global $ilDB;
3398  // remove the question from tst_solutions
3399  if ($question_id)
3400  {
3401  $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",
3402  array('integer','integer'),
3403  array($this->getTestId(), $question_id)
3404  );
3405  $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",
3406  array('integer','integer'),
3407  array($this->getTestId(), $question_id)
3408  );
3409  $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",
3410  array('integer','integer'),
3411  array($this->getTestId(), $question_id)
3412  );
3413  $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)",
3414  array('integer'),
3415  array($this->getTestId())
3416  );
3417  }
3418  else
3419  {
3420  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE tst_solutions.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3421  array('integer'),
3422  array($this->getTestId())
3423  );
3424  $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)",
3425  array('integer'),
3426  array($this->getTestId())
3427  );
3428  $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)",
3429  array('integer'),
3430  array($this->getTestId())
3431  );
3432  $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)",
3433  array('integer'),
3434  array($this->getTestId())
3435  );
3436  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3438  {
3439  $this->logAction($this->lng->txtlng("assessment", "log_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()));
3440  }
3441  }
3442  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE tst_sequence.active_fi IN (SELECT active_id FROM tst_active WHERE test_fi = %s)",
3443  array('integer'),
3444  array($this->getTestId())
3445  );
3446 
3447  if ($this->isRandomTest())
3448  {
3449  $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)",
3450  array('integer'),
3451  array($this->getTestId())
3452  );
3453  }
3454 
3455  // remove test_active entries, because test has changed
3456  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
3457  array('integer'),
3458  array($this->getTestId())
3459  );
3460 
3461  // remove saved user passwords
3462  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE keyword = %s",
3463  array('text'),
3464  array("tst_password_".$this->getTestId())
3465  );
3466 
3467  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3468  // remove file uploads
3469  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId()))
3470  {
3471  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId());
3472  }
3473  }
3474 
3475  function removeSelectedTestResults($active_ids)
3476  {
3477  global $ilDB;
3478 
3479  // remove the question from tst_solutions
3480  foreach ($active_ids as $active_id)
3481  {
3482  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s",
3483  array('integer'),
3484  array($active_id)
3485  );
3486  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s",
3487  array('integer'),
3488  array($active_id)
3489  );
3490  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE active_fi = %s",
3491  array('integer'),
3492  array($active_id)
3493  );
3494  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE active_fi = %s",
3495  array('integer'),
3496  array($active_id)
3497  );
3498 
3499  if ($this->isRandomTest())
3500  {
3501  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE active_fi = %s",
3502  array('integer'),
3503  array($active_id)
3504  );
3505  }
3506 
3507  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3509  {
3510  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id))));
3511  }
3512  }
3513 
3514  // remove test_active entries of selected users
3515  foreach ($active_ids as $active_id)
3516  {
3517  $usr_id = $this->_getUserIdFromActiveId($active_id);
3518 
3519  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE active_id = %s",
3520  array('integer'),
3521  array($active_id)
3522  );
3523 
3524  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
3525  array('integer'),
3526  array($active_id)
3527  );
3528 
3529  // remove saved user password
3530  if ($usr_id > 0)
3531  {
3532  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE usr_id = %s AND keyword = %s",
3533  array('integer', 'text'),
3534  array($usr_id, "tst_password_".$this->getTestId())
3535  );
3536  }
3537 
3538  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3539  // remove file uploads
3540  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"))
3541  {
3542  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id");
3543  }
3544  }
3545  }
3546 
3547  function removeTestResultsForUser($user_id)
3548  {
3549  global $ilDB;
3550 
3551  $active_id = $this->getActiveIdOfUser($user_id);
3552 
3553  // remove the question from tst_solutions
3554  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s",
3555  array('integer'),
3556  array($active_id)
3557  );
3558  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s",
3559  array('integer'),
3560  array($active_id)
3561  );
3562  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_result WHERE active_fi = %s",
3563  array('integer'),
3564  array($active_id)
3565  );
3566  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_pass_result WHERE active_fi = %s",
3567  array('integer'),
3568  array($active_id)
3569  );
3570 
3571  if ($this->isRandomTest())
3572  {
3573  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_rnd_qst WHERE active_fi = %s",
3574  array('integer'),
3575  array($active_id)
3576  );
3577  }
3578 
3579  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3581  {
3582  $this->logAction(sprintf($this->lng->txtlng("assessment", "log_selected_user_data_removed", ilObjAssessmentFolder::_getLogLanguage()), $this->userLookupFullName($this->_getUserIdFromActiveId($active_id))));
3583  }
3584 
3585  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_sequence WHERE active_fi = %s",
3586  array('integer'),
3587  array($active_id)
3588  );
3589 
3590  // remove test_active entry
3591  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE active_id = %s",
3592  array('integer'),
3593  array($active_id)
3594  );
3595 
3596  // remove saved user password
3597  if ($user_id > 0)
3598  {
3599  $affectedRows = $ilDB->manipulateF("DELETE FROM usr_pref WHERE usr_id = %s AND keyword = %s",
3600  array('integer', 'text'),
3601  array($user_id, "tst_password_".$this->getTestId())
3602  );
3603  }
3604 
3605  // TODO: this shouldn't be here since it is question stuff and should be modular but there's no other solution yet
3606  // remove file uploads
3607  if (@is_dir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id"))
3608  {
3609  ilUtil::delDir(CLIENT_WEB_DIR . "/assessment/tst_" . $this->getTestId() . "/$active_id");
3610  }
3611  }
3612 
3620  function questionMoveUp($question_id)
3621  {
3622  global $ilDB;
3623 
3624  // Move a question up in sequence
3625  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
3626  array('integer', 'integer'),
3627  array($this->getTestId(), $question_id)
3628  );
3629  $data = $ilDB->fetchObject($result);
3630  if ($data->sequence > 1)
3631  {
3632  // OK, it's not the top question, so move it up
3633  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
3634  array('integer','integer'),
3635  array($this->getTestId(), $data->sequence - 1)
3636  );
3637  $data_previous = $ilDB->fetchObject($result);
3638  // change previous dataset
3639  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3640  array('integer','integer'),
3641  array($data->sequence, $data_previous->test_question_id)
3642  );
3643  // move actual dataset up
3644  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3645  array('integer','integer'),
3646  array($data->sequence - 1, $data->test_question_id)
3647  );
3648  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3650  {
3651  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence-1), $question_id);
3652  }
3653  }
3654  $this->loadQuestions();
3655  }
3656 
3664  function questionMoveDown($question_id)
3665  {
3666  global $ilDB;
3667 
3668  // Move a question down in sequence
3669  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND question_fi=%s",
3670  array('integer','integer'),
3671  array($this->getTestId(), $question_id)
3672  );
3673  $data = $ilDB->fetchObject($result);
3674  $result = $ilDB->queryF("SELECT * FROM tst_test_question WHERE test_fi=%s AND sequence=%s",
3675  array('integer','integer'),
3676  array($this->getTestId(), $data->sequence + 1)
3677  );
3678  if ($result->numRows() == 1)
3679  {
3680  // OK, it's not the last question, so move it down
3681  $data_next = $ilDB->fetchObject($result);
3682  // change next dataset
3683  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3684  array('integer','integer'),
3685  array($data->sequence, $data_next->test_question_id)
3686  );
3687  // move actual dataset down
3688  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_question SET sequence=%s WHERE test_question_id=%s",
3689  array('integer','integer'),
3690  array($data->sequence + 1, $data->test_question_id)
3691  );
3692  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3694  {
3695  $this->logAction($this->lng->txtlng("assessment", "log_question_position_changed", ilObjAssessmentFolder::_getLogLanguage()) . ": " . ($data->sequence) . " => " . ($data->sequence+1), $question_id);
3696  }
3697  }
3698  $this->loadQuestions();
3699  }
3700 
3708  function duplicateQuestionForTest($question_id)
3709  {
3710  global $ilUser;
3711  $question =& ilObjTest::_instanciateQuestion($question_id);
3712  $duplicate_id = $question->duplicate(true);
3713 
3714  return $duplicate_id;
3715  }
3716 
3723  function insertQuestion($question_id)
3724  {
3725  global $ilDB;
3726 
3727  $duplicate_id = $this->duplicateQuestionForTest($question_id);
3728 
3729  // get maximum sequence index in test
3730  $result = $ilDB->queryF("SELECT MAX(sequence) seq FROM tst_test_question WHERE test_fi=%s",
3731  array('integer'),
3732  array($this->getTestId())
3733  );
3734  $sequence = 1;
3735 
3736  if ($result->numRows() == 1)
3737  {
3738  $data = $ilDB->fetchObject($result);
3739  $sequence = $data->seq + 1;
3740  }
3741 
3742  $next_id = $ilDB->nextId('tst_test_question');
3743  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_question (test_question_id, test_fi, question_fi, sequence, tstamp) VALUES (%s, %s, %s, %s, %s)",
3744  array('integer', 'integer','integer','integer','integer'),
3745  array($next_id, $this->getTestId(), $duplicate_id, $sequence, time())
3746  );
3747  if ($affectedRows == 1)
3748  {
3749  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3751  {
3752  $this->logAction($this->lng->txtlng("assessment", "log_question_added", ilObjAssessmentFolder::_getLogLanguage()) . ": " . $sequence, $duplicate_id);
3753  }
3754  }
3755  // remove test_active entries, because test has changed
3756  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_active WHERE test_fi = %s",
3757  array('integer'),
3758  array($this->getTestId())
3759  );
3760  $this->loadQuestions();
3761  $this->saveCompleteStatus();
3762  }
3763 
3771  function &getQuestionTitles()
3772  {
3773  $titles = array();
3774  if (!$this->isRandomTest())
3775  {
3776  global $ilDB;
3777  $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",
3778  array('integer'),
3779  array($this->getTestId())
3780  );
3781  while ($row = $ilDB->fetchAssoc($result))
3782  {
3783  array_push($titles, $row["title"]);
3784  }
3785  }
3786  return $titles;
3787  }
3788 
3797  {
3798  $titles = array();
3799  if (!$this->isRandomTest())
3800  {
3801  global $ilDB;
3802  $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",
3803  array('integer'),
3804  array($this->getTestId())
3805  );
3806  while ($row = $ilDB->fetchAssoc($result))
3807  {
3808  $titles[$row['question_id']] = $row["title"];
3809  }
3810  }
3811  return $titles;
3812  }
3813 
3823  {
3824  if ($this->getTitleOutput() == 2)
3825  {
3826  return $this->lng->txt("ass_question");
3827  }
3828  else
3829  {
3830  return $title;
3831  }
3832  }
3833 
3842  function getQuestionDataset($question_id)
3843  {
3844  global $ilDB;
3845 
3846  $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",
3847  array('integer'),
3848  array($question_id)
3849  );
3850  $row = $ilDB->fetchObject($result);
3851  return $row;
3852  }
3853 
3860  function &getExistingQuestions($pass = NULL)
3861  {
3862  global $ilUser;
3863  global $ilDB;
3864 
3865  $existing_questions = array();
3866  $active_id = $this->getActiveIdOfUser($ilUser->getId());
3867  if ($this->isRandomTest())
3868  {
3869  if (is_null($pass)) $pass = 0;
3870  $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",
3871  array('integer','integer'),
3872  array($active_id, $pass)
3873  );
3874  }
3875  else
3876  {
3877  $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",
3878  array('integer'),
3879  array($this->getTestId())
3880  );
3881  }
3882  while ($data = $ilDB->fetchObject($result))
3883  {
3884  array_push($existing_questions, $data->original_id);
3885  }
3886  return $existing_questions;
3887  }
3888 
3896  function getQuestionType($question_id)
3897  {
3898  global $ilDB;
3899 
3900  if ($question_id < 1) return -1;
3901  $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",
3902  array('integer'),
3903  array($question_id)
3904  );
3905  if ($result->numRows() == 1)
3906  {
3907  $data = $ilDB->fetchObject($result);
3908  return $data->type_tag;
3909  }
3910  else
3911  {
3912  return "";
3913  }
3914  }
3915 
3922  function startWorkingTime($active_id, $pass)
3923  {
3924  global $ilDB;
3925 
3926  $next_id = $ilDB->nextId('tst_times');
3927  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_times (times_id, active_fi, started, finished, pass, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
3928  array('integer', 'integer', 'timestamp', 'timestamp', 'integer', 'integer'),
3929  array($next_id, $active_id, strftime("%Y-%m-%d %H:%M:%S"), strftime("%Y-%m-%d %H:%M:%S"), $pass, time())
3930  );
3931  return $next_id;
3932  }
3933 
3940  function updateWorkingTime($times_id)
3941  {
3942  global $ilDB;
3943 
3944  $affectedRows = $ilDB->manipulateF("UPDATE tst_times SET finished = %s, tstamp = %s WHERE times_id = %s",
3945  array('timestamp', 'integer', 'integer'),
3946  array(strftime("%Y-%m-%d %H:%M:%S"), time(), $times_id)
3947  );
3948  }
3949 
3956  function &getWorkedQuestions($active_id, $pass = NULL)
3957  {
3958  global $ilUser;
3959  global $ilDB;
3960 
3961  if (is_null($pass))
3962  {
3963  $result = $ilDB->queryF("SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
3964  array('integer','integer'),
3965  array($active_id, 0)
3966  );
3967  }
3968  else
3969  {
3970  $result = $ilDB->queryF("SELECT question_fi FROM tst_solutions WHERE active_fi = %s AND pass = %s GROUP BY question_fi",
3971  array('integer','integer'),
3972  array($active_id, $pass)
3973  );
3974  }
3975  $result_array = array();
3976  while ($row = $ilDB->fetchAssoc($result))
3977  {
3978  array_push($result_array, $row["question_fi"]);
3979  }
3980  return $result_array;
3981  }
3982 
3991  function isTestFinishedToViewResults($active_id, $currentpass)
3992  {
3993  $num = $this->getPassFinishDate($active_id, $currentpass);
3994  return ((($currentpass > 0) && ($num == 0)) || $this->isTestFinished($active_id)) ? true : false;
3995  }
3996 
4003  function &getAllQuestions($pass = NULL)
4004  {
4005  global $ilUser;
4006  global $ilDB;
4007 
4008  $result_array = array();
4009  if ($this->isRandomTest())
4010  {
4011  $active_id = $this->getActiveIdOfUser($ilUser->getId());
4012  $this->loadQuestions($active_id, $pass);
4013  if (count($this->questions) == 0) return $result_array;
4014  if (is_null($pass))
4015  {
4016  $pass = $this->_getPass($active_id);
4017  }
4018  $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'),
4019  array('integer','integer'),
4020  array($active_id, $pass)
4021  );
4022  }
4023  else
4024  {
4025  if (count($this->questions) == 0) return $result_array;
4026  $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'));
4027  }
4028  while ($row = $ilDB->fetchAssoc($result))
4029  {
4030  $result_array[$row["question_id"]] = $row;
4031  }
4032  return $result_array;
4033  }
4034 
4043  function getActiveIdOfUser($user_id = "", $anonymous_id = "")
4044  {
4045  global $ilDB;
4046  global $ilUser;
4047 
4048  if (!$user_id) $user_id = $ilUser->getId();
4049  if (($_SESSION["AccountId"] == ANONYMOUS_USER_ID) && (strlen($_SESSION["tst_access_code"][$this->getTestId()])))
4050  {
4051  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
4052  array('integer','integer','text'),
4053  array($user_id, $this->test_id, $_SESSION["tst_access_code"][$this->getTestId()])
4054  );
4055  }
4056  else if (strlen($anonymous_id))
4057  {
4058  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
4059  array('integer','integer','text'),
4060  array($user_id, $this->test_id, $anonymous_id)
4061  );
4062  }
4063  else
4064  {
4065  if ($_SESSION["AccountId"] == ANONYMOUS_USER_ID)
4066  {
4067  return NULL;
4068  }
4069  $result = $ilDB->queryF("SELECT active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
4070  array('integer','integer'),
4071  array($user_id, $this->test_id)
4072  );
4073  }
4074  if ($result->numRows())
4075  {
4076  $row = $ilDB->fetchAssoc($result);
4077  return $row["active_id"];
4078  }
4079  else
4080  {
4081  return 0;
4082  }
4083  }
4084 
4093  function _getActiveIdOfUser($user_id = "", $test_id = "")
4094  {
4095  global $ilDB;
4096  global $ilUser;
4097 
4098  if (!$user_id) {
4099  $user_id = $ilUser->id;
4100  }
4101  if (!$test_id)
4102  {
4103  return "";
4104  }
4105  $result = $ilDB->queryF("SELECT tst_active.active_id FROM tst_active WHERE user_fi = %s AND test_fi = %s",
4106  array('integer', 'integer'),
4107  array($user_id, $test_id)
4108  );
4109  if ($result->numRows())
4110  {
4111  $row = $ilDB->fetchAssoc($result);
4112  return $row["active_id"];
4113  }
4114  else
4115  {
4116  return "";
4117  }
4118  }
4119 
4126  function pcArrayShuffle($array)
4127  {
4128  $keys = array_keys($array);
4129  shuffle($keys);
4130  $result = array();
4131  foreach ($keys as $key)
4132  {
4133  $result[$key] = $array[$key];
4134  }
4135  return $result;
4136  }
4137 
4145  function &getTestResult($active_id, $pass = NULL, $ordered_sequence = FALSE)
4146  {
4147  global $ilDB;
4148 
4149  $results = $this->getResultsForActiveId($active_id);
4150  if (is_null($pass))
4151  {
4152  $pass = $results['pass'];
4153  }
4154  include_once "./Modules/Test/classes/class.ilTestSequence.php";
4155  $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
4156  $sequence = array();
4157  if ($ordered_sequence)
4158  {
4159  $sequence = $testSequence->getOrderedSequenceQuestions();
4160  }
4161  else
4162  {
4163  $sequence = $testSequence->getUserSequenceQuestions();
4164  }
4165  $arrResults = array();
4166  $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",
4167  array('integer', 'integer'),
4168  array($active_id, $pass)
4169  );
4170  while ($row = $ilDB->fetchAssoc($solutionresult))
4171  {
4172  $arrResults[$row['question_fi']] = $row;
4173  }
4174 
4175  require_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
4176  $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'));
4177  $found = array();
4178  $unordered = array();
4179  $key = 1;
4180  $pass_max = 0;
4181  $pass_reached = 0;
4182  while ($row = $ilDB->fetchAssoc($result))
4183  {
4184  $percentvalue = ($row['points']) ? $arrResults[$row['question_id']]['reached'] / $row['points'] : 0;
4185  if ($percentvalue < 0) $percentvalue = 0.0;
4186  $data = array(
4187  "nr" => "$key",
4188  "title" => ilUtil::prepareFormOutput($row['title']),
4189  "max" => round($row['points'], 2),
4190  "reached" => round($arrResults[$row['question_id']]['reached'],2),
4191  "percent" => sprintf("%2.2f ", ($percentvalue) * 100) . "%",
4192  "solution" => ($row['has_sug_sol']) ? assQuestion::_getSuggestedSolutionOutput($row['question_id']) : '',
4193  "type" => $row["type_tag"],
4194  "qid" => $row['question_id'],
4195  "original_id" => $row["original_id"],
4196  "workedthrough" => ($arrResults[$row['question_id']]['workedthru']) ? 1 : 0
4197  );
4198  $pass_max += round($row['points'], 2);
4199  $pass_reached += round($arrResults[$row['question_id']]['reached'], 2);
4200  $unordered[$row['question_id']] = $data;
4201  $key++;
4202  }
4203  $key = 1;
4204  foreach ($sequence as $qid)
4205  {
4206  $unordered[$qid]['nr'] = $key;
4207  array_push($found, $unordered[$qid]);
4208  $key++;
4209  }
4210  $unordered = null;
4211  if ($this->getScoreCutting() == 1)
4212  {
4213  if ($results['reached_points'] < 0)
4214  {
4215  $results['reached_points'] = 0;
4216  }
4217  if ($pass_reached < 0) $pass_reached = 0;
4218  }
4219  $found['pass']['total_max_points'] = $pass_max;
4220  $found['pass']['total_reached_points'] = $pass_reached;
4221  $found['pass']['percent'] = ($pass_max > 0) ? $pass_reached / $pass_max : 0;
4222  $found["test"]["total_max_points"] = $results['max_points'];
4223  $found["test"]["total_reached_points"] = $results['reached_points'];
4224  $found["test"]["result_pass"] = $results['pass'];
4225  if ((!$total_reached_points) or (!$total_max_points))
4226  {
4227  $percentage = 0.0;
4228  }
4229  else
4230  {
4231  $percentage = ($total_reached_points / $total_max_points) * 100.0;
4232  if ($percentage < 0) $percentage = 0.0;
4233  }
4234  $found["test"]["passed"] = $results['passed'];
4235  return $found;
4236  }
4237 
4244  function evalTotalPersons()
4245  {
4246  global $ilDB;
4247 
4248  $result = $ilDB->queryF("SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s",
4249  array('integer'),
4250  array($this->getTestId())
4251  );
4252  $row = $ilDB->fetchAssoc($result);
4253  return $row["total"];
4254  }
4255 
4262  function getCompleteWorkingTime($user_id)
4263  {
4264  global $ilDB;
4265 
4266  $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",
4267  array('integer','integer'),
4268  array($this->getTestId(), $user_id)
4269  );
4270  $time = 0;
4271  while ($row = $ilDB->fetchAssoc($result))
4272  {
4273  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4274  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4275  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4276  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4277  $time += ($epoch_2 - $epoch_1);
4278  }
4279  return $time;
4280  }
4281 
4289  {
4290  return $this->_getCompleteWorkingTimeOfParticipants($this->getTestId());
4291  }
4292 
4301  {
4302  global $ilDB;
4303 
4304  $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",
4305  array('integer'),
4306  array($test_id)
4307  );
4308  $time = 0;
4309  $times = array();
4310  while ($row = $ilDB->fetchAssoc($result))
4311  {
4312  if (!array_key_exists($row["active_fi"], $times))
4313  {
4314  $times[$row["active_fi"]] = 0;
4315  }
4316  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4317  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4318  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4319  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4320  $times[$row["active_fi"]] += ($epoch_2 - $epoch_1);
4321  }
4322  return $times;
4323  }
4324 
4332  {
4333  global $ilDB;
4334 
4335  $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",
4336  array('integer','integer'),
4337  array($this->getTestId(), $active_id)
4338  );
4339  $time = 0;
4340  while ($row = $ilDB->fetchAssoc($result))
4341  {
4342  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4343  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4344  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4345  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4346  $time += ($epoch_2 - $epoch_1);
4347  }
4348  return $time;
4349  }
4350 
4358  {
4359  global $ilDB;
4360 
4361  $result = $ilDB->queryF("SELECT * FROM tst_times WHERE active_fi = %s AND pass = %s ORDER BY started",
4362  array('integer','integer'),
4363  array($active_id, $pass)
4364  );
4365  $time = 0;
4366  while ($row = $ilDB->fetchAssoc($result))
4367  {
4368  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4369  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4370  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4371  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4372  $time += ($epoch_2 - $epoch_1);
4373  }
4374  return $time;
4375  }
4376 
4384  function getVisitTimeOfParticipant($active_id)
4385  {
4386  return ilObjTest::_getVisitTimeOfParticipant($this->getTestId(), $active_id);
4387  }
4388 
4397  function _getVisitTimeOfParticipant($test_id, $active_id)
4398  {
4399  global $ilDB;
4400 
4401  $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",
4402  array('integer','integer'),
4403  array($test_id, $active_id)
4404  );
4405  $firstvisit = 0;
4406  $lastvisit = 0;
4407  while ($row = $ilDB->fetchAssoc($result))
4408  {
4409  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches);
4410  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4411  if ($firstvisit == 0 || $epoch_1 < $firstvisit) $firstvisit = $epoch_1;
4412  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["finished"], $matches);
4413  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4414  if ($epoch_2 > $lastvisit) $lastvisit = $epoch_2;
4415  }
4416  return array("firstvisit" => $firstvisit, "lastvisit" => $lastvisit);
4417  }
4418 
4425  function &evalStatistical($active_id)
4426  {
4427  global $ilDB;
4428 // global $ilBench;
4429  $pass = ilObjTest::_getResultPass($active_id);
4430  $test_result =& $this->getTestResult($active_id, $pass);
4431  $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",
4432  array('integer'),
4433  array($active_id)
4434  );
4435  $times = array();
4436  $first_visit = 0;
4437  $last_visit = 0;
4438  while ($row = $ilDB->fetchObject($result))
4439  {
4440  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
4441  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4442  if (!$first_visit) {
4443  $first_visit = $epoch_1;
4444  }
4445  if ($epoch_1 < $first_visit) {
4446  $first_visit = $epoch_1;
4447  }
4448  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
4449  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
4450  if (!$last_visit) {
4451  $last_visit = $epoch_2;
4452  }
4453  if ($epoch_2 > $last_visit) {
4454  $last_visit = $epoch_2;
4455  }
4456  $times[$row->active_fi] += ($epoch_2 - $epoch_1);
4457  }
4458  $max_time = 0;
4459  foreach ($times as $key => $value) {
4460  $max_time += $value;
4461  }
4462  if ((!$test_result["test"]["total_reached_points"]) or (!$test_result["test"]["total_max_points"]))
4463  {
4464  $percentage = 0.0;
4465  }
4466  else
4467  {
4468  $percentage = ($test_result["test"]["total_reached_points"] / $test_result["test"]["total_max_points"]) * 100.0;
4469  if ($percentage < 0) $percentage = 0.0;
4470  }
4471  $mark_obj = $this->mark_schema->getMatchingMark($percentage);
4472  $first_date = getdate($first_visit);
4473  $last_date = getdate($last_visit);
4474  $qworkedthrough = 0;
4475  foreach ($test_result as $key => $value)
4476  {
4477  if (preg_match("/\d+/", $key))
4478  {
4479  $qworkedthrough += $value["workedthrough"];
4480  }
4481  }
4482  if (!$qworkedthrough)
4483  {
4484  $atimeofwork = 0;
4485  }
4486  else
4487  {
4488  $atimeofwork = $max_time / $qworkedthrough;
4489  }
4490  $result_mark = "";
4491  $passed = "";
4492  if ($mark_obj)
4493  {
4494  $result_mark = $mark_obj->getShortName();
4495  if ($mark_obj->getPassed())
4496  {
4497  $passed = 1;
4498  }
4499  else
4500  {
4501  $passed = 0;
4502  }
4503  }
4504  $percent_worked_through = 0;
4505  if (count($this->questions))
4506  {
4507  $percent_worked_through = $qworkedthrough / count($this->questions);
4508  }
4509  $result_array = array(
4510  "qworkedthrough" => $qworkedthrough,
4511  "qmax" => count($this->questions),
4512  "pworkedthrough" => $percent_worked_through,
4513  "timeofwork" => $max_time,
4514  "atimeofwork" => $atimeofwork,
4515  "firstvisit" => $first_date,
4516  "lastvisit" => $last_date,
4517  "resultspoints" => $test_result["test"]["total_reached_points"],
4518  "maxpoints" => $test_result["test"]["total_max_points"],
4519  "resultsmarks" => $result_mark,
4520  "passed" => $passed,
4521  "distancemedian" => "0"
4522  );
4523  foreach ($test_result as $key => $value)
4524  {
4525  if (preg_match("/\d+/", $key))
4526  {
4527  $result_array[$key] = $value;
4528  }
4529  }
4530  return $result_array;
4531  }
4532 
4541  {
4542  $totalpoints_array = array();
4543  $all_users =& $this->evalTotalParticipantsArray();
4544  foreach ($all_users as $active_id => $user_name)
4545  {
4546  $test_result =& $this->getTestResult($active_id);
4547  $reached = $test_result["test"]["total_reached_points"];
4548  $total = $test_result["test"]["total_max_points"];
4549  $percentage = $total != 0 ? $reached/$total : 0;
4550  $mark = $this->mark_schema->getMatchingMark($percentage*100.0);
4551  if ($mark)
4552  {
4553  if ($mark->getPassed())
4554  {
4555  array_push($totalpoints_array, $test_result["test"]["total_reached_points"]);
4556  }
4557  }
4558  }
4559  return $totalpoints_array;
4560  }
4561 
4568  function &getParticipants()
4569  {
4570  global $ilDB;
4571  $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",
4572  array('integer'),
4573  array($this->getTestId())
4574  );
4575  $persons_array = array();
4576  while ($row = $ilDB->fetchAssoc($result))
4577  {
4578  $name = $this->lng->txt("unknown");
4579  $fullname = $this->lng->txt("unknown");
4580  $login = "";
4581  if (!$this->getAnonymity())
4582  {
4583  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4584  {
4585  $name = $this->lng->txt("deleted_user");
4586  $fullname = $this->lng->txt("deleted_user");
4587  $login = $this->lng->txt("unknown");
4588  }
4589  else
4590  {
4591  $login = $row["login"];
4592  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4593  {
4594  $name = $this->lng->txt("unknown");
4595  $fullname = $this->lng->txt("unknown");
4596  }
4597  else
4598  {
4599  $name = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]);
4600  $fullname = trim($row["title"] . " " . $row["firstname"] . " " . $row["lastname"]);
4601  }
4602  }
4603  }
4604  $persons_array[$row["active_id"]] = array(
4605  "name" => $name,
4606  "fullname" => $fullname,
4607  "login" => $login
4608  );
4609  }
4610  return $persons_array;
4611  }
4612 
4619  function &evalTotalPersonsArray($name_sort_order = "asc")
4620  {
4621  global $ilDB;
4622  $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),
4623  array('integer'),
4624  array($this->getTestId())
4625  );
4626  $persons_array = array();
4627  while ($row = $ilDB->fetchAssoc($result))
4628  {
4629  if ($this->getAnonymity())
4630  {
4631  $persons_array[$row["active_id"]] = $this->lng->txt("unknown");
4632  }
4633  else
4634  {
4635  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4636  {
4637  $persons_array[$row["active_id"]] = $this->lng->txt("deleted_user");
4638  }
4639  else
4640  {
4641  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4642  {
4643  $persons_array[$row["active_id"]] = $row["lastname"];
4644  }
4645  else
4646  {
4647  $persons_array[$row["active_id"]] = trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]);
4648  }
4649  }
4650  }
4651  }
4652  return $persons_array;
4653  }
4654 
4661  function &evalTotalParticipantsArray($name_sort_order = "asc")
4662  {
4663  global $ilDB;
4664  $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),
4665  array('integer'),
4666  array($this->getTestId())
4667  );
4668  $persons_array = array();
4669  while ($row = $ilDB->fetchAssoc($result))
4670  {
4671  if ($this->getAnonymity())
4672  {
4673  $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("unknown"));
4674  }
4675  else
4676  {
4677  if (strlen($row["firstname"].$row["lastname"].$row["title"]) == 0)
4678  {
4679  $persons_array[$row["active_id"]] = array("name" => $this->lng->txt("deleted_user"));
4680  }
4681  else
4682  {
4683  if ($row["user_fi"] == ANONYMOUS_USER_ID)
4684  {
4685  $persons_array[$row["active_id"]] = array("name" => $row["lastname"]);
4686  }
4687  else
4688  {
4689  $persons_array[$row["active_id"]] = array("name" => trim($row["lastname"] . ", " . $row["firstname"] . " " . $row["title"]), "login" => $row["login"]);
4690  }
4691  }
4692  }
4693  }
4694  return $persons_array;
4695  }
4696 
4704  {
4705  global $ilDB;
4706 
4707  $result = $ilDB->queryF("SELECT COUNT(active_id) total FROM tst_active WHERE test_fi = %s AND submitted = %s",
4708  array('integer', 'integer'),
4709  array($this->getTestId(), 1)
4710  );
4711  $row = $ilDB->fetchAssoc($result);
4712  return $row["total"];
4713  }
4714 
4721  function &getQuestionsOfTest($active_id)
4722  {
4723  global $ilDB;
4724  if ($this->isRandomTest())
4725  {
4726  $ilDB->setLimit($this->getQuestionCount(), 0);
4727  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " .
4728  "tst_test_rnd_qst.pass, qpl_questions.points " .
4729  "FROM tst_test_rnd_qst, qpl_questions " .
4730  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4731  "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence",
4732  array('integer'),
4733  array($active_id)
4734  );
4735  }
4736  else
4737  {
4738  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4739  "qpl_questions.points " .
4740  "FROM tst_test_question, tst_active, qpl_questions " .
4741  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4742  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi",
4743  array('integer'),
4744  array($active_id)
4745  );
4746  }
4747  $qtest = array();
4748  if ($result->numRows())
4749  {
4750  while ($row = $ilDB->fetchAssoc($result))
4751  {
4752  array_push($qtest, $row);
4753  }
4754  }
4755  return $qtest;
4756  }
4757 
4764  function &getQuestionsOfPass($active_id, $pass)
4765  {
4766  global $ilDB;
4767  if ($this->isRandomTest())
4768  {
4769  $ilDB->setLimit($this->getQuestionCount(), 0);
4770  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, " .
4771  "qpl_questions.points " .
4772  "FROM tst_test_rnd_qst, qpl_questions " .
4773  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4774  "AND tst_test_rnd_qst.active_fi = %s AND tst_test_rnd_qst.pass = %s " .
4775  "ORDER BY tst_test_rnd_qst.sequence",
4776  array('integer', 'integer'),
4777  array($active_id, $pass)
4778  );
4779  }
4780  else
4781  {
4782  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4783  "qpl_questions.points " .
4784  "FROM tst_test_question, tst_active, qpl_questions " .
4785  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4786  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi",
4787  array('integer'),
4788  array($active_id)
4789  );
4790  }
4791  $qpass = array();
4792  if ($result->numRows())
4793  {
4794  while ($row = $ilDB->fetchAssoc($result))
4795  {
4796  array_push($qpass, $row);
4797  }
4798  }
4799  return $qpass;
4800  }
4801 
4803  {
4804  global $ilDB;
4805  include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php";
4806  include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php";
4807  include_once "./Modules/Test/classes/class.ilTestEvaluationData.php";
4808  $data = new ilTestEvaluationData($this);
4809  $result = $ilDB->queryF("SELECT tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
4810  "qpl_questions.points maxpoints " .
4811  "FROM tst_test_result, qpl_questions, tst_active " .
4812  "WHERE tst_active.active_id = tst_test_result.active_fi " .
4813  "AND qpl_questions.question_id = tst_test_result.question_fi " .
4814  "AND tst_active.test_fi = %s " .
4815  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
4816  array('integer'),
4817  array($this->getTestId())
4818  );
4819  $pass = NULL;
4820  $checked = array();
4821  $datasets = 0;
4822  while ($row = $ilDB->fetchAssoc($result))
4823  {
4824  $data->getParticipant($row["active_fi"])->getPass($row["pass"])->addAnsweredQuestion($row["original_id"] ? $row["original_id"] : $row["question_fi"], $row["maxpoints"], $row["points"]);
4825  }
4826 
4827  foreach (array_keys($data->getParticipants()) as $active_id)
4828  {
4829  if ($this->isRandomTest())
4830  {
4831  for ($testpass = 0; $testpass <= $data->getParticipant($active_id)->getLastPass(); $testpass++)
4832  {
4833  $ilDB->setLimit($this->getQuestionCount(), 0);
4834  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.sequence, tst_test_rnd_qst.question_fi, qpl_questions.original_id, " .
4835  "tst_test_rnd_qst.pass, qpl_questions.points, qpl_questions.title " .
4836  "FROM tst_test_rnd_qst, qpl_questions " .
4837  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id " .
4838  "AND tst_test_rnd_qst.pass = %s " .
4839  "AND tst_test_rnd_qst.active_fi = %s ORDER BY tst_test_rnd_qst.sequence",
4840  array('integer','integer'),
4841  array($testpass, $active_id)
4842  );
4843  if ($result->numRows())
4844  {
4845  while ($row = $ilDB->fetchAssoc($result))
4846  {
4847  $tpass = array_key_exists("pass", $row) ? $row["pass"] : 0;
4848  $data->getParticipant($active_id)->addQuestion($row["original_id"] ? $row["original_id"] : $row["question_fi"], $row["question_fi"], $row["points"], $row["sequence"], $tpass);
4849  $data->addQuestionTitle($row["original_id"] ? $row["original_id"] : $row["question_fi"], $row["title"]);
4850  }
4851  }
4852  }
4853  }
4854  else
4855  {
4856  $result = $ilDB->queryF("SELECT tst_test_question.sequence, tst_test_question.question_fi, " .
4857  "qpl_questions.points, qpl_questions.title, qpl_questions.original_id " .
4858  "FROM tst_test_question, tst_active, qpl_questions " .
4859  "WHERE tst_test_question.question_fi = qpl_questions.question_id " .
4860  "AND tst_active.active_id = %s AND tst_active.test_fi = tst_test_question.test_fi ORDER BY tst_test_question.sequence",
4861  array('integer'),
4862  array($active_id)
4863  );
4864  if ($result->numRows())
4865  {
4866  $questionsbysequence = array();
4867  while ($row = $ilDB->fetchAssoc($result))
4868  {
4869  $questionsbysequence[$row["sequence"]] = $row;
4870  }
4871  $seqresult = $ilDB->queryF("SELECT * FROM tst_sequence WHERE active_fi = %s",
4872  array('integer'),
4873  array($active_id)
4874  );
4875  while ($seqrow = $ilDB->fetchAssoc($seqresult))
4876  {
4877  $questionsequence = unserialize($seqrow["sequence"]);
4878  foreach ($questionsequence as $sidx => $seq)
4879  {
4880  $qsid = $questionsbysequence[$seq]["original_id"] ? $questionsbysequence[$seq]["original_id"] : $questionsbysequence[$seq]["question_fi"];
4881  $data->getParticipant($active_id)->addQuestion($qsid, $questionsbysequence[$seq]["question_fi"], $questionsbysequence[$seq]["points"], $sidx + 1, $seqrow["pass"]);
4882  $data->addQuestionTitle($qsid, $questionsbysequence[$seq]["title"]);
4883  }
4884  }
4885  }
4886  }
4887  }
4888 
4889  if ($this->ects_output)
4890  {
4891  $passed_array =& $this->getTotalPointsPassedArray();
4892  }
4893  foreach (array_keys($data->getParticipants()) as $active_id)
4894  {
4895  $percentage = $data->getParticipant($active_id)->getReachedPointsInPercent();
4896  $mark = $this->mark_schema->getMatchingMark($percentage);
4897  if (is_object($mark))
4898  {
4899  $data->getParticipant($active_id)->setMark($mark->getShortName());
4900  $data->getParticipant($active_id)->setMarkOfficial($mark->getOfficialName());
4901  $data->getParticipant($active_id)->setPassed($mark->getPassed());
4902  }
4903  if ($this->ects_output)
4904  {
4905  $ects_mark = $this->getECTSGrade($passed_array, $data->getParticipant($active_id)->getReached(), $data->getParticipant($active_id)->getMaxPoints());
4906  $data->getParticipant($active_id)->setECTSMark($ects_mark);
4907  }
4908  $visitingTime =& $this->getVisitTimeOfParticipant($active_id);
4909  $data->getParticipant($active_id)->setFirstVisit($visitingTime["firstvisit"]);
4910  $data->getParticipant($active_id)->setLastVisit($visitingTime["lastvisit"]);
4911  }
4912  return $data;
4913  }
4914 
4916  {
4917  global $ilDB;
4918  $random = ilObjTest::_lookupRandomTestFromActiveId($active_id);
4919  if ($random)
4920  {
4921  $result = $ilDB->queryF("SELECT tst_test_rnd_qst.pass, COUNT(tst_test_rnd_qst.question_fi) qcount, " .
4922  "SUM(qpl_questions.points) qsum FROM tst_test_rnd_qst, qpl_questions " .
4923  "WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id AND " .
4924  "tst_test_rnd_qst.active_fi = %s and pass = %s GROUP BY tst_test_rnd_qst.active_fi, " .
4925  "tst_test_rnd_qst.pass",
4926  array('integer', 'integer'),
4927  array($active_id, $pass)
4928  );
4929  }
4930  else
4931  {
4932  $result = $ilDB->queryF("SELECT COUNT(tst_test_question.question_fi) qcount, " .
4933  "SUM(qpl_questions.points) qsum FROM tst_test_question, qpl_questions, tst_active " .
4934  "WHERE tst_test_question.question_fi = qpl_questions.question_id AND tst_test_question.test_fi = tst_active.test_fi AND " .
4935  "tst_active.active_id = %s GROUP BY tst_test_question.test_fi",
4936  array('integer'),
4937  array($active_id)
4938  );
4939  }
4940  if ($result->numRows())
4941  {
4942  $row = $ilDB->fetchAssoc($result);
4943  return array("count" => $row["qcount"], "points" => $row["qsum"]);
4944  }
4945  else
4946  {
4947  return array("count" => 0, "points" => 0);
4948  }
4949  }
4950 
4951  function &getCompleteEvaluationData($withStatistics = TRUE, $filterby = "", $filtertext = "")
4952  {
4953  include_once "./Modules/Test/classes/class.ilTestEvaluationData.php";
4954  include_once "./Modules/Test/classes/class.ilTestEvaluationPassData.php";
4955  include_once "./Modules/Test/classes/class.ilTestEvaluationUserData.php";
4957  if ($withStatistics)
4958  {
4959  $data->calculateStatistics();
4960  }
4961  $data->setFilter($filterby, $filtertext);
4962  return $data;
4963  }
4964 
4972  {
4973  return $this->_evalResultsOverview($this->getTestId());
4974  }
4975 
4983  {
4984  global $ilDB;
4985 
4986  $result = $ilDB->queryF("SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " .
4987  "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
4988  "qpl_questions.points maxpoints " .
4989  "FROM tst_test_result, qpl_questions, tst_active " .
4990  "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " .
4991  "WHERE tst_active.active_id = tst_test_result.active_fi " .
4992  "AND qpl_questions.question_id = tst_test_result.question_fi " .
4993  "AND tst_active.test_fi = %s " .
4994  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
4995  array('integer'),
4996  array($test_id)
4997  );
4998  $overview = array();
4999  while ($row = $ilDB->fetchAssoc($result))
5000  {
5001  if (!array_key_exists($row["active_fi"], $overview))
5002  {
5003  $overview[$row["active_fi"]] = array();
5004  $overview[$row["active_fi"]]["firstname"] = $row["firstname"];
5005  $overview[$row["active_fi"]]["lastname"] = $row["lastname"];
5006  $overview[$row["active_fi"]]["title"] = $row["title"];
5007  $overview[$row["active_fi"]]["login"] = $row["login"];
5008  $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"];
5009  $overview[$row["active_fi"]]["started"] = $row["started"];
5010  $overview[$row["active_fi"]]["finished"] = $row["finished"];
5011  }
5012  if (!array_key_exists($row["pass"], $overview[$row["active_fi"]]))
5013  {
5014  $overview[$row["active_fi"]][$row["pass"]] = array();
5015  $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0;
5016  $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"];
5017  }
5018  array_push($overview[$row["active_fi"]][$row["pass"]], $row);
5019  $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"];
5020  }
5021  return $overview;
5022  }
5023 
5031  function &evalResultsOverviewOfParticipant($active_id)
5032  {
5033  global $ilDB;
5034 
5035  $result = $ilDB->queryF("SELECT usr_data.usr_id, usr_data.firstname, usr_data.lastname, usr_data.title, usr_data.login, " .
5036  "tst_test_result.*, qpl_questions.original_id, qpl_questions.title questiontitle, " .
5037  "qpl_questions.points maxpoints " .
5038  "FROM tst_test_result, qpl_questions, tst_active " .
5039  "LEFT JOIN usr_data ON tst_active.user_fi = usr_data.usr_id " .
5040  "WHERE tst_active.active_id = tst_test_result.active_fi " .
5041  "AND qpl_questions.question_id = tst_test_result.question_fi " .
5042  "AND tst_active.test_fi = %s AND tst_active.active_id = %s" .
5043  "ORDER BY tst_active.active_id, tst_test_result.pass, tst_test_result.tstamp",
5044  array('integer', 'integer'),
5045  array($this->getTestId(), $active_id)
5046  );
5047  $overview = array();
5048  while ($row = $ilDB->fetchAssoc($result))
5049  {
5050  if (!array_key_exists($row["active_fi"], $overview))
5051  {
5052  $overview[$row["active_fi"]] = array();
5053  $overview[$row["active_fi"]]["firstname"] = $row["firstname"];
5054  $overview[$row["active_fi"]]["lastname"] = $row["lastname"];
5055  $overview[$row["active_fi"]]["title"] = $row["title"];
5056  $overview[$row["active_fi"]]["login"] = $row["login"];
5057  $overview[$row["active_fi"]]["usr_id"] = $row["usr_id"];
5058  $overview[$row["active_fi"]]["started"] = $row["started"];
5059  $overview[$row["active_fi"]]["finished"] = $row["finished"];
5060  }
5061  if (!array_key_exists($row["pass"], $overview[$row["active_fi"]]))
5062  {
5063  $overview[$row["active_fi"]][$row["pass"]] = array();
5064  $overview[$row["active_fi"]][$row["pass"]]["reached"] = 0;
5065  $overview[$row["active_fi"]][$row["pass"]]["maxpoints"] = $row["maxpoints"];
5066  }
5067  array_push($overview[$row["active_fi"]][$row["pass"]], $row);
5068  $overview[$row["active_fi"]][$row["pass"]]["reached"] += $row["points"];
5069  }
5070  return $overview;
5071  }
5072 
5084  function buildName($user_id, $firstname, $lastname, $title)
5085  {
5086  $name = "";
5087  if (strlen($firstname.$lastname.$title) == 0)
5088  {
5089  $name = $this->lng->txt("deleted_user");
5090  }
5091  else
5092  {
5093  if ($user_id == ANONYMOUS_USER_ID)
5094  {
5095  $name = $lastname;
5096  }
5097  else
5098  {
5099  $name = trim($lastname . ", " . $firstname . " " . $title);
5100  }
5101  if ($this->getAnonymity())
5102  {
5103  $name = $this->lng->txt("anonymous");
5104  }
5105  }
5106  return $name;
5107  }
5108 
5121  function _buildName($is_anonymous, $user_id, $firstname, $lastname, $title)
5122  {
5123  global $lng;
5124  $name = "";
5125  if (strlen($firstname.$lastname.$title) == 0)
5126  {
5127  $name = $lng->txt("deleted_user");
5128  }
5129  else
5130  {
5131  if ($user_id == ANONYMOUS_USER_ID)
5132  {
5133  $name = $lastname;
5134  }
5135  else
5136  {
5137  $name = trim($lastname . ", " . $firstname . " " . $title);
5138  }
5139  if ($is_anonymous)
5140  {
5141  $name = $lng->txt("anonymous");
5142  }
5143  }
5144  return $name;
5145  }
5146 
5154  {
5155  global $ilDB;
5156 
5157  $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",
5158  array('integer'),
5159  array($this->getTestId())
5160  );
5161  $times = array();
5162  while ($row = $ilDB->fetchObject($result))
5163  {
5164  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->started, $matches);
5165  $epoch_1 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5166  preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row->finished, $matches);
5167  $epoch_2 = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5168  $times[$row->active_fi] += ($epoch_2 - $epoch_1);
5169  }
5170  $max_time = 0;
5171  $counter = 0;
5172  foreach ($times as $key => $value)
5173  {
5174  $max_time += $value;
5175  $counter++;
5176  }
5177  if ($counter)
5178  {
5179  $average_time = round($max_time / $counter);
5180  }
5181  else
5182  {
5183  $average_time = 0;
5184  }
5185  return $average_time;
5186  }
5187 
5194  function &getAvailableQuestionpools($use_object_id = false, $equal_points = false, $could_be_offline = false, $show_path = FALSE, $with_questioncount = FALSE, $permission = "read")
5195  {
5196  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5197  return ilObjQuestionPool::_getAvailableQuestionpools($use_object_id, $equal_points, $could_be_offline, $show_path, $with_questioncount, $permission);
5198  }
5199 
5207  {
5208  $time_in_seconds = 0;
5209  foreach ($this->questions as $question_id)
5210  {
5211  $question =& ilObjTest::_instanciateQuestion($question_id);
5212  $est_time = $question->getEstimatedWorkingTime();
5213  $time_in_seconds += $est_time["h"] * 3600 + $est_time["m"] * 60 + $est_time["s"];
5214  }
5215  $hours = (int)($time_in_seconds / 3600) ;
5216  $time_in_seconds = $time_in_seconds - ($hours * 3600);
5217  $minutes = (int)($time_in_seconds / 60);
5218  $time_in_seconds = $time_in_seconds - ($minutes * 60);
5219  $result = array("hh" => $hours, "mm" => $minutes, "ss" => $time_in_seconds);
5220  return $result;
5221  }
5222 
5231  public function generateRandomPass($nr, $qpls, $pass = NULL)
5232  {
5233  global $ilDB;
5234  $qplids = array();
5235  foreach ($qpls as $arr) array_push($qplids, $arr['qpl']);
5236  $result = $ilDB->queryF('SELECT * FROM tst_rnd_cpy WHERE tst_fi = %s AND ' . $ilDB->in('qpl_fi', $qplids, false, 'integer'),
5237  array('integer'),
5238  array($this->getTestId())
5239  );
5240  if ($result->numRows())
5241  {
5242  $ids = array();
5243  while ($row = $ilDB->fetchAssoc($result)) array_push($ids, $row['qst_fi']);
5244  $nr = ($nr > count($ids)) ? count($ids) : $nr;
5245  if ($nr == 0) return array();
5246  $rand_keys = array_rand($ids, $nr);
5247  $selection = array();
5248  if (is_array($rand_keys))
5249  {
5250  foreach ($rand_keys as $key)
5251  {
5252  $selection[$ids[$key]] = $ids[$key];
5253  }
5254  }
5255  else
5256  {
5257  $selection[$ids[$rand_keys]] = $ids[$rand_keys];
5258  }
5259  return $selection;
5260  }
5261  else
5262  {
5263  // old style random questions
5264  return $this->randomSelectQuestions($nr, 0, 1, $qplids, $pass);
5265  }
5266  }
5267 
5278  function randomSelectQuestions($nr_of_questions, $questionpool, $use_obj_id = 0, $qpls = "", $pass = NULL)
5279  {
5280  global $rbacsystem;
5281  global $ilDB;
5282 
5283  // retrieve object id instead of ref id if necessary
5284  if (($questionpool != 0) && (!$use_obj_id)) $questionpool = ilObject::_lookupObjId($questionpool);
5285 
5286  // get original ids of all existing questions in the test
5287  $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",
5288  array("integer"),
5289  array($this->getTestId())
5290  );
5291  $original_ids = array();
5292  $paramtypes = array();
5293  $paramvalues = array();
5294  while ($row = $ilDB->fetchAssoc($result))
5295  {
5296  array_push($original_ids, $row['original_id']);
5297  }
5298 
5299  $available = "";
5300  // get a list of all available questionpools
5301  if (($questionpool == 0) && (!is_array($qpls)))
5302  {
5303  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5304  $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())));
5305  if (count($available_pools))
5306  {
5307  $available = " AND " . $ilDB->in('obj_fi', $available_pools, false, 'integer');
5308  }
5309  else
5310  {
5311  return array();
5312  }
5313  }
5314 
5315  $constraint_qpls = "";
5316  $result_array = array();
5317  if ($questionpool == 0)
5318  {
5319  if (is_array($qpls))
5320  {
5321  if (count($qpls) > 0)
5322  {
5323  $constraint_qpls = " AND " . $ilDB->in('obj_fi', $qpls, false, 'integer');
5324  }
5325  }
5326  }
5327 
5328  $original_clause = "";
5329  if (count($original_ids))
5330  {
5331  $original_clause = " AND " . $ilDB->in('question_id', $original_ids, true, 'integer');
5332  }
5333 
5334  if ($questionpool == 0)
5335  {
5336  $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",
5337  array('integer', 'text'),
5338  array(0, "1")
5339  );
5340  }
5341  else
5342  {
5343  $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",
5344  array('integer','integer', 'text'),
5345  array($questionpool, 0, "1")
5346  );
5347  }
5348  $found_ids = array();
5349  while ($row = $ilDB->fetchAssoc($result))
5350  {
5351  array_push($found_ids, $row['question_id']);
5352  }
5353  $nr_of_questions = ($nr_of_questions > count($found_ids)) ? count($found_ids) : $nr_of_questions;
5354  if ($nr_of_questions == 0) return array();
5355  $rand_keys = array_rand($found_ids, $nr_of_questions);
5356  $result = array();
5357  if (is_array($rand_keys))
5358  {
5359  foreach ($rand_keys as $key)
5360  {
5361  $result[$found_ids[$key]] = $found_ids[$key];
5362  }
5363  }
5364  else
5365  {
5366  $result[$found_ids[$rand_keys]] = $found_ids[$rand_keys];
5367  }
5368  return $result;
5369  }
5370 
5377  function getImagePath()
5378  {
5379  return CLIENT_WEB_DIR . "/assessment/" . $this->getId() . "/images/";
5380  }
5381 
5388  function getImagePathWeb()
5389  {
5390  include_once "./Services/Utilities/classes/class.ilUtil.php";
5391  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/" . $this->getId() . "/images/";
5393  }
5394 
5403  function &createQuestionGUI($question_type, $question_id = -1)
5404  {
5405  if ((!$question_type) and ($question_id > 0))
5406  {
5407  $question_type = $this->getQuestionType($question_id);
5408  }
5409  if (!strlen($question_type)) return null;
5410  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
5411  assQuestion::_includeClass($question_type, 1);
5412  $question_type_gui = $question_type . "GUI";
5413  $question = new $question_type_gui();
5414  if ($question_id > 0)
5415  {
5416  $question->object->loadFromDb($question_id);
5417  }
5418  return $question;
5419  }
5420 
5428  function &_instanciateQuestion($question_id)
5429  {
5430  if (strcmp($question_id, "") != 0)
5431  {
5432  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
5433  return assQuestion::_instanciateQuestion($question_id);
5434  }
5435  }
5436 
5445  function moveQuestions($move_questions, $target_index, $insert_mode)
5446  {
5447  $this->questions = array_values($this->questions);
5448  $array_pos = array_search($target_index, $this->questions);
5449  if ($insert_mode == 0)
5450  {
5451  $part1 = array_slice($this->questions, 0, $array_pos);
5452  $part2 = array_slice($this->questions, $array_pos);
5453  }
5454  else if ($insert_mode == 1)
5455  {
5456  $part1 = array_slice($this->questions, 0, $array_pos + 1);
5457  $part2 = array_slice($this->questions, $array_pos + 1);
5458  }
5459  foreach ($move_questions as $question_id)
5460  {
5461  if (!(array_search($question_id, $part1) === FALSE))
5462  {
5463  unset($part1[array_search($question_id, $part1)]);
5464  }
5465  if (!(array_search($question_id, $part2) === FALSE))
5466  {
5467  unset($part2[array_search($question_id, $part2)]);
5468  }
5469  }
5470  $part1 = array_values($part1);
5471  $part2 = array_values($part2);
5472  $new_array = array_values(array_merge($part1, $move_questions, $part2));
5473  $this->questions = array();
5474  $counter = 1;
5475  foreach ($new_array as $question_id)
5476  {
5477  $this->questions[$counter] = $question_id;
5478  $counter++;
5479  }
5480  $this->saveQuestionsToDb();
5481  }
5482 
5483 
5492  {
5493  if ($this->getStartingTime())
5494  {
5495  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getStartingTime(), $matches))
5496  {
5497  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5498  $now = mktime();
5499  if ($now < $epoch_time)
5500  {
5501  // starting time not reached
5502  return false;
5503  }
5504  }
5505  }
5506  return true;
5507  }
5508 
5517  {
5518  if ($this->getEndingTime())
5519  {
5520  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getEndingTime(), $matches))
5521  {
5522  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
5523  $now = mktime();
5524  if ($now > $epoch_time)
5525  {
5526  // ending time reached
5527  return true;
5528  }
5529  }
5530  }
5531  return false;
5532  }
5533 
5539  function getAvailableQuestions($arrFilter, $completeonly = 0)
5540  {
5541  global $ilUser;
5542  global $ilDB;
5543 
5544  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5545  $available_pools = array_keys(ilObjQuestionPool::_getAvailableQuestionpools($use_object_id = TRUE, $equal_points = FALSE, $could_be_offline = FALSE, $showPath = FALSE, $with_questioncount = FALSE));
5546  $available = "";
5547  if (count($available_pools))
5548  {
5549  $available = " AND " . $ilDB->in('qpl_questions.obj_fi', $available_pools, false, 'integer');
5550  }
5551  else
5552  {
5553  return array();
5554  }
5555  if ($completeonly)
5556  {
5557  $available .= " AND qpl_questions.complete = " . $ilDB->quote("1", 'text');
5558  }
5559 
5560  $where = "";
5561  if (is_array($arrFilter))
5562  {
5563  if (array_key_exists('title', $arrFilter) && strlen($arrFilter['title']))
5564  {
5565  $where .= " AND " . $ilDB->like('qpl_questions.title', 'text', "%%" . $arrFilter['title'] . "%%");
5566  }
5567  if (array_key_exists('description', $arrFilter) && strlen($arrFilter['description']))
5568  {
5569  $where .= " AND " . $ilDB->like('qpl_questions.description', 'text', "%%" . $arrFilter['description'] . "%%");
5570  }
5571  if (array_key_exists('author', $arrFilter) && strlen($arrFilter['author']))
5572  {
5573  $where .= " AND " . $ilDB->like('qpl_questions.author', 'text', "%%" . $arrFilter['author'] . "%%");
5574  }
5575  if (array_key_exists('type', $arrFilter) && strlen($arrFilter['type']))
5576  {
5577  $where .= " AND qpl_qst_type.type_tag = " . $ilDB->quote($arrFilter['type'], 'text');
5578  }
5579  if (array_key_exists('qpl', $arrFilter) && strlen($arrFilter['qpl']))
5580  {
5581  $where .= " AND " . $ilDB->like('object_data.title', 'text', "%%" . $arrFilter['qpl'] . "%%");
5582  }
5583  }
5584 
5585  $original_ids =& $this->getExistingQuestions();
5586  $original_clause = " qpl_questions.original_id IS NULL";
5587  if (count($original_ids))
5588  {
5589  $original_clause = " qpl_questions.original_id IS NULL AND " . $ilDB->in('qpl_questions.question_id', $original_ids, true, 'integer');
5590  }
5591 
5592  $query_result = $ilDB->query(
5593  "SELECT qpl_questions.*, qpl_questions.tstamp, qpl_qst_type.type_tag, qpl_qst_type.plugin, object_data.title qpl " .
5594  "FROM qpl_questions, qpl_qst_type, object_data WHERE $original_clause $available AND " .
5595  "object_data.obj_id = qpl_questions.obj_fi AND qpl_questions.tstamp > 0 AND " .
5596  "qpl_questions.question_type_fi = qpl_qst_type.question_type_id$where");
5597  $rows = array();
5598  $types = $this->getQuestionTypeTranslations();
5599  if ($query_result->numRows())
5600  {
5601  while ($row = $ilDB->fetchAssoc($query_result))
5602  {
5603  $row['ttype'] = $types[$row['type_tag']];
5604  if ($row["plugin"])
5605  {
5606  if ($this->isPluginActive($row["type_tag"]))
5607  {
5608  array_push($rows, $row);
5609  }
5610  }
5611  else
5612  {
5613  array_push($rows, $row);
5614  }
5615  }
5616  }
5617  return $rows;
5618  }
5619 
5620  public function &getQuestionTypeTranslations()
5621  {
5622  global $ilDB;
5623  global $lng;
5624  global $ilLog;
5625  global $ilPluginAdmin;
5626 
5627  $lng->loadLanguageModule("assessment");
5628  $result = $ilDB->query("SELECT * FROM qpl_qst_type");
5629  $types = array();
5630  while ($row = $ilDB->fetchAssoc($result))
5631  {
5632  if ($row["plugin"] == 0)
5633  {
5634  $types[$row['type_tag']] = $lng->txt($row["type_tag"]);
5635  }
5636  else
5637  {
5638  $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
5639  foreach ($pl_names as $pl_name)
5640  {
5641  $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
5642  if (strcmp($pl->getQuestionType(), $row["type_tag"]) == 0)
5643  {
5644  $types[$row['type_tag']] = $pl->getQuestionTypeTranslation();
5645  }
5646  }
5647  }
5648  }
5649  ksort($types);
5650  return $types;
5651  }
5652 
5659  function fromXML(&$assessment)
5660  {
5661  unset($_SESSION["import_mob_xhtml"]);
5662 
5663  $this->setDescription($assessment->getComment());
5664  $this->setTitle($assessment->getTitle());
5665 
5666  foreach ($assessment->objectives as $objectives)
5667  {
5668  foreach ($objectives->materials as $material)
5669  {
5670  $this->setIntroduction($this->QTIMaterialToString($material));
5671  }
5672  }
5673  if ($assessment->getPresentationMaterial())
5674  {
5675  $this->setFinalStatement($this->QTIMaterialToString($assessment->getPresentationMaterial()->getMaterial(0)));
5676  }
5677 
5678  foreach ($assessment->assessmentcontrol as $assessmentcontrol)
5679  {
5680  switch ($assessmentcontrol->getSolutionswitch())
5681  {
5682  case "Yes":
5683  $this->setInstantFeedbackSolution(1);
5684  break;
5685  default:
5686  $this->setInstantFeedbackSolution(0);
5687  break;
5688  }
5689  }
5690 
5691  foreach ($assessment->qtimetadata as $metadata)
5692  {
5693  switch ($metadata["label"])
5694  {
5695  case "test_type":
5696  // for old tests with a test type
5697  $type = $metadata["entry"];
5698  switch ($type)
5699  {
5700  case 1:
5701  // assessment
5702  $this->setAnonymity(1);
5703  break;
5704  case 2:
5705  // self assessment
5706  break;
5707  case 4:
5708  // online exam
5709  $this->setFixedParticipants(1);
5710  $this->setListOfQuestionsSettings(7);
5711  $this->setShowSolutionPrintview(1);
5712  break;
5713  case 5:
5714  // varying random test
5715  break;
5716  }
5717  break;
5718  case "sequence_settings":
5719  $this->setSequenceSettings($metadata["entry"]);
5720  break;
5721  case "author":
5722  $this->setAuthor($metadata["entry"]);
5723  break;
5724  case "nr_of_tries":
5725  $this->setNrOfTries($metadata["entry"]);
5726  break;
5727  case "kiosk":
5728  $this->setKiosk($metadata["entry"]);
5729  break;
5730  case "showfinalstatement":
5731  $this->setShowFinalStatement($metadata["entry"]);
5732  break;
5733  case "showinfo":
5734  $this->setShowInfo($metadata["entry"]);
5735  break;
5736  case "forcejs":
5737  $this->setForceJS($metadata["entry"]);
5738  break;
5739  case "customstyle":
5740  $this->setCustomStyle($metadata["entry"]);
5741  break;
5742  case "hide_previous_results":
5743  if ($metadata["entry"] == 0)
5744  {
5745  $this->setUsePreviousAnswers(1);
5746  }
5747  else
5748  {
5749  $this->setUsePreviousAnswers(0);
5750  }
5751  break;
5752  case "use_previous_answers":
5753  $this->setUsePreviousAnswers($metadata["entry"]);
5754  break;
5755  case "answer_feedback":
5756  $this->setAnswerFeedback($metadata["entry"]);
5757  break;
5758  case "hide_title_points":
5759  $this->setTitleOutput($metadata["entry"]);
5760  break;
5761  case "title_output":
5762  $this->setTitleOutput($metadata["entry"]);
5763  break;
5764  case "random_test":
5765  $this->setRandomTest($metadata["entry"]);
5766  break;
5767  case "random_question_count":
5768  $this->setRandomQuestionCount($metadata["entry"]);
5769  break;
5770  case "results_presentation":
5771  $this->setResultsPresentation($metadata["entry"]);
5772  break;
5773  case "reset_processing_time":
5774  $this->setResetProcessingTime($metadata["entry"]);
5775  break;
5776  case "instant_verification":
5777  $this->setInstantFeedbackSolution($metadata["entry"]);
5778  break;
5779  case "answer_feedback_points":
5780  $this->setAnswerFeedbackPoints($metadata["entry"]);
5781  break;
5782  case "anonymity":
5783  $this->setAnonymity($metadata["entry"]);
5784  break;
5785  case "show_cancel":
5786  $this->setShowCancel($metadata["entry"]);
5787  break;
5788  case "show_marker":
5789  $this->setShowMarker($metadata["entry"]);
5790  break;
5791  case "fixed_participants":
5792  $this->setFixedParticipants($metadata["entry"]);
5793  break;
5794  case "score_reporting":
5795  $this->setScoreReporting($metadata["entry"]);
5796  break;
5797  case "shuffle_questions":
5798  $this->setShuffleQuestions($metadata["entry"]);
5799  break;
5800  case "count_system":
5801  $this->setCountSystem($metadata["entry"]);
5802  break;
5803  case "mc_scoring":
5804  $this->setMCScoring($metadata["entry"]);
5805  break;
5806  case "mailnotification":
5807  $this->setMailNotification($metadata["entry"]);
5808  break;
5809  case "score_cutting":
5810  $this->setScoreCutting($metadata["entry"]);
5811  break;
5812  case "password":
5813  $this->setPassword($metadata["entry"]);
5814  break;
5815  case "allowedUsers":
5816  $this->setAllowedUsers($metadata["entry"]);
5817  break;
5818  case "allowedUsersTimeGap":
5819  $this->setAllowedUsersTimeGap($metadata["entry"]);
5820  break;
5821  case "pass_scoring":
5822  $this->setPassScoring($metadata["entry"]);
5823  break;
5824  case "show_summary":
5825  $this->setListOfQuestionsSettings($metadata["entry"]);
5826  break;
5827  case "reporting_date":
5828  $iso8601period = $metadata["entry"];
5829  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
5830  {
5831  $this->setReportingDate(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
5832  }
5833  break;
5834  case "processing_time":
5835  $this->setProcessingTime($metadata['entry']);
5836  break;
5837  case "starting_time":
5838  $iso8601period = $metadata["entry"];
5839  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
5840  {
5841  $this->setStartingTime(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
5842  }
5843  break;
5844  case "ending_time":
5845  $iso8601period = $metadata["entry"];
5846  if (preg_match("/P(\d+)Y(\d+)M(\d+)DT(\d+)H(\d+)M(\d+)S/", $iso8601period, $matches))
5847  {
5848  $this->setEndingTime(sprintf("%02d%02d%02d%02d%02d%02d", $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]));
5849  }
5850  break;
5851  }
5852  if (preg_match("/mark_step_\d+/", $metadata["label"]))
5853  {
5854  $xmlmark = $metadata["entry"];
5855  preg_match("/<short>(.*?)<\/short>/", $xmlmark, $matches);
5856  $mark_short = $matches[1];
5857  preg_match("/<official>(.*?)<\/official>/", $xmlmark, $matches);
5858  $mark_official = $matches[1];
5859  preg_match("/<percentage>(.*?)<\/percentage>/", $xmlmark, $matches);
5860  $mark_percentage = $matches[1];
5861  preg_match("/<passed>(.*?)<\/passed>/", $xmlmark, $matches);
5862  $mark_passed = $matches[1];
5863  $this->mark_schema->addMarkStep($mark_short, $mark_official, $mark_percentage, $mark_passed);
5864  }
5865  }
5866  // handle the import of media objects in XHTML code
5867  if (is_array($_SESSION["import_mob_xhtml"]))
5868  {
5869  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
5870  include_once "./Services/RTE/classes/class.ilRTE.php";
5871  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
5872  foreach ($_SESSION["import_mob_xhtml"] as $mob)
5873  {
5874  $importfile = $this->getImportDirectory() . "/" . $_SESSION["tst_import_subdir"] . "/" . $mob["uri"];
5875  if (file_exists($importfile))
5876  {
5877  $media_object =& ilObjMediaObject::_saveTempFileAsMediaObject(basename($importfile), $importfile, FALSE);
5878  ilObjMediaObject::_saveUsage($media_object->getId(), "tst:html", $this->getId());
5879  $this->setIntroduction(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getIntroduction()), 1));
5880  $this->setFinalStatement(ilRTE::_replaceMediaObjectImageSrc(str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $this->getFinalStatement()), 1));
5881  }
5882  else
5883  {
5884  global $ilLog;
5885  $ilLog->write("Error: Could not open XHTML mob file for test introduction during test import. File $importfile does not exist!");
5886  }
5887  }
5888  $this->saveToDb();
5889  }
5890  }
5891 
5898  function toXML()
5899  {
5900  include_once("./classes/class.ilXmlWriter.php");
5901  $a_xml_writer = new ilXmlWriter;
5902  // set xml header
5903  $a_xml_writer->xmlHeader();
5904  $a_xml_writer->xmlSetDtdDef("<!DOCTYPE questestinterop SYSTEM \"ims_qtiasiv1p2p1.dtd\">");
5905  $a_xml_writer->xmlStartTag("questestinterop");
5906 
5907  $attrs = array(
5908  "ident" => "il_".IL_INST_ID."_tst_".$this->getTestId(),
5909  "title" => $this->getTitle()
5910  );
5911  $a_xml_writer->xmlStartTag("assessment", $attrs);
5912  // add qti comment
5913  $a_xml_writer->xmlElement("qticomment", NULL, $this->getDescription());
5914 
5915  // add qti duration
5916  if ($this->enable_processing_time)
5917  {
5918  preg_match("/(\d+):(\d+):(\d+)/", $this->processing_time, $matches);
5919  $a_xml_writer->xmlElement("duration", NULL, sprintf("P0Y0M0DT%dH%dM%dS", $matches[1], $matches[2], $matches[3]));
5920  }
5921 
5922  // add the rest of the preferences in qtimetadata tags, because there is no correspondent definition in QTI
5923  $a_xml_writer->xmlStartTag("qtimetadata");
5924  $a_xml_writer->xmlStartTag("qtimetadatafield");
5925  $a_xml_writer->xmlElement("fieldlabel", NULL, "ILIAS_VERSION");
5926  $a_xml_writer->xmlElement("fieldentry", NULL, $this->ilias->getSetting("ilias_version"));
5927  $a_xml_writer->xmlEndTag("qtimetadatafield");
5928 
5929  // anonymity
5930  $a_xml_writer->xmlStartTag("qtimetadatafield");
5931  $a_xml_writer->xmlElement("fieldlabel", NULL, "anonymity");
5932  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnonymity()));
5933  $a_xml_writer->xmlEndTag("qtimetadatafield");
5934 
5935  // random test
5936  $a_xml_writer->xmlStartTag("qtimetadatafield");
5937  $a_xml_writer->xmlElement("fieldlabel", NULL, "random_test");
5938  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->isRandomTest()));
5939  $a_xml_writer->xmlEndTag("qtimetadatafield");
5940 
5941  // sequence settings
5942  $a_xml_writer->xmlStartTag("qtimetadatafield");
5943  $a_xml_writer->xmlElement("fieldlabel", NULL, "sequence_settings");
5944  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getSequenceSettings());
5945  $a_xml_writer->xmlEndTag("qtimetadatafield");
5946 
5947  // author
5948  $a_xml_writer->xmlStartTag("qtimetadatafield");
5949  $a_xml_writer->xmlElement("fieldlabel", NULL, "author");
5950  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAuthor());
5951  $a_xml_writer->xmlEndTag("qtimetadatafield");
5952 
5953  // reset processing time
5954  $a_xml_writer->xmlStartTag("qtimetadatafield");
5955  $a_xml_writer->xmlElement("fieldlabel", NULL, "reset_processing_time");
5956  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getResetProcessingTime());
5957  $a_xml_writer->xmlEndTag("qtimetadatafield");
5958 
5959  // count system
5960  $a_xml_writer->xmlStartTag("qtimetadatafield");
5961  $a_xml_writer->xmlElement("fieldlabel", NULL, "count_system");
5962  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getCountSystem());
5963  $a_xml_writer->xmlEndTag("qtimetadatafield");
5964 
5965  // multiple choice scoring
5966  $a_xml_writer->xmlStartTag("qtimetadatafield");
5967  $a_xml_writer->xmlElement("fieldlabel", NULL, "mc_scoring");
5968  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMCScoring());
5969  $a_xml_writer->xmlEndTag("qtimetadatafield");
5970 
5971  // multiple choice scoring
5972  $a_xml_writer->xmlStartTag("qtimetadatafield");
5973  $a_xml_writer->xmlElement("fieldlabel", NULL, "score_cutting");
5974  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getScoreCutting());
5975  $a_xml_writer->xmlEndTag("qtimetadatafield");
5976 
5977  // multiple choice scoring
5978  $a_xml_writer->xmlStartTag("qtimetadatafield");
5979  $a_xml_writer->xmlElement("fieldlabel", NULL, "password");
5980  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getPassword());
5981  $a_xml_writer->xmlEndTag("qtimetadatafield");
5982 
5983  // allowed users
5984  $a_xml_writer->xmlStartTag("qtimetadatafield");
5985  $a_xml_writer->xmlElement("fieldlabel", NULL, "allowedUsers");
5986  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAllowedUsers());
5987  $a_xml_writer->xmlEndTag("qtimetadatafield");
5988 
5989  // allowed users time gap
5990  $a_xml_writer->xmlStartTag("qtimetadatafield");
5991  $a_xml_writer->xmlElement("fieldlabel", NULL, "allowedUsersTimeGap");
5992  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getAllowedUsersTimeGap());
5993  $a_xml_writer->xmlEndTag("qtimetadatafield");
5994 
5995  // pass scoring
5996  $a_xml_writer->xmlStartTag("qtimetadatafield");
5997  $a_xml_writer->xmlElement("fieldlabel", NULL, "pass_scoring");
5998  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getPassScoring());
5999  $a_xml_writer->xmlEndTag("qtimetadatafield");
6000 
6001  // score reporting date
6002  if ($this->getReportingDate())
6003  {
6004  $a_xml_writer->xmlStartTag("qtimetadatafield");
6005  $a_xml_writer->xmlElement("fieldlabel", NULL, "reporting_date");
6006  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->reporting_date, $matches);
6007  $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]));
6008  $a_xml_writer->xmlEndTag("qtimetadatafield");
6009  }
6010  // number of tries
6011  $a_xml_writer->xmlStartTag("qtimetadatafield");
6012  $a_xml_writer->xmlElement("fieldlabel", NULL, "nr_of_tries");
6013  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getNrOfTries()));
6014  $a_xml_writer->xmlEndTag("qtimetadatafield");
6015 
6016  // kiosk
6017  $a_xml_writer->xmlStartTag("qtimetadatafield");
6018  $a_xml_writer->xmlElement("fieldlabel", NULL, "kiosk");
6019  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getKiosk()));
6020  $a_xml_writer->xmlEndTag("qtimetadatafield");
6021 
6022  // use previous answers
6023  $a_xml_writer->xmlStartTag("qtimetadatafield");
6024  $a_xml_writer->xmlElement("fieldlabel", NULL, "use_previous_answers");
6025  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getUsePreviousAnswers());
6026  $a_xml_writer->xmlEndTag("qtimetadatafield");
6027 
6028  // hide title points
6029  $a_xml_writer->xmlStartTag("qtimetadatafield");
6030  $a_xml_writer->xmlElement("fieldlabel", NULL, "title_output");
6031  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getTitleOutput()));
6032  $a_xml_writer->xmlEndTag("qtimetadatafield");
6033 
6034  // random question count
6035  $a_xml_writer->xmlStartTag("qtimetadatafield");
6036  $a_xml_writer->xmlElement("fieldlabel", NULL, "random_question_count");
6037  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getRandomQuestionCount()));
6038  $a_xml_writer->xmlEndTag("qtimetadatafield");
6039 
6040  // results presentation
6041  $a_xml_writer->xmlStartTag("qtimetadatafield");
6042  $a_xml_writer->xmlElement("fieldlabel", NULL, "results_presentation");
6043  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getResultsPresentation()));
6044  $a_xml_writer->xmlEndTag("qtimetadatafield");
6045 
6046  // solution details
6047  $a_xml_writer->xmlStartTag("qtimetadatafield");
6048  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_summary");
6049  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getListOfQuestionsSettings()));
6050  $a_xml_writer->xmlEndTag("qtimetadatafield");
6051 
6052  // solution details
6053  $a_xml_writer->xmlStartTag("qtimetadatafield");
6054  $a_xml_writer->xmlElement("fieldlabel", NULL, "score_reporting");
6055  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getScoreReporting()));
6056  $a_xml_writer->xmlEndTag("qtimetadatafield");
6057 
6058  // solution details
6059  $a_xml_writer->xmlStartTag("qtimetadatafield");
6060  $a_xml_writer->xmlElement("fieldlabel", NULL, "instant_verification");
6061  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getInstantFeedbackSolution()));
6062  $a_xml_writer->xmlEndTag("qtimetadatafield");
6063 
6064  // answer specific feedback
6065  $a_xml_writer->xmlStartTag("qtimetadatafield");
6066  $a_xml_writer->xmlElement("fieldlabel", NULL, "answer_feedback");
6067  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnswerFeedback()));
6068  $a_xml_writer->xmlEndTag("qtimetadatafield");
6069 
6070  // answer specific feedback of reached points
6071  $a_xml_writer->xmlStartTag("qtimetadatafield");
6072  $a_xml_writer->xmlElement("fieldlabel", NULL, "answer_feedback_points");
6073  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getAnswerFeedbackPoints()));
6074  $a_xml_writer->xmlEndTag("qtimetadatafield");
6075 
6076  // show cancel
6077  $a_xml_writer->xmlStartTag("qtimetadatafield");
6078  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_cancel");
6079  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShowCancel()));
6080  $a_xml_writer->xmlEndTag("qtimetadatafield");
6081 
6082  // show marker
6083  $a_xml_writer->xmlStartTag("qtimetadatafield");
6084  $a_xml_writer->xmlElement("fieldlabel", NULL, "show_marker");
6085  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShowMarker()));
6086  $a_xml_writer->xmlEndTag("qtimetadatafield");
6087 
6088  // fixed participants
6089  $a_xml_writer->xmlStartTag("qtimetadatafield");
6090  $a_xml_writer->xmlElement("fieldlabel", NULL, "fixed_participants");
6091  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getFixedParticipants()));
6092  $a_xml_writer->xmlEndTag("qtimetadatafield");
6093 
6094  // show final statement
6095  $a_xml_writer->xmlStartTag("qtimetadatafield");
6096  $a_xml_writer->xmlElement("fieldlabel", NULL, "showfinalstatement");
6097  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getShowFinalStatement()) ? "1" : "0")));
6098  $a_xml_writer->xmlEndTag("qtimetadatafield");
6099 
6100  // show introduction only
6101  $a_xml_writer->xmlStartTag("qtimetadatafield");
6102  $a_xml_writer->xmlElement("fieldlabel", NULL, "showinfo");
6103  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getShowInfo()) ? "1" : "0")));
6104  $a_xml_writer->xmlEndTag("qtimetadatafield");
6105 
6106  // mail notification
6107  $a_xml_writer->xmlStartTag("qtimetadatafield");
6108  $a_xml_writer->xmlElement("fieldlabel", NULL, "mailnotification");
6109  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getMailNotification());
6110  $a_xml_writer->xmlEndTag("qtimetadatafield");
6111 
6112  // force JavaScript
6113  $a_xml_writer->xmlStartTag("qtimetadatafield");
6114  $a_xml_writer->xmlElement("fieldlabel", NULL, "forcejs");
6115  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", (($this->getForceJS()) ? "1" : "0")));
6116  $a_xml_writer->xmlEndTag("qtimetadatafield");
6117 
6118  // custom style
6119  $a_xml_writer->xmlStartTag("qtimetadatafield");
6120  $a_xml_writer->xmlElement("fieldlabel", NULL, "customstyle");
6121  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getCustomStyle());
6122  $a_xml_writer->xmlEndTag("qtimetadatafield");
6123 
6124  // shuffle questions
6125  $a_xml_writer->xmlStartTag("qtimetadatafield");
6126  $a_xml_writer->xmlElement("fieldlabel", NULL, "shuffle_questions");
6127  $a_xml_writer->xmlElement("fieldentry", NULL, sprintf("%d", $this->getShuffleQuestions()));
6128  $a_xml_writer->xmlEndTag("qtimetadatafield");
6129 
6130  // processing time
6131  $a_xml_writer->xmlStartTag("qtimetadatafield");
6132  $a_xml_writer->xmlElement("fieldlabel", NULL, "processing_time");
6133  $a_xml_writer->xmlElement("fieldentry", NULL, $this->getProcessingTime());
6134  $a_xml_writer->xmlEndTag("qtimetadatafield");
6135 
6136  // starting time
6137  if ($this->getStartingTime())
6138  {
6139  $a_xml_writer->xmlStartTag("qtimetadatafield");
6140  $a_xml_writer->xmlElement("fieldlabel", NULL, "starting_time");
6141  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->starting_time, $matches);
6142  $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]));
6143  $a_xml_writer->xmlEndTag("qtimetadatafield");
6144  }
6145  // ending time
6146  if ($this->getEndingTime())
6147  {
6148  $a_xml_writer->xmlStartTag("qtimetadatafield");
6149  $a_xml_writer->xmlElement("fieldlabel", NULL, "ending_time");
6150  preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->ending_time, $matches);
6151  $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]));
6152  $a_xml_writer->xmlEndTag("qtimetadatafield");
6153  }
6154  foreach ($this->mark_schema->mark_steps as $index => $mark)
6155  {
6156  // mark steps
6157  $a_xml_writer->xmlStartTag("qtimetadatafield");
6158  $a_xml_writer->xmlElement("fieldlabel", NULL, "mark_step_$index");
6159  $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()));
6160  $a_xml_writer->xmlEndTag("qtimetadatafield");
6161  }
6162  $a_xml_writer->xmlEndTag("qtimetadata");
6163 
6164  // add qti objectives
6165  $a_xml_writer->xmlStartTag("objectives");
6166  $this->addQTIMaterial($a_xml_writer, $this->getIntroduction());
6167  $a_xml_writer->xmlEndTag("objectives");
6168 
6169  // add qti assessmentcontrol
6170  if ($this->getInstantFeedbackSolution() == 1)
6171  {
6172  $attrs = array(
6173  "solutionswitch" => "Yes"
6174  );
6175  }
6176  else
6177  {
6178  $attrs = NULL;
6179  }
6180  $a_xml_writer->xmlElement("assessmentcontrol", $attrs, NULL);
6181 
6182  if (strlen($this->getFinalStatement()))
6183  {
6184  // add qti presentation_material
6185  $a_xml_writer->xmlStartTag("presentation_material");
6186  $a_xml_writer->xmlStartTag("flow_mat");
6187  $this->addQTIMaterial($a_xml_writer, $this->getFinalStatement());
6188  $a_xml_writer->xmlEndTag("flow_mat");
6189  $a_xml_writer->xmlEndTag("presentation_material");
6190  }
6191 
6192  $attrs = array(
6193  "ident" => "1"
6194  );
6195  $a_xml_writer->xmlElement("section", $attrs, NULL);
6196  $a_xml_writer->xmlEndTag("assessment");
6197  $a_xml_writer->xmlEndTag("questestinterop");
6198 
6199  $xml = $a_xml_writer->xmlDumpMem(FALSE);
6200 
6201  foreach ($this->questions as $question_id)
6202  {
6203  $question =& ilObjTest::_instanciateQuestion($question_id);
6204  $qti_question = $question->toXML(false);
6205  $qti_question = preg_replace("/<questestinterop>/", "", $qti_question);
6206  $qti_question = preg_replace("/<\/questestinterop>/", "", $qti_question);
6207  if (strpos($xml, "</section>") !== false)
6208  {
6209  $xml = str_replace("</section>", "$qti_question</section>", $xml);
6210  }
6211  else
6212  {
6213  $xml = str_replace("<section ident=\"1\"/>", "<section ident=\"1\">\n$qti_question</section>", $xml);
6214  }
6215  }
6216  return $xml;
6217  }
6218 
6225  function exportPagesXML(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
6226  {
6227  global $ilBench;
6228 
6229  $this->mob_ids = array();
6230  $this->file_ids = array();
6231 
6232  $attrs = array();
6233  $attrs["Type"] = "Test";
6234  $a_xml_writer->xmlStartTag("ContentObject", $attrs);
6235 
6236  // MetaData
6237  $this->exportXMLMetaData($a_xml_writer);
6238 
6239  // PageObjects
6240  $expLog->write(date("[y-m-d H:i:s] ")."Start Export Page Objects");
6241  $ilBench->start("ContentObjectExport", "exportPageObjects");
6242  $this->exportXMLPageObjects($a_xml_writer, $a_inst, $expLog);
6243  $ilBench->stop("ContentObjectExport", "exportPageObjects");
6244  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export Page Objects");
6245 
6246  // MediaObjects
6247  $expLog->write(date("[y-m-d H:i:s] ")."Start Export Media Objects");
6248  $ilBench->start("ContentObjectExport", "exportMediaObjects");
6249  $this->exportXMLMediaObjects($a_xml_writer, $a_inst, $a_target_dir, $expLog);
6250  $ilBench->stop("ContentObjectExport", "exportMediaObjects");
6251  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export Media Objects");
6252 
6253  // FileItems
6254  $expLog->write(date("[y-m-d H:i:s] ")."Start Export File Items");
6255  $ilBench->start("ContentObjectExport", "exportFileItems");
6256  $this->exportFileItems($a_target_dir, $expLog);
6257  $ilBench->stop("ContentObjectExport", "exportFileItems");
6258  $expLog->write(date("[y-m-d H:i:s] ")."Finished Export File Items");
6259 
6260  $a_xml_writer->xmlEndTag("ContentObject");
6261  }
6262 
6269  function exportXMLMetaData(&$a_xml_writer)
6270  {
6271  include_once "./Services/MetaData/classes/class.ilMD2XML.php";
6272  $md2xml = new ilMD2XML($this->getId(), 0, $this->getType());
6273  $md2xml->setExportMode(true);
6274  $md2xml->startExport();
6275  $a_xml_writer->appendXML($md2xml->getXML());
6276  }
6277 
6283  function modifyExportIdentifier($a_tag, $a_param, $a_value)
6284  {
6285  if ($a_tag == "Identifier" && $a_param == "Entry")
6286  {
6287  include_once "./Services/Utilities/classes/class.ilUtil.php";
6288  $a_value = ilUtil::insertInstIntoID($a_value);
6289  }
6290 
6291  return $a_value;
6292  }
6293 
6294 
6301  function exportXMLPageObjects(&$a_xml_writer, $a_inst, &$expLog)
6302  {
6303  global $ilBench;
6304 
6305  include_once "./Modules/LearningModule/classes/class.ilLMPageObject.php";
6306 
6307  foreach ($this->questions as $question_id)
6308  {
6309  $ilBench->start("ContentObjectExport", "exportPageObject");
6310  $expLog->write(date("[y-m-d H:i:s] ")."Page Object ".$question_id);
6311 
6312  $attrs = array();
6313  $a_xml_writer->xmlStartTag("PageObject", $attrs);
6314 
6315 
6316  // export xml to writer object
6317  $ilBench->start("ContentObjectExport", "exportPageObject_XML");
6318  $page_object = new ilPageObject("qpl", $question_id);
6319  $page_object->buildDom();
6320  $page_object->insertInstIntoIDs($a_inst);
6321  $mob_ids = $page_object->collectMediaObjects(false);
6322  $file_ids = $page_object->collectFileItems();
6323  $xml = $page_object->getXMLFromDom(false, false, false, "", true);
6324  $xml = str_replace("&","&amp;", $xml);
6325  $a_xml_writer->appendXML($xml);
6326  $page_object->freeDom();
6327  unset ($page_object);
6328 
6329  $ilBench->stop("ContentObjectExport", "exportPageObject_XML");
6330 
6331  // collect media objects
6332  $ilBench->start("ContentObjectExport", "exportPageObject_CollectMedia");
6333  //$mob_ids = $page_obj->getMediaObjectIDs();
6334  foreach($mob_ids as $mob_id)
6335  {
6336  $this->mob_ids[$mob_id] = $mob_id;
6337  }
6338  $ilBench->stop("ContentObjectExport", "exportPageObject_CollectMedia");
6339 
6340  // collect all file items
6341  $ilBench->start("ContentObjectExport", "exportPageObject_CollectFileItems");
6342  //$file_ids = $page_obj->getFileItemIds();
6343  foreach($file_ids as $file_id)
6344  {
6345  $this->file_ids[$file_id] = $file_id;
6346  }
6347  $ilBench->stop("ContentObjectExport", "exportPageObject_CollectFileItems");
6348 
6349  $a_xml_writer->xmlEndTag("PageObject");
6350  //unset($page_obj);
6351 
6352  $ilBench->stop("ContentObjectExport", "exportPageObject");
6353 
6354 
6355  }
6356  }
6357 
6364  function exportXMLMediaObjects(&$a_xml_writer, $a_inst, $a_target_dir, &$expLog)
6365  {
6366  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
6367 
6368  foreach ($this->mob_ids as $mob_id)
6369  {
6370  $expLog->write(date("[y-m-d H:i:s] ")."Media Object ".$mob_id);
6371  if (ilObjMediaObject::_exists($mob_id))
6372  {
6373  $media_obj = new ilObjMediaObject($mob_id);
6374  $media_obj->exportXML($a_xml_writer, $a_inst);
6375  $media_obj->exportFiles($a_target_dir);
6376  unset($media_obj);
6377  }
6378  }
6379  }
6380 
6385  function exportFileItems($a_target_dir, &$expLog)
6386  {
6387  include_once "./Modules/File/classes/class.ilObjFile.php";
6388 
6389  foreach ($this->file_ids as $file_id)
6390  {
6391  $expLog->write(date("[y-m-d H:i:s] ")."File Item ".$file_id);
6392  $file_obj = new ilObjFile($file_id, false);
6393  $file_obj->export($a_target_dir);
6394  unset($file_obj);
6395  }
6396  }
6397 
6402  function getImportMapping()
6403  {
6404  if (!is_array($this->import_mapping))
6405  {
6406  return array();
6407  }
6408  else
6409  {
6410  return $this->import_mapping;
6411  }
6412  }
6413 
6423  function getECTSGrade($passed_array, $reached_points, $max_points)
6424  {
6425  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);
6426  }
6427 
6436  function _getECTSGrade($points_passed, $reached_points, $max_points, $a, $b, $c, $d, $e, $fx)
6437  {
6438  include_once "./classes/class.ilStatistics.php";
6439  // calculate the median
6440  $passed_statistics = new ilStatistics();
6441  $passed_statistics->setData($points_passed);
6442  $ects_percentiles = array
6443  (
6444  "A" => $passed_statistics->quantile($a),
6445  "B" => $passed_statistics->quantile($b),
6446  "C" => $passed_statistics->quantile($c),
6447  "D" => $passed_statistics->quantile($d),
6448  "E" => $passed_statistics->quantile($e)
6449  );
6450  if (count($points_passed) && ($reached_points >= $ects_percentiles["A"]))
6451  {
6452  return "A";
6453  }
6454  else if (count($points_passed) && ($reached_points >= $ects_percentiles["B"]))
6455  {
6456  return "B";
6457  }
6458  else if (count($points_passed) && ($reached_points >= $ects_percentiles["C"]))
6459  {
6460  return "C";
6461  }
6462  else if (count($points_passed) && ($reached_points >= $ects_percentiles["D"]))
6463  {
6464  return "D";
6465  }
6466  else if (count($points_passed) && ($reached_points >= $ects_percentiles["E"]))
6467  {
6468  return "E";
6469  }
6470  else if (strcmp($fx, "") != 0)
6471  {
6472  if ($max_points > 0)
6473  {
6474  $percentage = ($reached_points / $max_points) * 100.0;
6475  if ($percentage < 0) $percentage = 0.0;
6476  }
6477  else
6478  {
6479  $percentage = 0.0;
6480  }
6481  if ($percentage >= $fx)
6482  {
6483  return "FX";
6484  }
6485  else
6486  {
6487  return "F";
6488  }
6489  }
6490  else
6491  {
6492  return "F";
6493  }
6494  }
6495 
6496  function checkMarks()
6497  {
6498  return $this->mark_schema->checkMarks();
6499  }
6500 
6501  function getMarkSchema()
6502  {
6503  return $this->mark_schema;
6504  }
6505 
6513  function setAuthor($author = "")
6514  {
6515  $this->author = $author;
6516  }
6517 
6527  function saveAuthorToMetadata($a_author = "")
6528  {
6529  $md =& new ilMD($this->getId(), 0, $this->getType());
6530  $md_life =& $md->getLifecycle();
6531  if (!$md_life)
6532  {
6533  if (strlen($a_author) == 0)
6534  {
6535  global $ilUser;
6536  $a_author = $ilUser->getFullname();
6537  }
6538 
6539  $md_life =& $md->addLifecycle();
6540  $md_life->save();
6541  $con =& $md_life->addContribute();
6542  $con->setRole("Author");
6543  $con->save();
6544  $ent =& $con->addEntity();
6545  $ent->setEntity($a_author);
6546  $ent->save();
6547  }
6548  }
6549 
6555  function createMetaData()
6556  {
6558  $this->saveAuthorToMetadata();
6559  }
6560 
6568  function getAuthor()
6569  {
6570  $author = array();
6571  include_once "./Services/MetaData/classes/class.ilMD.php";
6572  $md =& new ilMD($this->getId(), 0, $this->getType());
6573  $md_life =& $md->getLifecycle();
6574  if ($md_life)
6575  {
6576  $ids =& $md_life->getContributeIds();
6577  foreach ($ids as $id)
6578  {
6579  $md_cont =& $md_life->getContribute($id);
6580  if (strcmp($md_cont->getRole(), "Author") == 0)
6581  {
6582  $entids =& $md_cont->getEntityIds();
6583  foreach ($entids as $entid)
6584  {
6585  $md_ent =& $md_cont->getEntity($entid);
6586  array_push($author, $md_ent->getEntity());
6587  }
6588  }
6589  }
6590  }
6591  return join($author, ",");
6592  }
6593 
6601  function _lookupAuthor($obj_id)
6602  {
6603  $author = array();
6604  include_once "./Services/MetaData/classes/class.ilMD.php";
6605  $md =& new ilMD($obj_id, 0, "tst");
6606  $md_life =& $md->getLifecycle();
6607  if ($md_life)
6608  {
6609  $ids =& $md_life->getContributeIds();
6610  foreach ($ids as $id)
6611  {
6612  $md_cont =& $md_life->getContribute($id);
6613  if (strcmp($md_cont->getRole(), "Author") == 0)
6614  {
6615  $entids =& $md_cont->getEntityIds();
6616  foreach ($entids as $entid)
6617  {
6618  $md_ent =& $md_cont->getEntity($entid);
6619  array_push($author, $md_ent->getEntity());
6620  }
6621  }
6622  }
6623  }
6624  return join($author, ",");
6625  }
6626 
6633  function &_getAvailableTests($use_object_id = FALSE)
6634  {
6635  global $ilUser;
6636  global $ilDB;
6637 
6638  $result_array = array();
6639  $tests = ilUtil::_getObjectsByOperations("tst","write", $ilUser->getId(), -1);
6640  if (count($tests))
6641  {
6642  $titles = ilObject::_prepareCloneSelection($tests, "tst");
6643  foreach ($tests as $ref_id)
6644  {
6645  if ($use_object_id)
6646  {
6647  $obj_id = ilObject::_lookupObjId($ref_id);
6648  $result_array[$obj_id] = $titles[$ref_id];
6649  }
6650  else
6651  {
6652  $result_array[$ref_id] = $titles[$ref_id];
6653  }
6654  }
6655  }
6656  return $result_array;
6657  }
6658 
6665  function cloneRandomQuestions($new_id)
6666  {
6667  global $ilDB;
6668 
6669  if ($new_id > 0)
6670  {
6671  $result = $ilDB->queryF("SELECT * FROM tst_test_random WHERE test_fi = %s ORDER BY sequence, test_random_id",
6672  array('integer'),
6673  array($this->getTestId())
6674  );
6675  if ($result->numRows())
6676  {
6677  while ($row = $ilDB->fetchAssoc($result))
6678  {
6679  $next_id = $ilDB->nextId('tst_test_random');
6680  $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)",
6681  array('integer', 'integer', 'integer', 'integer', 'integer', 'integer'),
6682  array($next_id, $new_id, $row["questionpool_fi"], $row["num_of_q"], time(), $row['sequence'])
6683  );
6684  }
6685  }
6686  }
6687  }
6688 
6689 
6698  public function cloneObject($a_target_id,$a_copy_id = 0)
6699  {
6700  global $ilDB,$ilLog;
6701 
6702  $this->loadFromDb();
6703 
6704  // Copy settings
6705  $newObj = parent::cloneObject($a_target_id,$a_copy_id);
6706  $this->cloneMetaData($newObj);
6707  $newObj->setAnonymity($this->getAnonymity());
6708  $newObj->setAnswerFeedback($this->getAnswerFeedback());
6709  $newObj->setAnswerFeedbackPoints($this->getAnswerFeedbackPoints());
6710  $newObj->setAuthor($this->getAuthor());
6711  $newObj->setCountSystem($this->getCountSystem());
6712  $newObj->setECTSFX($this->getECTSFX());
6713  $newObj->setECTSGrades($this->getECTSGrades());
6714  $newObj->setECTSOutput($this->getECTSOutput());
6715  $newObj->setEnableProcessingTime($this->getEnableProcessingTime());
6716  $newObj->setEndingTime($this->getEndingTime());
6717  $newObj->setFixedParticipants($this->getFixedParticipants());
6718  $newObj->setInstantFeedbackSolution($this->getInstantFeedbackSolution());
6719  $newObj->setIntroduction($this->getIntroduction());
6720  $newObj->setFinalStatement($this->getFinalStatement());
6721  $newObj->setShowInfo($this->getShowInfo());
6722  $newObj->setForceJS($this->getForceJS());
6723  $newObj->setCustomStyle($this->getCustomStyle());
6724  $newObj->setShowFinalStatement($this->getShowFinalStatement());
6725  $newObj->setListOfQuestionsSettings($this->getListOfQuestionsSettings());
6726  $newObj->setMCScoring($this->getMCScoring());
6727  $newObj->setMailNotification($this->getMailNotification());
6728  $newObj->setNrOfTries($this->getNrOfTries());
6729  $newObj->setPassScoring($this->getPassScoring());
6730  $newObj->setPassword($this->getPassword());
6731  $newObj->setProcessingTime($this->getProcessingTime());
6732  $newObj->setRandomQuestionCount($this->getRandomQuestionCount());
6733  $newObj->setRandomTest($this->isRandomTest());
6734  $newObj->setReportingDate($this->getReportingDate());
6735  $newObj->setResetProcessingTime($this->getResetProcessingTime());
6736  $newObj->setResultsPresentation($this->getResultsPresentation());
6737  $newObj->setScoreCutting($this->getScoreCutting());
6738  $newObj->setScoreReporting($this->getScoreReporting());
6739  $newObj->setSequenceSettings($this->getSequenceSettings());
6740  $newObj->setShowCancel($this->getShowCancel());
6741  $newObj->setShowMarker($this->getShowMarker());
6742  $newObj->setShuffleQuestions($this->getShuffleQuestions());
6743  $newObj->setStartingTime($this->getStartingTime());
6744  $newObj->setTitleOutput($this->getTitleOutput());
6745  $newObj->setUsePreviousAnswers($this->getUsePreviousAnswers());
6746  $newObj->setCertificateVisibility($this->getCertificateVisibility());
6747  $newObj->mark_schema = clone $this->mark_schema;
6748  $newObj->saveToDb();
6749 
6750  // clone certificate
6751  include_once "./Services/Certificate/classes/class.ilCertificate.php";
6752  include_once "./Modules/Test/classes/class.ilTestCertificateAdapter.php";
6753  $cert = new ilCertificate(new ilTestCertificateAdapter($this));
6754  $newcert = new ilCertificate(new ilTestCertificateAdapter($newObj));
6755  $cert->cloneCertificate($newcert);
6756 
6757  if ($this->isRandomTest())
6758  {
6759  $newObj->saveRandomQuestionCount($newObj->getRandomQuestionCount());
6760  $this->cloneRandomQuestions($newObj->getTestId());
6761  }
6762  else
6763  {
6764  include_once("./Services/CopyWizard/classes/class.ilCopyWizardOptions.php");
6765  $cwo = ilCopyWizardOptions::_getInstance($a_copy_id);
6766 
6767  // clone the questions
6768  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
6769  foreach ($this->questions as $key => $question_id)
6770  {
6771  $question = ilObjTest::_instanciateQuestion($question_id);
6772  $newObj->questions[$key] = $question->duplicate();
6773  $original_id = assQuestion::_getOriginalId($question_id);
6774  $question = ilObjTest::_instanciateQuestion($newObj->questions[$key]);
6775  $question->saveToDb($original_id);
6776 
6777  // Save the mapping of old question id <-> new question id
6778  // This will be used in class.ilObjCourse::cloneDependencies to copy learning objectives
6779  $cwo->appendMapping($this->getRefId().'_'.$question_id,$newObj->getRefId().'_'.$newObj->questions[$key]);
6780  $ilLog->write(__METHOD__.': Added mapping '.$this->getRefId().'_'.$question_id.' <-> ' .
6781  $newObj->getRefId().'_'.$newObj->questions[$key]);
6782  }
6783  }
6784  $newObj->saveToDb();
6785  return $newObj;
6786  }
6787 
6794  function getQuestionCount()
6795  {
6796  $num = 0;
6797 
6798  if ($this->isRandomTest())
6799  {
6800  if ($this->getRandomQuestionCount())
6801  {
6802  $num = $this->getRandomQuestionCount();
6803  $qpls =& $this->getRandomQuestionpools();
6804  $maxcount = 0;
6805  foreach ($qpls as $data)
6806  {
6807  $maxcount += $data["contains"];
6808  }
6809  if ($num > $maxcount) $num = $maxcount;
6810  }
6811  else
6812  {
6813  $qpls =& $this->getRandomQuestionpools();
6814  foreach ($qpls as $data)
6815  {
6816  $add = ($data["count"] <= $data["contains"]) ? $data["count"] : $data["contains"];
6817  $num += $add;
6818  }
6819  }
6820  }
6821  else
6822  {
6823  $num = count($this->questions);
6824  }
6825  return $num;
6826  }
6827 
6835  {
6836  global $ilDB;
6837 
6838  $num = 0;
6839 
6840  $result = $ilDB->queryF("SELECT * FROM tst_tests WHERE test_id = %s",
6841  array('integer'),
6842  array($test_id)
6843  );
6844  if (!$result->numRows())
6845  {
6846  return 0;
6847  }
6848  $test = $ilDB->fetchAssoc($result);
6849 
6850  if ($test["random_test"] == 1)
6851  {
6852  $qpls = array();
6853  $counter = 0;
6854  $result = $ilDB->queryF("SELECT * FROM tst_test_random WHERE test_fi = %s ORDER BY sequence, test_random_id",
6855  array('integer'),
6856  array($test_id)
6857  );
6858  if ($result->numRows())
6859  {
6860  while ($row = $ilDB->fetchAssoc($result))
6861  {
6862  $countresult = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE obj_fi = %s AND qpl_questions.tstamp > 0 AND original_id IS NULL",
6863  array('integer'),
6864  $row["questionpool_fi"]
6865  );
6866  $contains = $countresult->numRows();
6867  $qpls[$counter] = array(
6868  "index" => $counter,
6869  "count" => $row["num_of_q"],
6870  "qpl" => $row["questionpool_fi"],
6871  "contains" => $contains
6872  );
6873  $counter++;
6874  }
6875  }
6876  if ($test["random_question_count"] > 0)
6877  {
6878  $num = $test["random_question_count"];
6879  $maxcount = 0;
6880  foreach ($qpls as $data)
6881  {
6882  $maxcount += $data["contains"];
6883  }
6884  if ($num > $maxcount) $num = $maxcount;
6885  }
6886  else
6887  {
6888  $num = 0;
6889  foreach ($qpls as $data)
6890  {
6891  $add = ($data["count"] <= $data["contains"]) ? $data["count"] : $data["contains"];
6892  $num += $add;
6893  }
6894  }
6895  }
6896  else
6897  {
6898  $result = $ilDB->queryF("SELECT test_question_id FROM tst_test_question WHERE test_fi = %s",
6899  array('integer'),
6900  array($test_id)
6901  );
6902  $num = $result->numRows();
6903  }
6904  return $num;
6905  }
6906 
6913  {
6914  global $ilDB;
6915 
6916  // delete eventually set questions of a previous non-random test
6917  $this->removeAllTestEditings();
6918  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE test_fi = %s",
6919  array('integer'),
6920  array($this->getTestId())
6921  );
6922  $this->questions = array();
6923  $this->saveCompleteStatus();
6924  }
6925 
6932  {
6933  global $ilDB;
6934  // delete eventually set random question pools of a previous random test
6935  $this->removeAllTestEditings();
6936  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_random WHERE test_fi = %s",
6937  array('integer'),
6938  array($this->getTestId())
6939  );
6940  $this->questions = array();
6941  $this->saveCompleteStatus();
6942  }
6943 
6951  function logAction($logtext = "", $question_id = "")
6952  {
6953  global $ilUser;
6954 
6955  $original_id = "";
6956  if (strcmp($question_id, "") != 0)
6957  {
6958  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
6959  $original_id = assQuestion::_getOriginalId($question_id);
6960  }
6961  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
6962  ilObjAssessmentFolder::_addLog($ilUser->getId(), $this->getId(), $logtext, $question_id, $original_id, TRUE, $this->getRefId());
6963  }
6964 
6973  {
6974  global $ilDB;
6975  $object_id = FALSE;
6976  $result = $ilDB->queryF("SELECT obj_fi FROM tst_tests WHERE test_id = %s",
6977  array('integer'),
6978  array($test_id)
6979  );
6980  if ($result->numRows())
6981  {
6982  $row = $ilDB->fetchAssoc($result);
6983  $object_id = $row["obj_fi"];
6984  }
6985  return $object_id;
6986  }
6987 
6995  function _getObjectIDFromActiveID($active_id)
6996  {
6997  global $ilDB;
6998  $object_id = FALSE;
6999  $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",
7000  array('integer'),
7001  array($active_id)
7002  );
7003  if ($result->numRows())
7004  {
7005  $row = $ilDB->fetchAssoc($result);
7006  $object_id = $row["obj_fi"];
7007  }
7008  return $object_id;
7009  }
7010 
7018  function _getTestIDFromObjectID($object_id)
7019  {
7020  global $ilDB;
7021  $test_id = FALSE;
7022  $result = $ilDB->queryF("SELECT test_id FROM tst_tests WHERE obj_fi = %s",
7023  array('integer'),
7024  array($object_id)
7025  );
7026  if ($result->numRows())
7027  {
7028  $row = $ilDB->fetchAssoc($result);
7029  $test_id = $row["test_id"];
7030  }
7031  return $test_id;
7032  }
7033 
7042  function getTextAnswer($active_id, $question_id, $pass = NULL)
7043  {
7044  global $ilDB;
7045 
7046  $res = "";
7047  if (($active_id) && ($question_id))
7048  {
7049  if (is_null($pass))
7050  {
7051  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7052  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
7053  }
7054  $result = $ilDB->queryF("SELECT value1 FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
7055  array('integer', 'integer', 'integer'),
7056  array($active_id, $question_id, $pass)
7057  );
7058  if ($result->numRows() == 1)
7059  {
7060  $row = $ilDB->fetchAssoc($result);
7061  $res = $row["value1"];
7062  }
7063  }
7064  return $res;
7065  }
7066 
7074  function getQuestiontext($question_id)
7075  {
7076  global $ilDB;
7077 
7078  $res = "";
7079  if ($question_id)
7080  {
7081  $result = $ilDB->queryF("SELECT question_text FROM qpl_questions WHERE question_id = %s",
7082  array('integer'),
7083  array($question_id)
7084  );
7085  if ($result->numRows() == 1)
7086  {
7087  $row = $ilDB->fetchAssoc($result);
7088  $res = $row["question_text"];
7089  }
7090  }
7091  return $res;
7092  }
7093 
7100  function &getInvitedUsers($user_id="", $order="login, lastname, firstname")
7101  {
7102  global $ilDB;
7103 
7104  $result_array = array();
7105 
7106  if ($this->getAnonymity())
7107  {
7108  if (is_numeric($user_id))
7109  {
7110  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " .
7111  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7112  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7113  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
7114  "ORDER BY $order",
7115  array('text', 'text', 'text', 'integer', 'integer'),
7116  array("", $this->lng->txt("unknown"), "", $this->getTestId(), $user_id)
7117  );
7118  }
7119  else
7120  {
7121  $result = $ilDB->queryF("SELECT tst_active.active_id, usr_id, %s login, %s lastname, %s firstname, tst_invited_user.clientip, " .
7122  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7123  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7124  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
7125  "ORDER BY $order",
7126  array('text', 'text', 'text', 'integer'),
7127  array("", $this->lng->txt("unknown"), "", $this->getTestId())
7128  );
7129  }
7130  }
7131  else
7132  {
7133  if (is_numeric($user_id))
7134  {
7135  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " .
7136  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7137  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7138  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id AND usr_data.usr_id=%s " .
7139  "ORDER BY $order",
7140  array('integer', 'integer'),
7141  array($this->getTestId(), $user_id)
7142  );
7143  }
7144  else
7145  {
7146  $result = $ilDB->queryF("SELECT tst_active.active_id, tst_active.tries, usr_id, login, lastname, firstname, tst_invited_user.clientip, " .
7147  "tst_active.submitted test_finished, matriculation FROM usr_data, tst_invited_user " .
7148  "LEFT JOIN tst_active ON tst_active.user_fi = tst_invited_user.user_fi AND tst_active.test_fi = tst_invited_user.test_fi " .
7149  "WHERE tst_invited_user.test_fi = %s and tst_invited_user.user_fi=usr_data.usr_id " .
7150  "ORDER BY $order",
7151  array('integer'),
7152  array($this->getTestId())
7153  );
7154  }
7155  }
7156  $result_array = array();
7157  while ($row = $ilDB->fetchAssoc($result))
7158  {
7159  $result_array[$row['usr_id']]= $row;
7160  }
7161  return $result_array;
7162  }
7163 
7171  {
7172  global $ilDB;
7173 
7174  if ($this->getAnonymity())
7175  {
7176  $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 ".
7177  "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),
7178  array('text', 'text', 'text', 'integer'),
7179  array("", $this->lng->txt("unknown"), "", $this->getTestId())
7180  );
7181  }
7182  else
7183  {
7184  $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 ".
7185  "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),
7186  array('integer'),
7187  array($this->getTestId())
7188  );
7189  }
7190  $data = array();
7191  while ($row = $ilDB->fetchAssoc($result))
7192  {
7193  $data[$row['active_id']] = $row;
7194  }
7195  foreach ($data as $index => $participant)
7196  {
7197  if (strlen(trim($participant["firstname"].$participant["lastname"])) == 0)
7198  {
7199  $data[$index]["lastname"] = $this->lng->txt("deleted_user");
7200  }
7201  }
7202  return $data;
7203  }
7204 
7206  {
7207  global $ilDB;
7208 
7209  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
7211  if (count($scoring) == 0) return array();
7212 
7213  $participants =& $this->getTestParticipants();
7214  $filtered_participants = array();
7215  foreach ($participants as $active_id => $participant)
7216  {
7217  $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",
7218  array("integer"),
7219  array($active_id)
7220  );
7221  $count = $result->numRows();
7222  if ($count > 0)
7223  {
7224  switch ($filter)
7225  {
7226  case 1: // only active users
7227  if ($participant->active) $filtered_participants[$active_id] = $participant;
7228  break;
7229  case 2: // only inactive users
7230  if (!$participant->active) $filtered_participants[$active_id] = $participant;
7231  break;
7232  case 3: // all users
7233  $filtered_participants[$active_id] = $participant;
7234  break;
7235  case 4:
7236  // already scored participants
7237  //$found = 0;
7238  //while ($row = $ilDB->fetchAssoc($result))
7239  //{
7240  // if ($row["manual"]) $found++;
7241  //}
7242  //if ($found == $count)
7243  //{
7244  //$filtered_participants[$active_id] = $participant;
7245  //}
7246  //else
7247  //{
7248  $assessmentSetting = new ilSetting("assessment");
7249  $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id);
7250  if ($manscoring_done) $filtered_participants[$active_id] = $participant;
7251  //}
7252  break;
7253  case 5:
7254  // unscored participants
7255  //$found = 0;
7256  //while ($row = $ilDB->fetchAssoc($result))
7257  //{
7258  // if ($row["manual"]) $found++;
7259  //}
7260  //if ($found == 0)
7261  //{
7262  $assessmentSetting = new ilSetting("assessment");
7263  $manscoring_done = $assessmentSetting->get("manscoring_done_" . $active_id);
7264  if (!$manscoring_done) $filtered_participants[$active_id] = $participant;
7265  //}
7266  break;
7267  case 6:
7268  // partially scored participants
7269  $found = 0;
7270  while ($row = $ilDB->fetchAssoc($result))
7271  {
7272  if ($row["manual"]) $found++;
7273  }
7274  if (($found > 0) && ($found < $count)) $filtered_participants[$active_id] = $participant;
7275  break;
7276  default:
7277  $filtered_participants[$active_id] = $participant;
7278  break;
7279  }
7280  }
7281  }
7282  return $filtered_participants;
7283  }
7284 
7292  function &getUserData($ids)
7293  {
7294  global $ilDB;
7295 
7296  if (!is_array($ids) || count($ids) ==0) return array();
7297 
7298  if ($this->getAnonymity())
7299  {
7300  $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",
7301  array('text', 'text', 'text'),
7302  array("", $this->lng->txt("unknown"), "")
7303  );
7304  }
7305  else
7306  {
7307  $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");
7308  }
7309 
7310  $result_array = array();
7311  while ($row = $ilDB->fetchAssoc($result))
7312  {
7313  $result_array[$row["usr_id"]]= $row;
7314  }
7315  return $result_array;
7316  }
7317 
7318  function &getGroupData($ids)
7319  {
7320  if (!is_array($ids) || count($ids) ==0) return array();
7321  $result = array();
7322  foreach ($ids as $ref_id)
7323  {
7324  $obj_id = ilObject::_lookupObjId($ref_id);
7325  $result[$ref_id] = array("ref_id" => $ref_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id));
7326  }
7327  return $result;
7328  }
7329 
7330  function &getRoleData($ids)
7331  {
7332  if (!is_array($ids) || count($ids) ==0) return array();
7333  $result = array();
7334  foreach ($ids as $obj_id)
7335  {
7336  $result[$obj_id] = array("obj_id" => $obj_id, "title" => ilObject::_lookupTitle($obj_id), "description" => ilObject::_lookupDescription($obj_id));
7337  }
7338  return $result;
7339  }
7340 
7341 
7348  function inviteGroup($group_id)
7349  {
7350  include_once "./Modules/Group/classes/class.ilObjGroup.php";
7351  $group = new ilObjGroup($group_id);
7352  $members = $group->getGroupMemberIds();
7353  include_once './Services/User/classes/class.ilObjUser.php';
7354  foreach ($members as $user_id)
7355  {
7356  $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id));
7357  }
7358  }
7359 
7366  function inviteRole($role_id)
7367  {
7368  global $rbacreview;
7369  $members = $rbacreview->assignedUsers($role_id,"usr_id");
7370  include_once './Services/User/classes/class.ilObjUser.php';
7371  foreach ($members as $user_id)
7372  {
7373  $this->inviteUser($user_id, ilObjUser::_lookupClientIP($user_id));
7374  }
7375  }
7376 
7377 
7378 
7385  function disinviteUser($user_id)
7386  {
7387  global $ilDB;
7388 
7389  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
7390  array('integer', 'integer'),
7391  array($this->getTestId(), $user_id)
7392  );
7393  }
7394 
7401  function inviteUser($user_id, $client_ip="")
7402  {
7403  global $ilDB;
7404 
7405  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_invited_user WHERE test_fi = %s AND user_fi = %s",
7406  array('integer', 'integer'),
7407  array($this->getTestId(), $user_id)
7408  );
7409  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_invited_user (test_fi, user_fi, clientip, tstamp) VALUES (%s, %s, %s, %s)",
7410  array('integer', 'integer', 'text', 'integer'),
7411  array($this->getTestId(), $user_id, (strlen($client_ip)) ? $client_ip : NULL, time())
7412  );
7413  }
7414 
7415 
7416  function setClientIP($user_id, $client_ip)
7417  {
7418  global $ilDB;
7419 
7420  $affectedRows = $ilDB->manipulateF("UPDATE tst_invited_user SET clientip = %s, tstamp = %s WHERE test_fi=%s and user_fi=%s",
7421  array('text', 'integer', 'integer', 'integer'),
7422  array((strlen($client_ip)) ? $client_ip : NULL, time(), $this->getTestId(), $user_id)
7423  );
7424  }
7425 
7431  function _getSolvedQuestions($active_id, $question_fi = null)
7432  {
7433  global $ilDB;
7434  if (is_numeric($question_fi))
7435  {
7436  $result = $ilDB->queryF("SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s AND question_fi=%s",
7437  array('integer', 'integer'),
7438  array($active_id, $question_fi)
7439  );
7440  }
7441  else
7442  {
7443  $result = $ilDB->queryF("SELECT question_fi, solved FROM tst_qst_solved WHERE active_fi = %s",
7444  array('integer'),
7445  array($active_id)
7446  );
7447  }
7448  $result_array = array();
7449  while ($row = $ilDB->fetchAssoc($result))
7450  {
7451  $result_array[$row["question_fi"]]= $row;
7452  }
7453  return $result_array;
7454  }
7455 
7456 
7460  function setQuestionSetSolved($value, $question_id, $user_id)
7461  {
7462  global $ilDB;
7463 
7464  $active_id = $this->getActiveIdOfUser($user_id);
7465  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_qst_solved WHERE active_fi = %s AND question_fi = %s",
7466  array('integer', 'integer'),
7467  array($active_id, $question_id)
7468  );
7469  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_qst_solved (solved, question_fi, active_fi) VALUES (%s, %s, %s)",
7470  array('integer', 'integer', 'integer'),
7471  array($value, $question_id, $active_id)
7472  );
7473  }
7474 
7475 
7479  function setActiveTestSubmitted($user_id)
7480  {
7481  global $ilDB, $ilLog;
7482 
7483  $affectedRows = $ilDB->manipulateF("UPDATE tst_active SET submitted = %s, submittimestamp = %s, tstamp = %s WHERE test_fi = %s AND user_fi = %s",
7484  array('integer', 'timestamp', 'integer', 'integer', 'integer'),
7485  array(1, date('Y-m-d H:i:s'), time(), $this->getTestId(), $user_id)
7486  );
7487  $this->testSession = NULL;
7488  }
7489 
7493  function isTestFinished($active_id)
7494  {
7495  global $ilDB;
7496 
7497  $result = $ilDB->queryF("SELECT submitted FROM tst_active WHERE active_id=%s AND submitted=%s",
7498  array('integer', 'integer'),
7499  array($active_id, 1)
7500  );
7501  return $result->numRows() == 1;
7502  }
7503 
7507  function isActiveTestSubmitted($user_id = null)
7508  {
7509  global $ilUser;
7510  global $ilDB;
7511 
7512  if (!is_numeric($user_id))
7513  $user_id = $ilUser->getId();
7514 
7515  $result = $ilDB->queryF("SELECT submitted FROM tst_active WHERE test_fi=%s AND user_fi=%s AND submitted=%s",
7516  array('integer', 'integer', 'integer'),
7517  array($this->getTestId(), $user_id, 1)
7518  );
7519  return $result->numRows() == 1;
7520  }
7521 
7526  {
7527  return $this->getNrOfTries() != 0;
7528  }
7529 
7530 
7535  function isNrOfTriesReached($tries)
7536  {
7537  return $tries >= (int) $this->getNrOfTries();
7538  }
7539 
7540 
7549  function getAllTestResults($participants, $prepareForCSV = true)
7550  {
7551  $results = array();
7552  $row = array(
7553  "user_id" => $this->lng->txt("user_id"),
7554  "matriculation" => $this->lng->txt("matriculation"),
7555  "lastname" => $this->lng->txt("lastname"),
7556  "firstname" => $this->lng->txt("firstname"),
7557  "login" =>$this->lng->txt("login"),
7558  "reached_points" => $this->lng->txt("tst_reached_points"),
7559  "max_points" => $this->lng->txt("tst_maximum_points"),
7560  "percent_value" => $this->lng->txt("tst_percent_solved"),
7561  "mark" => $this->lng->txt("tst_mark"),
7562  "ects" => $this->lng->txt("ects_grade")
7563  );
7564  $results[] = $row;
7565  if (count($participants))
7566  {
7567  if ($this->ects_output)
7568  {
7569  $passed_array =& $this->getTotalPointsPassedArray();
7570  }
7571  foreach ($participants as $active_id => $user_rec)
7572  {
7573  $row = array();
7574  $reached_points = 0;
7575  $max_points = 0;
7576  foreach ($this->questions as $value)
7577  {
7578  $question =& ilObjTest::_instanciateQuestion($value);
7579  if (is_object($question))
7580  {
7581  $max_points += $question->getMaximumPoints();
7582  $reached_points += $question->getReachedPoints($active_id);
7583  }
7584  }
7585  if ($max_points > 0)
7586  {
7587  $percentvalue = $reached_points / $max_points;
7588  if ($percentvalue < 0) $percentvalue = 0.0;
7589  }
7590  else
7591  {
7592  $percentvalue = 0;
7593  }
7594  $mark_obj = $this->mark_schema->getMatchingMark($percentvalue * 100);
7595  $passed = "";
7596  if ($mark_obj)
7597  {
7598  $mark = $mark_obj->getOfficialName();
7599  $ects_mark = $this->getECTSGrade($passed_array, $reached_points, $max_points);
7600  }
7601  if ($this->getAnonymity())
7602  {
7603  $user_rec['firstname'] = "";
7604  $user_rec['lastname'] = $this->lng->txt("unknown");
7605  }
7606  $row = array(
7607  "user_id"=>$user_rec['usr_id'],
7608  "matriculation" => $user_rec['matriculation'],
7609  "lastname" => $user_rec['lastname'],
7610  "firstname" => $user_rec['firstname'],
7611  "login"=>$user_rec['login'],
7612  "reached_points" => $reached_points,
7613  "max_points" => $max_points,
7614  "percent_value" => $percentvalue,
7615  "mark" => $mark,
7616  "ects" => $ects_mark
7617  );
7618  $results[] = $prepareForCSV ? $this->processCSVRow ($row, true) : $row;
7619  }
7620  }
7621  return $results;
7622  }
7623 
7634  function &processCSVRow($row, $quoteAll = FALSE, $separator = ";")
7635  {
7636  $resultarray = array();
7637  foreach ($row as $rowindex => $entry)
7638  {
7639  $surround = FALSE;
7640  if ($quoteAll)
7641  {
7642  $surround = TRUE;
7643  }
7644  if (strpos($entry, "\"") !== FALSE)
7645  {
7646  $entry = str_replace("\"", "\"\"", $entry);
7647  $surround = TRUE;
7648  }
7649  if (strpos($entry, $separator) !== FALSE)
7650  {
7651  $surround = TRUE;
7652  }
7653  // replace all CR LF with LF (for Excel for Windows compatibility
7654  $entry = str_replace(chr(13).chr(10), chr(10), $entry);
7655  if ($surround)
7656  {
7657  $resultarray[$rowindex] = utf8_decode("\"" . $entry . "\"");
7658  }
7659  else
7660  {
7661  $resultarray[$rowindex] = utf8_decode($entry);
7662  }
7663  }
7664  return $resultarray;
7665  }
7666 
7675  function _getPass($active_id)
7676  {
7677  global $ilDB;
7678  $result = $ilDB->queryF("SELECT tries FROM tst_active WHERE active_id = %s",
7679  array('integer'),
7680  array($active_id)
7681  );
7682  if ($result->numRows())
7683  {
7684  $row = $ilDB->fetchAssoc($result);
7685  return $row["tries"];
7686  }
7687  else
7688  {
7689  return 0;
7690  }
7691  }
7692 
7702  function _getMaxPass($active_id)
7703  {
7704  global $ilDB;
7705  $result = $ilDB->queryF("SELECT MAX(pass) maxpass FROM tst_test_result WHERE active_fi = %s",
7706  array('integer'),
7707  array($active_id)
7708  );
7709  if ($result->numRows())
7710  {
7711  $row = $ilDB->fetchAssoc($result);
7712  $max = $row["maxpass"];
7713  }
7714  else
7715  {
7716  $max = NULL;
7717  }
7718  return $max;
7719  }
7720 
7729  function _getBestPass($active_id)
7730  {
7731  global $ilDB;
7732 
7733  $result = $ilDB->queryF("SELECT * FROM tst_pass_result WHERE active_fi = %s",
7734  array('integer'),
7735  array($active_id)
7736  );
7737  if ($result->numRows())
7738  {
7739  $bestrow = null;
7740  $bestpoints = -1;
7741  while ($row = $ilDB->fetchAssoc($result))
7742  {
7743  if ($row["points"] > $bestpoints)
7744  {
7745  $bestrow = $row;
7746  $bestpoints = $row["points"];
7747  }
7748  }
7749  if (is_array($bestrow))
7750  {
7751  return $bestrow["pass"];
7752  }
7753  else
7754  {
7755  return 0;
7756  }
7757  }
7758  else
7759  {
7760  return 0;
7761  }
7762  }
7763 
7772  function _getResultPass($active_id)
7773  {
7774  $counted_pass = NULL;
7775  if (ilObjTest::_getPassScoring($active_id) == SCORE_BEST_PASS)
7776  {
7777  $counted_pass = ilObjTest::_getBestPass($active_id);
7778  }
7779  else
7780  {
7781  $counted_pass = ilObjTest::_getMaxPass($active_id);
7782  }
7783  return $counted_pass;
7784  }
7785 
7795  function getAnsweredQuestionCount($active_id, $pass = NULL)
7796  {
7797  if ($this->isRandomTest())
7798  {
7799  $this->loadQuestions($active_id, $pass);
7800  }
7801  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
7802  $workedthrough = 0;
7803  foreach ($this->questions as $value)
7804  {
7805  if (assQuestion::_isWorkedThrough($active_id, $value, $pass))
7806  {
7807  $workedthrough += 1;
7808  }
7809  }
7810  return $workedthrough;
7811  }
7812 
7822  function getPassFinishDate($active_id, $pass)
7823  {
7824  global $ilDB;
7825  if (is_null($pass)) $pass = 0;
7826  $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",
7827  array('integer', 'integer'),
7828  array($active_id, $pass)
7829  );
7830  if ($result->numRows())
7831  {
7832  $row = $ilDB->fetchAssoc($result);
7833  return $row["tstamp"];
7834  }
7835  else
7836  {
7837  return 0;
7838  }
7839  }
7840 
7848  function isExecutable($user_id, $allowPassIncrease = FALSE)
7849  {
7850  $result = array(
7851  "executable" => true,
7852  "errormessage" => ""
7853  );
7854  if (!$this->startingTimeReached())
7855  {
7856  $result["executable"] = false;
7857  $result["errormessage"] = sprintf($this->lng->txt("detail_starting_time_not_reached"), ilFormat::ftimestamp2datetimeDB($this->getStartingTime()));
7858  return $result;
7859  }
7860  if ($this->endingTimeReached())
7861  {
7862  $result["executable"] = false;
7863  $result["errormessage"] = sprintf($this->lng->txt("detail_ending_time_reached"), ilFormat::ftimestamp2datetimeDB($this->getEndingTime()));
7864  return $result;
7865  }
7866 
7867  $active_id = $this->getActiveIdOfUser($user_id);
7868 
7869  if ($this->getEnableProcessingTime())
7870  {
7871  if ($active_id > 0)
7872  {
7873  $starting_time = $this->getStartingTimeOfUser($active_id);
7874  if ($starting_time !== FALSE)
7875  {
7877  {
7878  if ($allowPassIncrease && $this->getResetProcessingTime() && (($this->getNrOfTries() == 0) || ($this->getNrOfTries() > ($this->_getPass($active_id)+1))))
7879  {
7880  // a test pass was quitted because the maximum processing time was reached, but the time
7881  // will be resetted for future passes, so if there are more passes allowed, the participant may
7882  // start the test again.
7883  // This code block is only called when $allowPassIncrease is TRUE which only happens when
7884  // the test info page is opened. Otherwise this will lead to unexpected results!
7885  $this->getTestSession()->increasePass();
7886  $this->getTestSession()->setLastSequence(0);
7887  $this->getTestSession()->saveToDb();
7888  }
7889  else
7890  {
7891  $result["executable"] = false;
7892  $result["errormessage"] = $this->lng->txt("detail_max_processing_time_reached");
7893  }
7894  return $result;
7895  }
7896  }
7897  }
7898  }
7899 
7900  if ($this->hasNrOfTriesRestriction() && ($active_id > 0) && $this->isNrOfTriesReached($this->getTestSession($active_id)->getPass()))
7901  {
7902  $result["executable"] = false;
7903  $result["errormessage"] = $this->lng->txt("maximum_nr_of_tries_reached");
7904  return $result;
7905  }
7906 
7907  if ($this->getTestSession($active_id)->isSubmitted())
7908  {
7909  $result["executable"] = FALSE;
7910  $result["errormessage"] = $this->lng->txt("maximum_nr_of_tries_reached");
7911  return $result;
7912  }
7913 
7914  // TODO: max. processing time
7915 
7916  return $result;
7917  }
7918 
7925  function canViewResults()
7926  {
7927  $result = true;
7928  if ($this->getScoreReporting() == 4) return false;
7929  if ($this->getReportingDate())
7930  {
7931  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches))
7932  {
7933  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
7934  $now = mktime();
7935  if ($now < $epoch_time)
7936  {
7937  $result = false;
7938  }
7939  }
7940  }
7941  return $result;
7942  }
7943 
7944  function canShowTestResults($user_id)
7945  {
7946  $active_id = $this->getActiveIdOfUser($user_id);
7947  if ($active_id > 0)
7948  {
7949  $starting_time = $this->getStartingTimeOfUser($active_id);
7950  }
7951  $notimeleft = FALSE;
7952  if ($starting_time !== FALSE)
7953  {
7955  {
7956  $notimeleft = TRUE;
7957  }
7958  }
7959  $result = TRUE;
7960  if (!$this->isTestFinishedToViewResults($active_id, $this->getTestSession($active_id)->getPass()) && ($this->getScoreReporting() == REPORT_AFTER_TEST))
7961  {
7962  $result = FALSE;
7963  }
7964  if (($this->endingTimeReached()) || $notimeleft) $result = TRUE;
7965  $result = $result & $this->canViewResults();
7966  return $result;
7967  }
7968 
7969  function canEditMarks()
7970  {
7971  $total = $this->evalTotalPersons();
7972  if ($total > 0)
7973  {
7974  if ($this->getReportingDate())
7975  {
7976  if (preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $this->getReportingDate(), $matches))
7977  {
7978  $epoch_time = mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
7979  $now = mktime();
7980  if ($now < $epoch_time)
7981  {
7982  return true;
7983  }
7984  }
7985  }
7986  return false;
7987  }
7988  else
7989  {
7990  return true;
7991  }
7992  }
7993 
8001  function getStartingTimeOfUser($active_id)
8002  {
8003  global $ilDB;
8004 
8005  if ($active_id < 1) return FALSE;
8006  $pass = ($this->getResetProcessingTime()) ? $this->_getPass($active_id) : 0;
8007  $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",
8008  array('integer', 'integer'),
8009  array($active_id, $pass)
8010  );
8011  if ($result->numRows())
8012  {
8013  $row = $ilDB->fetchAssoc($result);
8014  if (preg_match("/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/", $row["started"], $matches))
8015  {
8016  return mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
8017  }
8018  else
8019  {
8020  return mktime();
8021  }
8022  }
8023  else
8024  {
8025  return mktime();
8026  }
8027  }
8028 
8038  {
8039  if ($this->getEnableProcessingTime())
8040  {
8042  $now = mktime();
8043  if ($now > ($starting_time + $processing_time))
8044  {
8045  return TRUE;
8046  }
8047  else
8048  {
8049  return FALSE;
8050  }
8051  }
8052  else
8053  {
8054  return FALSE;
8055  }
8056  }
8057 
8058  function &getTestQuestions()
8059  {
8060  global $ilDB;
8061  $query_result = $ilDB->queryF("SELECT qpl_questions.*, qpl_qst_type.type_tag, tst_test_question.sequence FROM qpl_questions, qpl_qst_type, tst_test_question WHERE qpl_questions.question_type_fi = qpl_qst_type.question_type_id AND tst_test_question.test_fi = %s AND tst_test_question.question_fi = qpl_questions.question_id ORDER BY tst_test_question.sequence",
8062  array('integer'),
8063  array($this->getTestId())
8064  );
8065  $removableQuestions = array();
8066  while ($row = $ilDB->fetchAssoc($query_result))
8067  {
8068  array_push($removableQuestions, $row);
8069  }
8070  return $removableQuestions;
8071  }
8072 
8080  {
8081  return ($this->shuffle_questions) ? 1 : 0;
8082  }
8083 
8090  function setShuffleQuestions($a_shuffle)
8091  {
8092  $this->shuffle_questions = ($a_shuffle) ? 1 : 0;
8093  }
8094 
8108  {
8109  return ($this->show_summary) ? $this->show_summary : 0;
8110  }
8111 
8124  function setListOfQuestionsSettings($a_value = 0)
8125  {
8126  $this->show_summary = $a_value;
8127  }
8128 
8136  {
8137  if (($this->show_summary & 1) > 0)
8138  {
8139  return TRUE;
8140  }
8141  else
8142  {
8143  return FALSE;
8144  }
8145  }
8146 
8153  function setListOfQuestions($a_value = TRUE)
8154  {
8155  if ($a_value)
8156  {
8157  $this->show_summary = 1;
8158  }
8159  else
8160  {
8161  $this->show_summary = 0;
8162  }
8163  }
8164 
8172  {
8173  if (($this->show_summary & 2) > 0)
8174  {
8175  return TRUE;
8176  }
8177  else
8178  {
8179  return FALSE;
8180  }
8181  }
8182 
8189  function setListOfQuestionsStart($a_value = TRUE)
8190  {
8191  if ($a_value && $this->getListOfQuestions())
8192  {
8193  $this->show_summary = $this->show_summary | 2;
8194  }
8195  if (!$a_value && $this->getListOfQuestions())
8196  {
8197  if ($this->getListOfQuestionsStart())
8198  {
8199  $this->show_summary = $this->show_summary ^ 2;
8200  }
8201  }
8202  }
8203 
8211  {
8212  if (($this->show_summary & 4) > 0)
8213  {
8214  return TRUE;
8215  }
8216  else
8217  {
8218  return FALSE;
8219  }
8220  }
8221 
8228  function setListOfQuestionsEnd($a_value = TRUE)
8229  {
8230  if ($a_value && $this->getListOfQuestions())
8231  {
8232  $this->show_summary = $this->show_summary | 4;
8233  }
8234  if (!$a_value && $this->getListOfQuestions())
8235  {
8236  if ($this->getListOfQuestionsEnd())
8237  {
8238  $this->show_summary = $this->show_summary ^ 4;
8239  }
8240  }
8241  }
8242 
8250  {
8251  if (($this->show_summary & 8) > 0)
8252  {
8253  return TRUE;
8254  }
8255  else
8256  {
8257  return FALSE;
8258  }
8259  }
8260 
8267  function setListOfQuestionsDescription($a_value = TRUE)
8268  {
8269  if ($a_value && $this->getListOfQuestions())
8270  {
8271  $this->show_summary = $this->show_summary | 8;
8272  }
8273  if (!$a_value && $this->getListOfQuestions())
8274  {
8275  if ($this->getListOfQuestionsDescription())
8276  {
8277  $this->show_summary = $this->show_summary ^ 8;
8278  }
8279  }
8280  }
8281 
8289  {
8290  return ($this->results_presentation) ? $this->results_presentation : 0;
8291  }
8292 
8300  {
8301  if (($this->results_presentation & 1) > 0)
8302  {
8303  return TRUE;
8304  }
8305  else
8306  {
8307  return FALSE;
8308  }
8309  }
8310 
8318  {
8319  if (($this->results_presentation & 2) > 0)
8320  {
8321  return TRUE;
8322  }
8323  else
8324  {
8325  return FALSE;
8326  }
8327  }
8328 
8336  {
8337  if (($this->results_presentation & 4) > 0)
8338  {
8339  return TRUE;
8340  }
8341  else
8342  {
8343  return FALSE;
8344  }
8345  }
8346 
8354  {
8355  if (($this->results_presentation & 8) > 0)
8356  {
8357  return TRUE;
8358  }
8359  else
8360  {
8361  return FALSE;
8362  }
8363  }
8364 
8372  {
8373  if (($this->results_presentation & 16) > 0)
8374  {
8375  return TRUE;
8376  }
8377  else
8378  {
8379  return FALSE;
8380  }
8381  }
8382 
8390  {
8391  if (($this->results_presentation & 32) > 0)
8392  {
8393  return TRUE;
8394  }
8395  else
8396  {
8397  return FALSE;
8398  }
8399  }
8400 
8406  {
8407  if (($this->results_presentation & 64) > 0)
8408  {
8409  return TRUE;
8410  }
8411  else
8412  {
8413  return FALSE;
8414  }
8415  }
8416 
8423  function setResultsPresentation($a_results_presentation = 3)
8424  {
8425  $this->results_presentation = $a_results_presentation;
8426  }
8427 
8436  function setShowPassDetails($a_details = 1)
8437  {
8438  if ($a_details)
8439  {
8440  $this->results_presentation = $this->results_presentation | 1;
8441  }
8442  else
8443  {
8444  if ($this->getShowPassDetails())
8445  {
8446  $this->results_presentation = $this->results_presentation ^ 1;
8447  }
8448  }
8449  }
8450 
8457  function setShowSolutionDetails($a_details = 1)
8458  {
8459  if ($a_details)
8460  {
8461  $this->results_presentation = $this->results_presentation | 2;
8462  }
8463  else
8464  {
8465  if ($this->getShowSolutionDetails())
8466  {
8467  $this->results_presentation = $this->results_presentation ^ 2;
8468  }
8469  }
8470  }
8471 
8478  function canShowSolutionPrintview($user_id = NULL)
8479  {
8480  return $this->getShowSolutionPrintview();
8481  }
8482 
8489  function setShowSolutionPrintview($a_printview = 1)
8490  {
8491  if ($a_printview)
8492  {
8493  $this->results_presentation = $this->results_presentation | 4;
8494  }
8495  else
8496  {
8497  if ($this->getShowSolutionPrintview())
8498  {
8499  $this->results_presentation = $this->results_presentation ^ 4;
8500  }
8501  }
8502  }
8503 
8510  function setShowSolutionFeedback($a_feedback = TRUE)
8511  {
8512  if ($a_feedback)
8513  {
8514  $this->results_presentation = $this->results_presentation | 8;
8515  }
8516  else
8517  {
8518  if ($this->getShowSolutionFeedback())
8519  {
8520  $this->results_presentation = $this->results_presentation ^ 8;
8521  }
8522  }
8523  }
8524 
8531  function setShowSolutionAnswersOnly($a_full = TRUE)
8532  {
8533  if ($a_full)
8534  {
8535  $this->results_presentation = $this->results_presentation | 16;
8536  }
8537  else
8538  {
8539  if ($this->getShowSolutionAnswersOnly())
8540  {
8541  $this->results_presentation = $this->results_presentation ^ 16;
8542  }
8543  }
8544  }
8545 
8552  function setShowSolutionSignature($a_signature = FALSE)
8553  {
8554  if ($a_signature)
8555  {
8556  $this->results_presentation = $this->results_presentation | 32;
8557  }
8558  else
8559  {
8560  if ($this->getShowSolutionSignature())
8561  {
8562  $this->results_presentation = $this->results_presentation ^ 32;
8563  }
8564  }
8565  }
8566 
8573  function setShowSolutionSuggested($a_solution = FALSE)
8574  {
8575  if ($a_solution)
8576  {
8577  $this->results_presentation = $this->results_presentation | 64;
8578  }
8579  else
8580  {
8581  if ($this->getShowSolutionSuggested())
8582  {
8583  $this->results_presentation = $this->results_presentation ^ 64;
8584  }
8585  }
8586  }
8587 
8594  {
8595  // create a 5 character code
8596  $codestring = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
8597  mt_srand();
8598  $code = "";
8599  for ($i = 1; $i <=5; $i++)
8600  {
8601  $index = mt_rand(0, strlen($codestring)-1);
8602  $code .= substr($codestring, $index, 1);
8603  }
8604  // verify it against the database
8605  while ($this->isAccessCodeUsed($code))
8606  {
8607  $code = $this->createNewAccessCode();
8608  }
8609  return $code;
8610  }
8611 
8612  function isAccessCodeUsed($code)
8613  {
8614  global $ilDB;
8615 
8616  $result = $ilDB->queryF("SELECT anonymous_id FROM tst_active WHERE test_fi = %s AND anonymous_id = %s",
8617  array('integer', 'text'),
8618  array($this->getTestId(), $code)
8619  );
8620  return ($result->numRows() > 0) ? true : false;
8621  }
8622 
8623  public static function _getUserIdFromActiveId($active_id)
8624  {
8625  global $ilDB;
8626  $result = $ilDB->queryF("SELECT user_fi FROM tst_active WHERE active_id = %s",
8627  array('integer'),
8628  array($active_id)
8629  );
8630  if ($result->numRows())
8631  {
8632  $row = $ilDB->fetchAssoc($result);
8633  return $row["user_fi"];
8634  }
8635  else
8636  {
8637  return -1;
8638  }
8639  }
8640 
8642  {
8643  $id = $this->getTestId();
8644  if (!is_array($_SESSION["tst_access_code"]))
8645  {
8646  return "";
8647  }
8648  else
8649  {
8650  return $_SESSION["tst_access_code"]["$id"];
8651  }
8652  }
8653 
8654  function setAccessCodeSession($access_code)
8655  {
8656  $id = $this->getTestId();
8657  if (!is_array($_SESSION["tst_access_code"]))
8658  {
8659  $_SESSION["tst_access_code"] = array();
8660  }
8661  $_SESSION["tst_access_code"]["$id"] = $access_code;
8662  }
8663 
8665  {
8666  $id = $this->getTestId();
8667  unset($_SESSION["tst_access_code"]["$id"]);
8668  }
8669 
8670  function getAllowedUsers()
8671  {
8672  return ($this->allowedUsers) ? $this->allowedUsers : 0;
8673  }
8674 
8675  function setAllowedUsers($a_allowed_users)
8676  {
8677  $this->allowedUsers = $a_allowed_users;
8678  }
8679 
8681  {
8682  return ($this->allowedUsersTimeGap) ? $this->allowedUsersTimeGap : 0;
8683  }
8684 
8685  function setAllowedUsersTimeGap($a_allowed_users_time_gap)
8686  {
8687  $this->allowedUsersTimeGap = $a_allowed_users_time_gap;
8688  }
8689 
8691  {
8692  global $ilDB;
8693 
8694  $nr_of_users = $this->getAllowedUsers();
8695  $time_gap = ($this->getAllowedUsersTimeGap()) ? $this->getAllowedUsersTimeGap() : 60;
8696  if (($nr_of_users > 0) && ($time_gap > 0))
8697  {
8698  $now = mktime();
8699  $time_border = $now - $time_gap;
8700  $str_time_border = strftime("%Y%m%d%H%M%S", $time_border);
8701  $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",
8702  array('integer', 'integer'),
8703  array($time_border, $this->getTestId())
8704  );
8705  if ($result->numRows() >= $nr_of_users)
8706  {
8707  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
8709  {
8710  $this->logAction($this->lng->txtlng("assessment", "log_could_not_enter_test_due_to_simultaneous_users", ilObjAssessmentFolder::_getLogLanguage()));
8711  }
8712  return FALSE;
8713  }
8714  else
8715  {
8716  return TRUE;
8717  }
8718  }
8719  return TRUE;
8720  }
8721 
8722  function _getLastAccess($active_id)
8723  {
8724  global $ilDB;
8725 
8726  $result = $ilDB->queryF("SELECT finished FROM tst_times WHERE active_fi = %s ORDER BY finished DESC",
8727  array('integer'),
8728  array($active_id)
8729  );
8730  if ($result->numRows())
8731  {
8732  $row = $ilDB->fetchAssoc($result);
8733  return $row["finished"];
8734  }
8735  return "";
8736  }
8737 
8745  function isHTML($a_text)
8746  {
8747  if (preg_match("/<[^>]*?>/", $a_text))
8748  {
8749  return TRUE;
8750  }
8751  else
8752  {
8753  return FALSE;
8754  }
8755  }
8756 
8764  function QTIMaterialToString($a_material)
8765  {
8766  $result = "";
8767  for ($i = 0; $i < $a_material->getMaterialCount(); $i++)
8768  {
8769  $material = $a_material->getMaterial($i);
8770  if (strcmp($material["type"], "mattext") == 0)
8771  {
8772  $result .= $material["material"]->getContent();
8773  }
8774  if (strcmp($material["type"], "matimage") == 0)
8775  {
8776  $matimage = $material["material"];
8777  if (preg_match("/(il_([0-9]+)_mob_([0-9]+))/", $matimage->getLabel(), $matches))
8778  {
8779  // import an mediaobject which was inserted using tiny mce
8780  if (!is_array($_SESSION["import_mob_xhtml"])) $_SESSION["import_mob_xhtml"] = array();
8781  array_push($_SESSION["import_mob_xhtml"], array("mob" => $matimage->getLabel(), "uri" => $matimage->getUri()));
8782  }
8783  }
8784  }
8785  global $ilLog;
8786  $ilLog->write(print_r($_SESSION["import_mob_xhtml"], true));
8787  return $result;
8788  }
8789 
8798  function addQTIMaterial(&$a_xml_writer, $a_material)
8799  {
8800  include_once "./Services/RTE/classes/class.ilRTE.php";
8801  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
8802 
8803  $a_xml_writer->xmlStartTag("material");
8804  $attrs = array(
8805  "texttype" => "text/plain"
8806  );
8807  if ($this->isHTML($a_material))
8808  {
8809  $attrs["texttype"] = "text/xhtml";
8810  }
8811  $a_xml_writer->xmlElement("mattext", $attrs, ilRTE::_replaceMediaObjectImageSrc($a_material, 0));
8812 
8813  $mobs = ilObjMediaObject::_getMobsOfObject("tst:html", $this->getId());
8814  foreach ($mobs as $mob)
8815  {
8816  $moblabel = "il_" . IL_INST_ID . "_mob_" . $mob;
8817  if (strpos($a_material, "mm_$mob") !== FALSE)
8818  {
8819  if (ilObjMediaObject::_exists($mob))
8820  {
8821  $mob_obj =& new ilObjMediaObject($mob);
8822  $imgattrs = array(
8823  "label" => $moblabel,
8824  "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle()
8825  );
8826  }
8827  $a_xml_writer->xmlElement("matimage", $imgattrs, NULL);
8828  }
8829  }
8830  $a_xml_writer->xmlEndTag("material");
8831  }
8832 
8839  function prepareTextareaOutput($txt_output, $prepare_for_latex_output = FALSE)
8840  {
8841  include_once "./Services/Utilities/classes/class.ilUtil.php";
8842  return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output);
8843  }
8844 
8851  function saveCertificateVisibility($a_value)
8852  {
8853  global $ilDB;
8854 
8855  $affectedRows = $ilDB->manipulateF("UPDATE tst_tests SET certificate_visibility = %s, tstamp = %s WHERE test_id = %s",
8856  array('text', 'integer', 'integer'),
8857  array($a_value, time(), $this->getTestId())
8858  );
8859  }
8860 
8868  {
8869  return (strlen($this->certificate_visibility)) ? $this->certificate_visibility : 0;
8870  }
8871 
8878  function setCertificateVisibility($a_value)
8879  {
8880  $this->certificate_visibility = $a_value;
8881  }
8882 
8889  function getAnonymity()
8890  {
8891  return ($this->anonymity) ? 1 : 0;
8892  }
8893 
8900  function setAnonymity($a_value = 0)
8901  {
8902  switch ($a_value)
8903  {
8904  case 1:
8905  $this->anonymity = 1;
8906  break;
8907  default:
8908  $this->anonymity = 0;
8909  break;
8910  }
8911  }
8912 
8919  function getShowCancel()
8920  {
8921  return ($this->show_cancel) ? 1 : 0;
8922  }
8923 
8930  function setShowCancel($a_value = 1)
8931  {
8932  switch ($a_value)
8933  {
8934  case 1:
8935  $this->show_cancel = 1;
8936  break;
8937  default:
8938  $this->show_cancel = 0;
8939  break;
8940  }
8941  }
8942 
8949  function getShowMarker()
8950  {
8951  return ($this->show_marker) ? 1 : 0;
8952  }
8953 
8960  function setShowMarker($a_value = 1)
8961  {
8962  switch ($a_value)
8963  {
8964  case 1:
8965  $this->show_marker = 1;
8966  break;
8967  default:
8968  $this->show_marker = 0;
8969  break;
8970  }
8971  }
8972 
8980  {
8981  return ($this->fixed_participants) ? 1 : 0;
8982  }
8983 
8990  function setFixedParticipants($a_value = 1)
8991  {
8992  switch ($a_value)
8993  {
8994  case 1:
8995  $this->fixed_participants = 1;
8996  break;
8997  default:
8998  $this->fixed_participants = 0;
8999  break;
9000  }
9001  }
9002 
9010  function _lookupAnonymity($a_obj_id)
9011  {
9012  global $ilDB;
9013 
9014  $result = $ilDB->queryF("SELECT anonymity FROM tst_tests WHERE obj_fi = %s",
9015  array('integer'),
9016  array($a_obj_id)
9017  );
9018  while($row = $ilDB->fetchAssoc($result))
9019  {
9020  return $row['anonymity'];
9021  }
9022  return 0;
9023  }
9024 
9032  function _lookupRandomTestFromActiveId($active_id)
9033  {
9034  global $ilDB;
9035 
9036  $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",
9037  array('integer'),
9038  array($active_id)
9039  );
9040  while($row = $ilDB->fetchAssoc($result))
9041  {
9042  return $row['random_test'];
9043  }
9044  return 0;
9045  }
9046 
9055  function userLookupFullName($user_id, $overwrite_anonymity = FALSE, $sorted_order = FALSE, $suffix = "")
9056  {
9057  if ($this->getAnonymity() && !$overwrite_anonymity)
9058  {
9059  return $this->lng->txt("unknown") . $suffix;
9060  }
9061  else
9062  {
9063  include_once './Services/User/classes/class.ilObjUser.php';
9064  $uname = ilObjUser::_lookupName($user_id);
9065  if (strlen($uname["firstname"].$uname["lastname"]) == 0) $uname["firstname"] = $this->lng->txt("deleted_user");
9066  if ($sorted_order)
9067  {
9068  return trim($uname["lastname"] . ", " . $uname["firstname"]) . $suffix;
9069  }
9070  else
9071  {
9072  return trim($uname["firstname"] . " " . $uname["lastname"]) . $suffix;
9073  }
9074  }
9075  }
9076 
9084  function getStartTestLabel($active_id)
9085  {
9086  if ($this->getNrOfTries() == 1)
9087  {
9088  return $this->lng->txt("tst_start_test");
9089  }
9090  $active_pass = $this->_getPass($active_id);
9091  $res = $this->getNrOfResultsForPass($active_id, $active_pass);
9092  if ($res == 0)
9093  {
9094  if ($active_pass == 0)
9095  {
9096  return $this->lng->txt("tst_start_test");
9097  }
9098  else
9099  {
9100  return $this->lng->txt("tst_start_new_test_pass");
9101  }
9102  }
9103  else
9104  {
9105  return $this->lng->txt("tst_resume_test");
9106  }
9107  }
9108 
9117  function &getAvailableDefaults($sortby = "name", $sortorder = "asc")
9118  {
9119  global $ilDB;
9120  global $ilUser;
9121 
9122  $result = $ilDB->queryF("SELECT * FROM tst_test_defaults WHERE user_fi = %s ORDER BY $sortby $sortorder",
9123  array('integer'),
9124  array($ilUser->getId())
9125  );
9126  $defaults = array();
9127  while ($row = $ilDB->fetchAssoc($result))
9128  {
9129  $defaults[$row["test_defaults_id"]] = $row;
9130  }
9131  return $defaults;
9132  }
9133 
9141  function &getTestDefaults($test_defaults_id)
9142  {
9143  global $ilDB;
9144 
9145  $result = $ilDB->queryF("SELECT * FROM tst_test_defaults WHERE test_defaults_id = %s",
9146  array('integer'),
9147  array($test_defaults_id)
9148  );
9149  if ($result->numRows() == 1)
9150  {
9151  $row = $ilDB->fetchAssoc($result);
9152  return $row;
9153  }
9154  else
9155  {
9156  return NULL;
9157  }
9158  }
9159 
9166  function deleteDefaults($test_default_id)
9167  {
9168  global $ilDB;
9169  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_defaults WHERE test_defaults_id = %s",
9170  array('integer'),
9171  array($test_default_id)
9172  );
9173  }
9174 
9181  function addDefaults($a_name)
9182  {
9183  global $ilDB;
9184  global $ilUser;
9185  $testsettings = array(
9186  "TitleOutput" => $this->getTitleOutput(),
9187  "PassScoring" => $this->getPassScoring(),
9188  "Introduction" => $this->getIntroduction(),
9189  "FinalStatement" => $this->getFinalStatement(),
9190  "ShowInfo" => $this->getShowInfo(),
9191  "ForceJS" => $this->getForceJS(),
9192  "CustomStyle" => $this->getCustomStyle(),
9193  "ShowFinalStatement" => $this->getShowFinalStatement(),
9194  "SequenceSettings" => $this->getSequenceSettings(),
9195  "ScoreReporting" => $this->getScoreReporting(),
9196  "InstantFeedbackSolution" => $this->getInstantFeedbackSolution(),
9197  "AnswerFeedback" => $this->getAnswerFeedback(),
9198  "AnswerFeedbackPoints" => $this->getAnswerFeedbackPoints(),
9199  "ResultsPresentation" => $this->getResultsPresentation(),
9200  "Anonymity" => $this->getAnonymity(),
9201  "ShowCancel" => $this->getShowCancel(),
9202  "ShowMarker" => $this->getShowMarker(),
9203  "ReportingDate" => $this->getReportingDate(),
9204  "NrOfTries" => $this->getNrOfTries(),
9205  "Kiosk" => $this->getKiosk(),
9206  "UsePreviousAnswers" => $this->getUsePreviousAnswers(),
9207  "ProcessingTime" => $this->getProcessingTime(),
9208  "EnableProcessingTime" => $this->getEnableProcessingTime(),
9209  "ResetProcessingTime" => $this->getResetProcessingTime(),
9210  "StartingTime" => $this->getStartingTime(),
9211  "EndingTime" => $this->getEndingTime(),
9212  "ECTSOutput" => $this->getECTSOutput(),
9213  "ECTSFX" => $this->getECTSFX(),
9214  "ECTSGrades" => $this->getECTSGrades(),
9215  "isRandomTest" => $this->isRandomTest(),
9216  "RandomQuestionCount" => $this->getRandomQuestionCount(),
9217  "CountSystem" => $this->getCountSystem(),
9218  "MCScoring" => $this->getMCScoring(),
9219  "mailnotification" => $this->getMailNotification(),
9220  "ListOfQuestionsSettings" => $this->getListOfQuestionsSettings()
9221  );
9222  $next_id = $ilDB->nextId('tst_test_defaults');
9223  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_defaults (test_defaults_id, name, user_fi, defaults, marks, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
9224  array('integer', 'text', 'integer', 'text', 'text', 'integer'),
9225  array($next_id, $a_name, $ilUser->getId(), serialize($testsettings), serialize($this->mark_schema), time())
9226  );
9227  }
9228 
9236  function applyDefaults($test_defaults_id)
9237  {
9238  $total = $this->evalTotalPersons();
9239  $result = FALSE;
9240  if (($this->getQuestionCount() == 0) && ($total == 0))
9241  {
9242  // only apply if there are no questions added and not user datasets exist
9243  $defaults =& $this->getTestDefaults($test_defaults_id);
9244  $testsettings = unserialize($defaults["defaults"]);
9245  include_once "./Modules/Test/classes/class.assMarkSchema.php";
9246  $this->mark_schema = unserialize($defaults["marks"]);
9247  $this->setTitleOutput($testsettings["TitleOutput"]);
9248  $this->setPassScoring($testsettings["PassScoring"]);
9249  $this->setIntroduction($testsettings["Introduction"]);
9250  $this->setFinalStatement($testsettings["FinalStatement"]);
9251  $this->setShowInfo($testsettings["ShowInfo"]);
9252  $this->setForceJS($testsettings["ForceJS"]);
9253  $this->setCustomStyle($testsettings["CustomStyle"]);
9254  $this->setShowFinalStatement($testsettings["ShowFinalStatement"]);
9255  $this->setSequenceSettings($testsettings["SequenceSettings"]);
9256  $this->setScoreReporting($testsettings["ScoreReporting"]);
9257  $this->setInstantFeedbackSolution($testsettings["InstantFeedbackSolution"]);
9258  $this->setAnswerFeedback($testsettings["AnswerFeedback"]);
9259  $this->setAnswerFeedbackPoints($testsettings["AnswerFeedbackPoints"]);
9260  $this->setResultsPresentation($testsettings["ResultsPresentation"]);
9261  $this->setAnonymity($testsettings["Anonymity"]);
9262  $this->setShowCancel($testsettings["ShowCancel"]);
9263  $this->setShowMarker($testsettings["ShowMarker"]);
9264  $this->setReportingDate($testsettings["ReportingDate"]);
9265  $this->setNrOfTries($testsettings["NrOfTries"]);
9266  $this->setUsePreviousAnswers($testsettings["UsePreviousAnswers"]);
9267  $this->setProcessingTime($testsettings["ProcessingTime"]);
9268  $this->setResetProcessingTime($testsettings["ResetProcessingTime"]);
9269  $this->setEnableProcessingTime($testsettings["EnableProcessingTime"]);
9270  $this->setStartingTime($testsettings["StartingTime"]);
9271  $this->setKiosk($testsettings["Kiosk"]);
9272  $this->setEndingTime($testsettings["EndingTime"]);
9273  $this->setECTSOutput($testsettings["ECTSOutput"]);
9274  $this->setECTSFX($testsettings["ECTSFX"]);
9275  $this->setECTSGrades($testsettings["ECTSGrades"]);
9276  $this->setRandomTest($testsettings["isRandomTest"]);
9277  $this->setRandomQuestionCount($testsettings["RandomQuestionCount"]);
9278  $this->setCountSystem($testsettings["CountSystem"]);
9279  $this->setMCScoring($testsettings["MCScoring"]);
9280  $this->setMailNotification($testsettings["mailnotification"]);
9281  $this->setListOfQuestionsSettings($testsettings["ListOfQuestionsSettings"]);
9282  $this->saveToDb();
9283  $result = TRUE;
9284  }
9285  return $result;
9286  }
9287 
9295  function processPrintoutput2FO($print_output)
9296  {
9297  if (extension_loaded("tidy"))
9298  {
9299  $config = array(
9300  "indent" => false,
9301  "output-xml" => true,
9302  "numeric-entities" => true
9303  );
9304  $tidy = new tidy();
9305  $tidy->parseString($print_output, $config, 'utf8');
9306  $tidy->cleanRepair();
9307  $print_output = tidy_get_output($tidy);
9308  $print_output = preg_replace("/^.*?(<html)/", "\\1", $print_output);
9309  }
9310  else
9311  {
9312  $print_output = str_replace("&nbsp;", "&#160;", $print_output);
9313  $print_output = str_replace("&otimes;", "X", $print_output);
9314  }
9315  $xsl = file_get_contents("./Modules/Test/xml/question2fo.xsl");
9316  $args = array( '/_xml' => $print_output, '/_xsl' => $xsl );
9317  $xh = xslt_create();
9318  $params = array();
9319  $output = xslt_process($xh, "arg:/_xml", "arg:/_xsl", NULL, $args, $params);
9320  xslt_error($xh);
9321  xslt_free($xh);
9322  return $output;
9323  }
9324 
9331  public function deliverPDFfromHTML($content, $title = NULL)
9332  {
9333  $content = preg_replace("/href=\".*?\"/", "", $content);
9334  $printbody = new ilTemplate("tpl.il_as_tst_print_body.html", TRUE, TRUE, "Modules/Test");
9335  $printbody->setVariable("TITLE", ilUtil::prepareFormOutput($this->getTitle()));
9336  $printbody->setVariable("ADM_CONTENT", $content);
9337  $printbody->setCurrentBlock("css_file");
9338  $printbody->setVariable("CSS_FILE", $this->getTestStyleLocation("filesystem"));
9339  $printbody->parseCurrentBlock();
9340  $printbody->setCurrentBlock("css_file");
9341  $printbody->setVariable("CSS_FILE", ilUtil::getStyleSheetLocation("filesystem", "delos.css"));
9342  $printbody->parseCurrentBlock();
9343  $printoutput = $printbody->get();
9344  $html = str_replace("href=\"./", "href=\"" . ILIAS_HTTP_PATH . "/", $printoutput);
9345  $html = preg_replace("/<div id=\"dontprint\">.*?<\\/div>/ims", "", $html);
9346  if (extension_loaded("tidy"))
9347  {
9348  $config = array(
9349  "indent" => false,
9350  "output-xml" => true,
9351  "numeric-entities" => true
9352  );
9353  $tidy = new tidy();
9354  $tidy->parseString($html, $config, 'utf8');
9355  $tidy->cleanRepair();
9356  $html = tidy_get_output($tidy);
9357  $html = preg_replace("/^.*?(<html)/", "\\1", $html);
9358  }
9359  else
9360  {
9361  $html = str_replace("&nbsp;", "&#160;", $html);
9362  $html = str_replace("&otimes;", "X", $html);
9363  }
9364  $this->deliverPDFfromFO($this->processPrintoutput2FO($html), $title);
9365  }
9366 
9373  public function deliverPDFfromFO($fo, $title = null)
9374  {
9375  global $ilLog;
9376 
9377  include_once "./Services/Utilities/classes/class.ilUtil.php";
9378  $fo_file = ilUtil::ilTempnam() . ".fo";
9379  $fp = fopen($fo_file, "w"); fwrite($fp, $fo); fclose($fp);
9380 
9381  include_once './Services/WebServices/RPC/classes/class.ilRpcClientFactory.php';
9382  try
9383  {
9384  $pdf_base64 = ilRpcClientFactory::factory('RPCTransformationHandler')->ilFO2PDF($fo);
9385  $filename = (strlen($title)) ? $title : $this->getTitle();
9386  ilUtil::deliverData($pdf_base64->scalar, ilUtil::getASCIIFilename($filename) . ".pdf", "application/pdf", false, true);
9387  return true;
9388  }
9389  catch(XML_RPC2_FaultException $e)
9390  {
9391  $ilLog->write(__METHOD__.': '.$e->getMessage());
9392  return false;
9393  }
9394  catch(Exception $e)
9395  {
9396  $ilLog->write(__METHOD__.': '.$e->getMessage());
9397  return false;
9398  }
9399 
9400  /*
9401  include_once "./Services/Transformation/classes/class.ilFO2PDF.php";
9402  $fo2pdf = new ilFO2PDF();
9403  $fo2pdf->setFOString($fo);
9404  $result = $fo2pdf->send();
9405  $filename = (strlen($title)) ? $title : $this->getTitle();
9406  ilUtil::deliverData($result, ilUtil::getASCIIFilename($filename) . ".pdf", "application/pdf", false, true);
9407  */
9408  }
9409 
9419  static function getManualFeedback($active_id, $question_id, $pass)
9420  {
9421  global $ilDB;
9422  $feedback = "";
9423  $result = $ilDB->queryF("SELECT feedback FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
9424  array('integer', 'integer', 'integer'),
9425  array($active_id, $question_id, $pass)
9426  );
9427  if ($result->numRows())
9428  {
9429  $row = $ilDB->fetchAssoc($result);
9430  include_once("./Services/RTE/classes/class.ilRTE.php");
9431  $feedback = ilRTE::_replaceMediaObjectImageSrc($row["feedback"], 1);
9432  }
9433  return $feedback;
9434  }
9435 
9446  function saveManualFeedback($active_id, $question_id, $pass, $feedback)
9447  {
9448  global $ilDB;
9449 
9450  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_manual_fb WHERE active_fi = %s AND question_fi = %s AND pass = %s",
9451  array('integer', 'integer', 'integer'),
9452  array($active_id, $question_id, $pass)
9453  );
9454 
9455  if (strlen($feedback))
9456  {
9457  $next_id = $ilDB->nextId('tst_manual_fb');
9458  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_manual_fb (manual_feedback_id, active_fi, question_fi, pass, feedback, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
9459  array('integer', 'integer', 'integer', 'integer', 'text', 'integer'),
9460  array($next_id, $active_id, $question_id, $pass, ilRTE::_replaceMediaObjectImageSrc($feedback, 0), time())
9461  );
9462  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
9464  {
9465  global $lng, $ilUser;
9466  include_once "./Modules/Test/classes/class.ilObjTestAccess.php";
9467  $username = ilObjTestAccess::_getParticipantData($active_id);
9468  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
9469  $this->logAction(sprintf($lng->txtlng("assessment", "log_manual_feedback", ilObjAssessmentFolder::_getLogLanguage()), $ilUser->getFullname() . " (" . $ilUser->getLogin() . ")", $username, assQuestion::_getQuestionTitle($question_id), $feedback));
9470  }
9471  }
9472  if (PEAR::isError($result))
9473  {
9474  global $ilias;
9475  $ilias->raiseError($result->getMessage());
9476  }
9477  else
9478  {
9479  return TRUE;
9480  }
9481  }
9482 
9491  {
9492  global $ilUser;
9493  if (strcmp($_GET["tst_javascript"], "0") == 0) return FALSE;
9494  if ($this->getForceJS()) return TRUE;
9495  $assessmentSetting = new ilSetting("assessment");
9496  return ($ilUser->getPref("tst_javascript") === FALSE) ? $assessmentSetting->get("use_javascript") : $ilUser->getPref("tst_javascript");
9497  }
9498 
9505  function &createTestSession()
9506  {
9507  global $ilUser;
9508 
9509  include_once "./Modules/Test/classes/class.ilTestSession.php";
9510  $testSession = FALSE;
9511  $testSession = new ilTestSession();
9512  $testSession->setTestId($this->getTestId());
9513  $testSession->setUserId($ilUser->getId());
9514  $testSession->setAnonymousId($_SESSION["tst_access_code"][$this->getTestId()]);
9515  $testSession->saveToDb();
9516  $this->testSession =& $testSession;
9517  return $this->testSession;
9518  }
9519 
9525  public function setTestId($a_id)
9526  {
9527  $this->test_id = $a_id;
9528  }
9529 
9537  function &setTestSession($active_id = "")
9538  {
9539  if (is_object($this->testSession) && ($this->testSession->getActiveId() > 0)) return $this->testSession;
9540 
9541  global $ilUser;
9542 
9543  include_once "./Modules/Test/classes/class.ilTestSession.php";
9544  $testSession = FALSE;
9545  if ($active_id > 0)
9546  {
9547  $testSession = new ilTestSession($active_id);
9548  }
9549  else
9550  {
9551  $testSession = new ilTestSession();
9552  $testSession->loadTestSession($this->getTestId(), $ilUser->getId(), $_SESSION["tst_access_code"][$this->getTestId()]);
9553  }
9554  $this->testSession =& $testSession;
9555  return $this->testSession;
9556  }
9557 
9564  function &getTestSession($active_id = "")
9565  {
9566  if (is_object($this->testSession) && ($this->testSession->getActiveId() > 0)) return $this->testSession;
9567  return $this->setTestSession($active_id);
9568  }
9569 
9570  function &createTestSequence($active_id, $pass, $shuffle)
9571  {
9572  include_once "./Modules/Test/classes/class.ilTestSequence.php";
9573  $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
9574  if (!$this->testSequence->hasSequence())
9575  {
9576  $this->testSequence->createNewSequence($this->getQuestionCount(), $shuffle);
9577  $this->testSequence->saveToDb();
9578  }
9579  }
9580 
9581  function &getTestSequence($active_id = "", $pass = "")
9582  {
9583  if (is_object($this->testSequence) && ($this->testSequence->getActiveId() > 0)) return $this->testSequence;
9584 
9585  include_once "./Modules/Test/classes/class.ilTestSequence.php";
9586  if (($active_id > 0) && (strlen($pass)))
9587  {
9588  $this->testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
9589  }
9590  else
9591  {
9592  $this->testSequence = new ilTestSequence($this->getTestSession()->getActiveId(), $this->getTestSession()->getPass(), $this->isRandomTest());
9593  }
9594  return $this->testSequence;
9595  }
9596 
9598  {
9599  if ($this->getTestSession()->getActiveId() > 0)
9600  {
9601  $result = $this->getTestResult($this->getTestSession()->getActiveId(), $this->getTestSession()->getPass(), TRUE);
9602  foreach ($result as $sequence => $question)
9603  {
9604  if (is_numeric($sequence))
9605  {
9606  if ($question["reached"] == $question["max"])
9607  {
9608  $this->getTestSequence()->hideQuestion($question["qid"]);
9609  }
9610  }
9611  }
9612  $this->getTestSequence()->saveToDb();
9613  }
9614  }
9615 
9624  function getDetailedTestResults($participants)
9625  {
9626  $results = array();
9627  if (count($participants))
9628  {
9629  foreach ($participants as $active_id => $user_rec)
9630  {
9631  $row = array();
9632  $reached_points = 0;
9633  $max_points = 0;
9634  foreach ($this->questions as $value)
9635  {
9636  $question =& ilObjTest::_instanciateQuestion($value);
9637  if (is_object($question))
9638  {
9639  $max_points += $question->getMaximumPoints();
9640  $reached_points += $question->getReachedPoints($active_id);
9641  if ($max_points > 0)
9642  {
9643  $percentvalue = $reached_points / $max_points;
9644  if ($percentvalue < 0) $percentvalue = 0.0;
9645  }
9646  else
9647  {
9648  $percentvalue = 0;
9649  }
9650  if ($this->getAnonymity())
9651  {
9652  $user_rec['firstname'] = "";
9653  $user_rec['lastname'] = $this->lng->txt("unknown");
9654  }
9655  $row = array(
9656  "user_id"=>$user_rec['usr_id'],
9657  "matriculation" => $user_rec['matriculation'],
9658  "lastname" => $user_rec['lastname'],
9659  "firstname" => $user_rec['firstname'],
9660  "login"=>$user_rec['login'],
9661  "question_id" => $question->getId(),
9662  "question_title" => $question->getTitle(),
9663  "reached_points" => $reached_points,
9664  "max_points" => $max_points
9665  );
9666  $results[] = $row;
9667  }
9668  }
9669  }
9670  }
9671  return $results;
9672  }
9673 
9678  {
9679  global $ilDB;
9680 
9681  $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",
9682  array('integer'),
9683  array($a_q_id)
9684  );
9685  $rec = $ilDB->fetchAssoc($result);
9686  return $rec["obj_id"];
9687  }
9688 
9695  function isPluginActive($a_pname)
9696  {
9697  global $ilPluginAdmin;
9698  if ($ilPluginAdmin->isActive(IL_COMP_MODULE, "TestQuestionPool", "qst", $a_pname))
9699  {
9700  return TRUE;
9701  }
9702  else
9703  {
9704  return FALSE;
9705  }
9706  }
9707 
9714  {
9715  $assessmentSetting = new ilSetting("assessment");
9716  $assessmentSetting->set("evalFields_" . $this->getId(), serialize($fields));
9717  }
9718 
9725  {
9726  $assessmentSetting = new ilSetting("assessment");
9727  $found = $assessmentSetting->get("evalFields_" . $this->getId());
9728  $fields = array();
9729  if (strlen($found)) $fields = unserialize($found);
9730  if (is_array($fields)) return $fields; else return array();
9731  }
9732 
9733  protected function getPassed($active_id)
9734  {
9735  global $ilDB;
9736 
9737  $result = $ilDB->queryF("SELECT passed FROM tst_result_cache WHERE active_fi = %s",
9738  array('integer'),
9739  array($active_id)
9740  );
9741  if ($result->numRows())
9742  {
9743  $row = $ilDB->fetchAssoc($result);
9744  return $row['passed'];
9745  }
9746  else
9747  {
9748  $counted_pass = ilObjTest::_getResultPass($active_id);
9749  $result_array =& $this->getTestResult($active_id, $counted_pass);
9750  return $result_array["test"]["passed"];
9751  }
9752  }
9753 
9759  function canShowCertificate($user_id, $active_id)
9760  {
9761  if ($this->canShowTestResults($user_id))
9762  {
9763  include_once "./Services/Certificate/classes/class.ilCertificate.php";
9764  include_once "./Modules/Test/classes/class.ilTestCertificateAdapter.php";
9765  $cert = new ilCertificate(new ilTestCertificateAdapter($this));
9766  if ($cert->isComplete())
9767  {
9768  $vis = $this->getCertificateVisibility();
9769  $showcert = FALSE;
9770  switch ($vis)
9771  {
9772  case 0:
9773  $showcert = TRUE;
9774  break;
9775  case 1:
9776  if ($this->getPassed($active_id))
9777  {
9778  $showcert = TRUE;
9779  }
9780  break;
9781  case 2:
9782  $showcert = FALSE;
9783  break;
9784  }
9785  if ($showcert)
9786  {
9787  return TRUE;
9788  }
9789  else
9790  {
9791  return FALSE;
9792  }
9793  }
9794  else
9795  {
9796  return FALSE;
9797  }
9798  }
9799  else
9800  {
9801  return FALSE;
9802  }
9803  }
9804 
9811  {
9812  global $ilDB;
9813  $result = $ilDB->queryF("SELECT tst_test_result.active_fi, tst_test_result.question_fi, tst_test_result.pass FROM tst_test_result, tst_active, qpl_questions WHERE tst_active.active_id = tst_test_result.active_fi AND tst_active.test_fi = %s AND tst_test_result.question_fi = qpl_questions.question_id AND qpl_questions.original_id = %s",
9814  array('integer', 'integer'),
9815  array($test_id, $question_id)
9816  );
9817  $foundusers = array();
9818  while ($row = $ilDB->fetchAssoc($result))
9819  {
9820  if (!array_key_exists($row["active_fi"], $foundusers))
9821  {
9822  $foundusers[$row["active_fi"]] = array();
9823  }
9824  array_push($foundusers[$row["active_fi"]], array("pass" => $row["pass"], "qid" => $row["question_fi"]));
9825  }
9826  return $foundusers;
9827  }
9828 
9834  public function hasPDFProcessing()
9835  {
9836  global $ilias;
9837 
9838  include_once './Services/WebServices/RPC/classes/class.ilRPCServerSettings.php';
9839  if(ilRPCServerSettings::getInstance()->isEnabled())
9840  {
9841  return TRUE;
9842  }
9843  else
9844  {
9845  return FALSE;
9846  }
9847  }
9848 
9854  public function getAggregatedResultsData()
9855  {
9856  $data =& $this->getCompleteEvaluationData();
9857  $foundParticipants =& $data->getParticipants();
9858  $results = array("overview" => array(), "questions" => array());
9859  if (count($foundParticipants))
9860  {
9861  $results["overview"][$this->lng->txt("tst_eval_total_persons")] = count($foundParticipants);
9862  $total_finished = $this->evalTotalFinished();
9863  $results["overview"][$this->lng->txt("tst_eval_total_finished")] = $total_finished;
9864  $average_time = $this->evalTotalStartedAverageTime();
9865  $diff_seconds = $average_time;
9866  $diff_hours = floor($diff_seconds/3600);
9867  $diff_seconds -= $diff_hours * 3600;
9868  $diff_minutes = floor($diff_seconds/60);
9869  $diff_seconds -= $diff_minutes * 60;
9870  $results["overview"][$this->lng->txt("tst_eval_total_finished_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds);
9871  $total_passed = 0;
9872  $total_passed_reached = 0;
9873  $total_passed_max = 0;
9874  $total_passed_time = 0;
9875  foreach ($foundParticipants as $userdata)
9876  {
9877  if ($userdata->getPassed())
9878  {
9879  $total_passed++;
9880  $total_passed_reached += $userdata->getReached();
9881  $total_passed_max += $userdata->getMaxpoints();
9882  $total_passed_time += $userdata->getTimeOfWork();
9883  }
9884  }
9885  $average_passed_reached = $total_passed ? $total_passed_reached / $total_passed : 0;
9886  $average_passed_max = $total_passed ? $total_passed_max / $total_passed : 0;
9887  $average_passed_time = $total_passed ? $total_passed_time / $total_passed : 0;
9888  $results["overview"][$this->lng->txt("tst_eval_total_passed")] = $total_passed;
9889  $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);
9890  $average_time = $average_passed_time;
9891  $diff_seconds = $average_time;
9892  $diff_hours = floor($diff_seconds/3600);
9893  $diff_seconds -= $diff_hours * 3600;
9894  $diff_minutes = floor($diff_seconds/60);
9895  $diff_seconds -= $diff_minutes * 60;
9896  $results["overview"][$this->lng->txt("tst_eval_total_passed_average_time")] = sprintf("%02d:%02d:%02d", $diff_hours, $diff_minutes, $diff_seconds);
9897  }
9898 
9899  foreach ($data->getQuestionTitles() as $question_id => $question_title)
9900  {
9901  $answered = 0;
9902  $reached = 0;
9903  $max = 0;
9904  foreach ($foundParticipants as $userdata)
9905  {
9906  for ($i = 0; $i <= $userdata->getLastPass(); $i++)
9907  {
9908  if (is_object($userdata->getPass($i)))
9909  {
9910  $question =& $userdata->getPass($i)->getAnsweredQuestionByQuestionId($question_id);
9911  if (is_array($question))
9912  {
9913  $answered++;
9914  $reached += $question["reached"];
9915  $max += $question["points"];
9916  }
9917  }
9918  }
9919  }
9920  $percent = $max ? $reached/$max * 100.0 : 0;
9921  $counter++;
9922  $results["questions"][$question_id] = array(
9923  $question_title,
9924  sprintf("%.2f", $answered ? $reached / $answered : 0) . " " . strtolower($this->lng->txt("of")) . " " . sprintf("%.2f", $answered ? $max / $answered : 0),
9925  sprintf("%.2f", $percent) . "%",
9926  $answered,
9927  sprintf("%.2f", $answered ? $reached / $answered : 0),
9928  sprintf("%.2f", $answered ? $max / $answered : 0),
9929  $percent / 100.0
9930  );
9931  }
9932  return $results;
9933  }
9934 
9938  function getXMLZip()
9939  {
9940  include_once("./Modules/Test/classes/class.ilTestExport.php");
9941  $test_exp = new ilTestExport($this, "xml");
9942  return $test_exp->buildExportFile();
9943  }
9944 
9948  public function getMailNotification()
9949  {
9950  return $this->mailnotification;
9951  }
9952 
9958  public function setMailNotification($a_notification)
9959  {
9960  $this->mailnotification = $a_notification;
9961  }
9962 
9963  public function sendSimpleNotification($active_id)
9964  {
9965  include_once "./Services/Mail/classes/class.ilMail.php";
9966  $mail = new ilMail(ANONYMOUS_USER_ID);
9967 
9968  $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id));
9969  $message = new ilTemplate("tpl.il_as_tst_finish_notification_simple.html", TRUE, TRUE, "Modules/Test");
9970  $message->setVariable('TEXT_TEST_TITLE', $this->lng->txt('title'));
9971  $message->setVariable('VALUE_TEST_TITLE', $this->getTitle());
9972  $message->setVariable('TEXT_USER_NAME', $this->lng->txt('username'));
9973  $message->setVariable('VALUE_USER_NAME', $usr_data);
9974  $message->setVariable('TEXT_FINISH_TIME', $this->lng->txt('tst_finished'));
9976  $message->setVariable('VALUE_FINISH_TIME', ilDatePresentation::formatDate(new ilDateTime(time(), IL_CAL_UNIX)));
9977 
9978  $res = $mail->sendMail(
9979  ilObjUser::_lookupLogin($this->getOwner()), // to
9980  "", // cc
9981  "", // bcc
9982  sprintf($this->lng->txt('tst_user_finished_test'), $this->getTitle()), // subject
9983  $message->get(), // message
9984  array(), // attachments
9985  array('normal') // type
9986  );
9987  }
9988 
9989  public function sendAdvancedNotification($active_id)
9990  {
9991  include_once "./Services/Mail/classes/class.ilMail.php";
9992  $mail = new ilMail(ANONYMOUS_USER_ID);
9993 
9994  $usr_data = $this->userLookupFullName(ilObjTest::_getUserIdFromActiveId($active_id));
9995  $message = new ilTemplate("tpl.il_as_tst_finish_notification_simple.html", TRUE, TRUE, "Modules/Test");
9996  $message->setVariable('TEXT_TEST_TITLE', $this->lng->txt('title'));
9997  $message->setVariable('VALUE_TEST_TITLE', $this->getTitle());
9998  $message->setVariable('TEXT_USER_NAME', $this->lng->txt('username'));
9999  $message->setVariable('VALUE_USER_NAME', $usr_data);
10000  $message->setVariable('TEXT_FINISH_TIME', $this->lng->txt('tst_finished'));
10002  $message->setVariable('VALUE_FINISH_TIME', ilDatePresentation::formatDate(new ilDateTime(time(), IL_CAL_UNIX)));
10003 
10004  include_once "./Modules/Test/classes/class.ilTestExport.php";
10005  $exportObj = new ilTestExport($this, "results");
10006  $file = $exportObj->exportToExcel($deliver = FALSE, 'active_id', $active_id, $passedonly = FALSE);
10007  include_once "./classes/class.ilFileDataMail.php";
10008  $fd = new ilFileDataMail(ANONYMOUS_USER_ID);
10009  $fd->copyAttachmentFile($file, "result_" . $active_id . ".xls");
10010  $file_names[] = "result_" . $active_id . ".xls";
10011  $result = $mail->sendMail(
10012  ilObjUser::_lookupLogin($this->getOwner()), // to
10013  "", // cc
10014  "", // bcc
10015  sprintf($this->lng->txt('tst_user_finished_test'), $this->getTitle()), // subject
10016  $message->get(), // message
10017  count($file_names) ? $file_names : array(), // attachments
10018  array('normal') // type
10019  );
10020  if(count($file_names))
10021  {
10022  $fd->unlinkFiles($file_names);
10023  unset($fd);
10024  @unlink($file);
10025  }
10026  }
10027 
10028  function createRandomSolutions($number)
10029  {
10030  global $ilDB;
10031 
10032  // 1. get a user
10033  $query = "SELECT usr_id FROM usr_data";
10034  $result = $ilDB->query($query);
10035  while ($data = $ilDB->fetchAssoc($result))
10036  {
10037  $activequery = sprintf("SELECT user_fi FROM tst_active WHERE test_fi = %s AND user_fi = %s",
10038  $ilDB->quote($this->getTestId()),
10039  $ilDB->quote($data['usr_id'])
10040  );
10041  $activeresult = $ilDB->query($activequery);
10042  if ($activeresult->numRows() == 0)
10043  {
10044  $user_id = $data['usr_id'];
10045  if ($user_id != 13)
10046  {
10047  include_once "./Modules/Test/classes/class.ilTestSession.php";
10048  $testSession = FALSE;
10049  $testSession = new ilTestSession();
10050  $testSession->setTestId($this->getTestId());
10051  $testSession->setUserId($user_id);
10052  $testSession->saveToDb();
10053  $passes = ($this->getNrOfTries()) ? $this->getNrOfTries() : 10;
10054  $nr_of_passes = rand(1, $passes);
10055  $active_id = $testSession->getActiveId();
10056  for ($pass = 0; $pass < $nr_of_passes; $pass++)
10057  {
10058  include_once "./Modules/Test/classes/class.ilTestSequence.php";
10059  $testSequence = new ilTestSequence($active_id, $pass, $this->isRandomTest());
10060  if (!$testSequence->hasSequence())
10061  {
10062  $testSequence->createNewSequence($this->getQuestionCount(), $shuffle);
10063  $testSequence->saveToDb();
10064  }
10065  for ($seq = 1; $seq <= count($this->questions); $seq++)
10066  {
10067  $question_id = $testSequence->getQuestionForSequence($seq);
10068  $objQuestion = ilObjTest::_instanciateQuestion($question_id);
10069  $objQuestion->createRandomSolution($testSession->getActiveId(), $pass);
10070  }
10071  if ($pass < $nr_of_passes - 1)
10072  {
10073  $testSession->increasePass();
10074  $testSession->setLastSequence(0);
10075  $testSession->saveToDb();
10076  }
10077  else
10078  {
10079  $this->setActiveTestSubmitted($user_id);
10080  }
10081  }
10082  $number--;
10083  if ($number == 0) return;
10084  }
10085  }
10086  }
10087  }
10088 
10089  public function getResultsForActiveId($active_id)
10090  {
10091  global $ilDB;
10092 
10093  $result = $ilDB->queryF("SELECT * FROM tst_result_cache WHERE active_fi = %s",
10094  array('integer'),
10095  array($active_id)
10096  );
10097  if (!$result->numRows())
10098  {
10099  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
10101  $result = $ilDB->queryF("SELECT * FROM tst_result_cache WHERE active_fi = %s",
10102  array('integer'),
10103  array($active_id)
10104  );
10105  }
10106  $row = $ilDB->fetchAssoc($result);
10107  return $row;
10108 
10109  }
10110 } // END class.ilObjTest
10111 
10112 ?>