ILIAS  Release_4_4_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.assQuestion.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3 
4 include_once "./Modules/Test/classes/inc.AssessmentConstants.php";
5 
20 abstract class assQuestion
21 {
27  protected $id;
28 
34  protected $title;
35 
41  protected $comment;
42 
48  protected $owner;
49 
55  protected $author;
56 
62  protected $question;
63 
69  protected $points;
70 
76  protected $est_working_time;
77 
83  protected $shuffle;
84 
90  protected $test_id;
91 
97  protected $obj_id;
98 
104  protected $ilias;
105 
111  protected $tpl;
112 
118  protected $lng;
119 
125  protected $outputType;
126 
133 
139  protected $original_id;
140 
146  protected $page;
147 
151  private $nr_of_tries;
152 
156  private $arrData;
157 
162 
169  private $obligationsToBeConsidered = false;
170 
175  protected $external_id = '';
176 
181 
186 
193 
199  public $feedbackOBJ = null;
200 
206  var $prevent_rte_usage = false;
207 
214 
220  var $defaultnroftries = false;
221 
225  protected $questionChangeListeners = array();
226 
230  protected $processLocker;
231 
242  function __construct(
243  $title = "",
244  $comment = "",
245  $author = "",
246  $owner = -1,
247  $question = ""
248  )
249  {
250  global $ilias;
251  global $lng;
252  global $tpl;
253 
254  $this->ilias =& $ilias;
255  $this->lng =& $lng;
256  $this->tpl =& $tpl;
257 
258  $this->original_id = null;
259  $this->title = $title;
260  $this->comment = $comment;
261  $this->page = null;
262  $this->author = $author;
263  $this->setQuestion($question);
264  if (!$this->author)
265  {
266  $this->author = $this->ilias->account->fullname;
267  }
268  $this->owner = $owner;
269  if ($this->owner <= 0)
270  {
271  $this->owner = $this->ilias->account->id;
272  }
273  $this->id = -1;
274  $this->test_id = -1;
275  $this->suggested_solutions = array();
276  $this->shuffle = 1;
277  $this->nr_of_tries = "";
278  $this->setEstimatedWorkingTime(0,1,0);
279  $this->outputType = OUTPUT_HTML;
280  $this->arrData = array();
281  $this->setExternalId('');
282  }
283 
288  {
289  $this->processLocker = $processLocker;
290  }
291 
295  public function getProcessLocker()
296  {
297  return $this->processLocker;
298  }
299 
311  function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
312  {
313  include_once "./Modules/TestQuestionPool/classes/import/qti12/class." . $this->getQuestionType() . "Import.php";
314  $classname = $this->getQuestionType() . "Import";
315  $import = new $classname($this);
316  $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
317  }
318 
325  function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
326  {
327  include_once "./Modules/TestQuestionPool/classes/export/qti12/class." . $this->getQuestionType() . "Export.php";
328  $classname = $this->getQuestionType() . "Export";
329  $export = new $classname($this);
330  return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
331  }
332 
339  function isComplete()
340  {
341  return false;
342  }
343 
351  function questionTitleExists($questionpool_id, $title)
352  {
353  global $ilDB;
354 
355  $result = $ilDB->queryF("SELECT * FROM qpl_questions WHERE obj_fi = %s AND title = %s",
356  array('integer','text'),
357  array($questionpool_id, $title)
358  );
359  return ($result->numRows() == 1) ? TRUE : FALSE;
360  }
361 
369  function setTitle($title = "")
370  {
371  $this->title = $title;
372  }
373 
381  function setId($id = -1)
382  {
383  $this->id = $id;
384  }
385 
393  function setTestId($id = -1)
394  {
395  $this->test_id = $id;
396  }
397 
405  function setComment($comment = "")
406  {
407  $this->comment = $comment;
408  }
409 
418  {
419  $this->outputType = $outputType;
420  }
421 
422 
430  function setShuffle($shuffle = true)
431  {
432  if ($shuffle)
433  {
434  $this->shuffle = 1;
435  }
436  else
437  {
438  $this->shuffle = 0;
439  }
440  }
441 
451  function setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
452  {
453  $this->est_working_time = array("h" => (int)$hour, "m" => (int)$min, "s" => (int)$sec);
454  }
455 
463  function keyInArray($searchkey, $array)
464  {
465  if ($searchkey)
466  {
467  foreach ($array as $key => $value)
468  {
469  if (strcmp($key, $searchkey)==0)
470  {
471  return true;
472  }
473  }
474  }
475  return false;
476  }
477 
485  function setAuthor($author = "")
486  {
487  if (!$author)
488  {
489  $author = $this->ilias->account->fullname;
490  }
491  $this->author = $author;
492  }
493 
501  function setOwner($owner = "")
502  {
503  $this->owner = $owner;
504  }
505 
513  function getTitle()
514  {
515  return $this->title;
516  }
517 
525  function getId()
526  {
527  return $this->id;
528  }
529 
537  function getShuffle()
538  {
539  return $this->shuffle;
540  }
541 
549  function getTestId()
550  {
551  return $this->test_id;
552  }
553 
561  function getComment()
562  {
563  return $this->comment;
564  }
565 
573  function getOutputType()
574  {
575  return $this->outputType;
576  }
577 
584  public function supportsJavascriptOutput()
585  {
586  return FALSE;
587  }
588 
589  public function supportsNonJsOutput()
590  {
591  return true;
592  }
593 
594  public function requiresJsSwitch()
595  {
596  return $this->supportsJavascriptOutput() && $this->supportsNonJsOutput();
597  }
598 
607  {
608  if (!$this->est_working_time)
609  {
610  $this->est_working_time = array("h" => 0, "m" => 0, "s" => 0);
611  }
613  }
614 
622  function getAuthor()
623  {
624  return $this->author;
625  }
626 
634  function getOwner()
635  {
636  return $this->owner;
637  }
638 
646  function getObjId()
647  {
648  return $this->obj_id;
649  }
650 
658  function setObjId($obj_id = 0)
659  {
660  $this->obj_id = $obj_id;
661  }
662 
666  public function setExternalId($external_id)
667  {
668  $this->external_id = $external_id;
669  }
670 
674  public function getExternalId()
675  {
676  if(!strlen($this->external_id))
677  {
678  if($this->getId() > 0)
679  {
680  return 'il_' . IL_INST_ID . '_qst_' . $this->getId();
681  }
682  else
683  {
684  return uniqid('', true);
685  }
686  }
687  else
688  {
689  return $this->external_id;
690  }
691  }
692 
699  function _getMaximumPoints($question_id)
700  {
701  global $ilDB;
702 
703  $points = 0;
704  $result = $ilDB->queryF("SELECT points FROM qpl_questions WHERE question_id = %s",
705  array('integer'),
706  array($question_id)
707  );
708  if ($result->numRows() == 1)
709  {
710  $row = $ilDB->fetchAssoc($result);
711  $points = $row["points"];
712  }
713  return $points;
714  }
715 
722  function &_getQuestionInfo($question_id)
723  {
724  global $ilDB;
725 
726  $result = $ilDB->queryF("SELECT qpl_questions.*, qpl_qst_type.type_tag FROM qpl_qst_type, qpl_questions WHERE qpl_questions.question_id = %s AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id",
727  array('integer'),
728  array($question_id)
729  );
730  if ($result->numRows())
731  {
732  return $ilDB->fetchAssoc($result);
733  }
734  else return array();
735  }
736 
743  public static function _getSuggestedSolutionCount($question_id)
744  {
745  global $ilDB;
746 
747  $result = $ilDB->queryF("SELECT suggested_solution_id FROM qpl_sol_sug WHERE question_fi = %s",
748  array('integer'),
749  array($question_id)
750  );
751  return $result->numRows();
752  }
753 
760  public static function _getSuggestedSolutionOutput($question_id)
761  {
763  if (!is_object($question)) return "";
764  return $question->getSuggestedSolutionOutput();
765  }
766 
767  public function getSuggestedSolutionOutput()
768  {
769  $output = array();
770  foreach ($this->suggested_solutions as $solution)
771  {
772  switch ($solution["type"])
773  {
774  case "lm":
775  case "st":
776  case "pg":
777  case "git":
778  array_push($output, '<a href="' . assQuestion::_getInternalLinkHref($solution["internal_link"]) . '">' . $this->lng->txt("solution_hint") . '</a>');
779  break;
780  case "file":
781  $possible_texts = array_values(array_filter(array(
782  ilUtil::prepareFormOutput($solution['value']['filename']),
783  ilUtil::prepareFormOutput($solution['value']['name']),
784  $this->lng->txt('tst_show_solution_suggested')
785  )));
786  array_push($output, '<a href="' . $this->getSuggestedSolutionPathWeb() . $solution["value"]["name"] . '">' . $possible_texts[0] . '</a>');
787  break;
788  case "text":
789  array_push($output, $this->prepareTextareaOutput($solution["value"], true));
790  break;
791  }
792  }
793  return join($output, "<br />");
794  }
795 
804  function &_getSuggestedSolution($question_id, $subquestion_index = 0)
805  {
806  global $ilDB;
807 
808  $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s AND subquestion_index = %s",
809  array('integer','integer'),
810  array($question_id, $subquestion_index)
811  );
812  if ($result->numRows() == 1)
813  {
814  $row = $ilDB->fetchAssoc($result);
815  return array(
816  "internal_link" => $row["internal_link"],
817  "import_id" => $row["import_id"]
818  );
819  }
820  else
821  {
822  return array();
823  }
824  }
825 
831  public function getSuggestedSolutions()
832  {
834  }
835 
843  function _getReachedPoints($active_id, $question_id, $pass = NULL)
844  {
845  global $ilDB;
846 
847  $points = 0;
848  if (is_null($pass))
849  {
850  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
851  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
852  }
853  $result = $ilDB->queryF("SELECT * FROM tst_test_result WHERE active_fi = %s AND question_fi = %s AND pass = %s",
854  array('integer','integer','integer'),
855  array($active_id, $question_id, $pass)
856  );
857  if ($result->numRows() == 1)
858  {
859  $row = $ilDB->fetchAssoc($result);
860  $points = $row["points"];
861  }
862  return $points;
863  }
864 
873  function getReachedPoints($active_id, $pass = NULL)
874  {
875  return round($this->_getReachedPoints($active_id, $this->getId(), $pass), 2);
876  }
877 
884  function getMaximumPoints()
885  {
886  return $this->points;
887  }
888 
900  final public function getAdjustedReachedPoints($active_id, $pass = NULL)
901  {
902  if (is_null($pass))
903  {
904  include_once "./Modules/Test/classes/class.ilObjTest.php";
905  $pass = ilObjTest::_getPass($active_id);
906  }
907 
908  // determine reached points for submitted solution
909  $reached_points = $this->calculateReachedPoints($active_id, $pass);
910 
911  // deduct points for requested hints from reached points
912  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
913  $requestsStatisticData = ilAssQuestionHintTracking::getRequestStatisticDataByQuestionAndTestpass($this->getId(), $active_id, $pass);
914  $reached_points = $reached_points - $requestsStatisticData->getRequestsPoints();
915 
916  // adjust reached points regarding to tests scoring options
917  $reached_points = $this->adjustReachedPointsByScoringOptions($reached_points, $active_id, $pass);
918 
919  return $reached_points;
920  }
921 
931  final public function calculateResultsFromSolution($active_id, $pass = NULL, $obligationsEnabled = false)
932  {
933  global $ilDB, $ilUser;
934 
935  if( is_null($pass) )
936  {
937  include_once "./Modules/Test/classes/class.ilObjTest.php";
938  $pass = ilObjTest::_getPass($active_id);
939  }
940 
941  // determine reached points for submitted solution
942  $reached_points = $this->calculateReachedPoints($active_id, $pass);
943 
944  // deduct points for requested hints from reached points
945  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
946  $requestsStatisticData = ilAssQuestionHintTracking::getRequestStatisticDataByQuestionAndTestpass($this->getId(), $active_id, $pass);
947  $reached_points = $reached_points - $requestsStatisticData->getRequestsPoints();
948 
949  // adjust reached points regarding to tests scoring options
950  $reached_points = $this->adjustReachedPointsByScoringOptions($reached_points, $active_id, $pass);
951 
952  if( $obligationsEnabled && ilObjTest::isQuestionObligatory($this->getId()) )
953  {
954  $isAnswered = $this->isAnswered($active_id, $pass);
955  }
956  else
957  {
958  $isAnswered = true;
959  }
960 
961  if( is_null($reached_points) ) $reached_points = 0;
962 
963  $this->getProcessLocker()->requestUserQuestionResultUpdateLock();
964 
965  $query = "
966  DELETE FROM tst_test_result
967 
968  WHERE active_fi = %s
969  AND question_fi = %s
970  AND pass = %s
971  ";
972 
973  $affectedRows = $ilDB->manipulateF(
974  $query, array("integer", "integer", "integer"), array($active_id, $this->getId(), $pass)
975  );
976 
977  $next_id = $ilDB->nextId("tst_test_result");
978 
979  $ilDB->insert('tst_test_result', array(
980  'test_result_id' => array('integer', $next_id),
981  'active_fi' => array('integer', $active_id),
982  'question_fi' => array('integer', $this->getId()),
983  'pass' => array('integer', $pass),
984  'points' => array('float', $reached_points),
985  'tstamp' => array('integer', time()),
986  'hint_count' => array('integer', $requestsStatisticData->getRequestsCount()),
987  'hint_points' => array('float', $requestsStatisticData->getRequestsPoints()),
988  'answered' => array('integer', $isAnswered)
989  ));
990 
991  $this->getProcessLocker()->releaseUserQuestionResultUpdateLock();
992 
993  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
994 
996  {
997  $this->logAction(
998  sprintf(
999  $this->lng->txtlng(
1000  "assessment", "log_user_answered_question", ilObjAssessmentFolder::_getLogLanguage()
1001  ),
1002  $reached_points
1003  ),
1004  $active_id,
1005  $this->getId()
1006  );
1007  }
1008 
1009  // update test pass results
1010  $this->_updateTestPassResults($active_id, $pass, $obligationsEnabled, $this->getProcessLocker());
1011 
1012  // Update objective status
1013  include_once 'Modules/Course/classes/class.ilCourseObjectiveResult.php';
1014  ilCourseObjectiveResult::_updateObjectiveResult($ilUser->getId(),$active_id,$this->getId());
1015  }
1016 
1025  final public function persistWorkingState($active_id, $pass = NULL, $obligationsEnabled = false)
1026  {
1027  if( $pass === null )
1028  {
1029  require_once 'Modules/Test/classes/class.ilObjTest.php';
1030  $pass = ilObjTest::_getPass($active_id);
1031  }
1032 
1033  $this->getProcessLocker()->requestPersistWorkingStateLock();
1034 
1035  $saveStatus = $this->saveWorkingData($active_id, $pass);
1036 
1037  $this->calculateResultsFromSolution($active_id, $pass, $obligationsEnabled);
1038 
1039  $this->reworkWorkingData($active_id, $pass, $obligationsEnabled);
1040 
1041  $this->getProcessLocker()->releasePersistWorkingStateLock();
1042 
1043  return $saveStatus;
1044  }
1045 
1055  abstract public function saveWorkingData($active_id, $pass = NULL);
1056 
1066  abstract protected function reworkWorkingData($active_id, $pass, $obligationsAnswered);
1067 
1070  {
1071  global $ilDB;
1072 
1073  include_once "./Modules/Test/classes/class.ilObjTest.php";
1074  include_once "./Modules/Test/classes/class.assMarkSchema.php";
1075 
1076  $pass = ilObjTest::_getResultPass($active_id);
1077 
1078  $query = "
1079  SELECT tst_pass_result.*
1080  FROM tst_pass_result
1081  WHERE active_fi = %s
1082  AND pass = %s
1083  ";
1084 
1085  $result = $ilDB->queryF(
1086  $query, array('integer','integer'), array($active_id, $pass)
1087  );
1088 
1089  $row = $ilDB->fetchAssoc($result);
1090 
1091  $max = $row['maxpoints'];
1092  $reached = $row['points'];
1093 
1094  $obligationsAnswered = (int)$row['obligations_answered'];
1095 
1096  include_once "./Modules/Test/classes/class.assMarkSchema.php";
1097 
1098  $percentage = (!$max) ? 0 : ($reached / $max) * 100.0;
1099 
1100  $mark = ASS_MarkSchema::_getMatchingMarkFromActiveId($active_id, $percentage);
1101 
1102  $isPassed = ( $mark["passed"] ? 1 : 0 );
1103  $isFailed = ( !$mark["passed"] ? 1 : 0 );
1104 
1105  if( is_object($processLocker) )
1106  {
1107  $processLocker->requestUserTestResultUpdateLock();
1108  }
1109 
1110  $query = "
1111  DELETE FROM tst_result_cache
1112  WHERE active_fi = %s
1113  ";
1114 
1115  $affectedRows = $ilDB->manipulateF(
1116  $query, array('integer'), array($active_id)
1117  );
1118 
1119  $ilDB->insert('tst_result_cache', array(
1120  'active_fi'=> array('integer', $active_id),
1121  'pass'=> array('integer', strlen($pass) ? $pass : 0),
1122  'max_points'=> array('float', strlen($max) ? $max : 0),
1123  'reached_points'=> array('float', strlen($reached) ? $reached : 0),
1124  'mark_short'=> array('text', strlen($mark["short_name"]) ? $mark["short_name"] : " "),
1125  'mark_official'=> array('text', strlen($mark["official_name"]) ? $mark["official_name"] : " "),
1126  'passed'=> array('integer', $isPassed),
1127  'failed'=> array('integer', $isFailed),
1128  'tstamp'=> array('integer', time()),
1129  'hint_count'=> array('integer', $row['hint_count']),
1130  'hint_points'=> array('float', $row['hint_points']),
1131  'obligations_answered' => array('integer', $obligationsAnswered)
1132  ));
1133 
1134  if( is_object($processLocker) )
1135  {
1136  $processLocker->releaseUserTestResultUpdateLock();
1137  }
1138  }
1139 
1141  function _updateTestPassResults($active_id, $pass, $obligationsEnabled = false, ilAssQuestionProcessLocker $processLocker = null)
1142  {
1143  global $ilDB;
1144 
1145  include_once "./Modules/Test/classes/class.ilObjTest.php";
1146 
1149 
1150  // update test pass results
1151 
1152  $result = $ilDB->queryF("
1153  SELECT SUM(points) reachedpoints,
1154  SUM(hint_count) hint_count,
1155  SUM(hint_points) hint_points,
1156  COUNT(DISTINCT(question_fi)) answeredquestions
1157  FROM tst_test_result
1158  WHERE active_fi = %s
1159  AND pass = %s
1160  ",
1161  array('integer','integer'),
1162  array($active_id, $pass)
1163  );
1164 
1165  if ($result->numRows() > 0)
1166  {
1167  if( $obligationsEnabled )
1168  {
1169  $query = '
1170  SELECT count(*) cnt,
1171  min( answered ) answ
1172  FROM tst_test_question
1173  INNER JOIN tst_active
1174  ON active_id = %s
1175  AND tst_test_question.test_fi = tst_active.test_fi
1176  LEFT JOIN tst_test_result
1177  ON tst_test_result.active_fi = %s
1178  AND tst_test_result.pass = %s
1179  AND tst_test_question.question_fi = tst_test_result.question_fi
1180  WHERE obligatory = 1';
1181 
1182  $result_obligatory = $ilDB->queryF(
1183  $query, array('integer','integer','integer'), array($active_id, $active_id, $pass)
1184  );
1185 
1186  $row_obligatory = $ilDB->fetchAssoc($result_obligatory);
1187 
1188  if ($row_obligatory['cnt'] == 0)
1189  {
1190  $obligations_answered = 1;
1191  }
1192  else
1193  {
1194  $obligations_answered = (int) $row_obligatory['answ'];
1195  }
1196  }
1197  else
1198  {
1199  $obligations_answered = 1;
1200  }
1201 
1202  $row = $ilDB->fetchAssoc($result);
1203 
1204  if( $row['hint_count'] === null ) $row['hint_count'] = 0;
1205  if( $row['hint_points'] === null ) $row['hint_points'] = 0;
1206 
1207  $exam_identifier = ilObjTest::buildExamId( $active_id, $pass );
1208 
1209  if( is_object($processLocker) )
1210  {
1211  $processLocker->requestUserPassResultUpdateLock();
1212  }
1213 
1214  /*
1215  $query = "
1216  DELETE FROM tst_pass_result
1217 
1218  WHERE active_fi = %s
1219  AND pass = %s
1220  ";
1221 
1222  $affectedRows = $ilDB->manipulateF(
1223  $query, array('integer','integer'), array($active_id, $pass)
1224  );
1225  */
1227  $ilDB->replace('tst_pass_result',
1228  array(
1229  'active_fi' => array('integer', $active_id),
1230  'pass' => array('integer', strlen($pass) ? $pass : 0)),
1231  array(
1232  'points' => array('float', $row['reachedpoints'] ? $row['reachedpoints'] : 0),
1233  'maxpoints' => array('float', $data['points']),
1234  'questioncount' => array('integer', $data['count']),
1235  'answeredquestions' => array('integer', $row['answeredquestions']),
1236  'workingtime' => array('integer', $time),
1237  'tstamp' => array('integer', time()),
1238  'hint_count' => array('integer', $row['hint_count']),
1239  'hint_points' => array('float', $row['hint_points']),
1240  'obligations_answered' => array('integer', $obligations_answered),
1241  'exam_id' => array('text', $exam_identifier)
1242  )
1243  );
1244 
1245  /*
1246  $ilDB->insert('tst_pass_result', array(
1247  'active_fi' => array('integer', $active_id),
1248  'pass' => array('integer', strlen($pass) ? $pass : 0),
1249  'points' => array('float', $row['reachedpoints'] ? $row['reachedpoints'] : 0),
1250  'maxpoints' => array('float', $data['points']),
1251  'questioncount' => array('integer', $data['count']),
1252  'answeredquestions' => array('integer', $row['answeredquestions']),
1253  'workingtime' => array('integer', $time),
1254  'tstamp' => array('integer', time()),
1255  'hint_count' => array('integer', $row['hint_count']),
1256  'hint_points' => array('float', $row['hint_points']),
1257  'obligations_answered' => array('integer', $obligations_answered),
1258  'exam_id' => array('text', $exam_identifier)
1259  ));
1260  */
1261 
1262  if( is_object($processLocker) )
1263  {
1264  $this->getProcessLocker()->releaseUserPassResultUpdateLock();
1265  }
1266  }
1267 
1269 
1270  return array(
1271  'active_fi' => $active_id,
1272  'pass' => $pass,
1273  'points' => ($row["reachedpoints"]) ? $row["reachedpoints"] : 0,
1274  'maxpoints' => $data["points"],
1275  'questioncount' => $data["count"],
1276  'answeredquestions' => $row["answeredquestions"],
1277  'workingtime' => $time,
1278  'tstamp' => time(),
1279  'hint_count' => $row['hint_count'],
1280  'hint_points' => $row['hint_points'],
1281  'obligations_answered' => $obligations_answered,
1282  'exam_id' => $exam_identifier
1283  );
1284  }
1285 
1293  function logAction($logtext = "", $active_id = "", $question_id = "")
1294  {
1295  global $ilUser;
1296 
1297  $original_id = "";
1298  if (strcmp($question_id, "") != 0)
1299  {
1300  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
1301  $original_id = assQuestion::_getOriginalId($question_id);
1302  }
1303  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
1304  include_once "./Modules/Test/classes/class.ilObjTest.php";
1305  ilObjAssessmentFolder::_addLog($ilUser->id, ilObjTest::_getObjectIDFromActiveID($active_id), $logtext, $question_id, $original_id);
1306  }
1307 
1315  function _logAction($logtext = "", $active_id = "", $question_id = "")
1316  {
1317  global $ilUser;
1318 
1319  $original_id = "";
1320  if (strcmp($question_id, "") != 0)
1321  {
1322  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
1323  $original_id = assQuestion::_getOriginalId($question_id);
1324  }
1325  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
1326  include_once "./Modules/Test/classes/class.ilObjTest.php";
1327  ilObjAssessmentFolder::_addLog($ilUser->id, ilObjTest::_getObjectIDFromActiveID($active_id), $logtext, $question_id, $original_id);
1328  }
1329 
1337  function moveUploadedMediaFile($file, $name)
1338  {
1339  $mediatempdir = CLIENT_WEB_DIR . "/assessment/temp";
1340  if (!@is_dir($mediatempdir)) ilUtil::createDirectory($mediatempdir);
1341  $temp_name = tempnam($mediatempdir, $name . "_____");
1342  $temp_name = str_replace("\\", "/", $temp_name);
1343  @unlink($temp_name);
1344  if (!ilUtil::moveUploadedFile($file, $name, $temp_name))
1345  {
1346  return FALSE;
1347  }
1348  else
1349  {
1350  return $temp_name;
1351  }
1352  }
1353 
1360  return CLIENT_WEB_DIR . "/assessment/$this->obj_id/$this->id/solution/";
1361  }
1362 
1369  function getJavaPath() {
1370  return CLIENT_WEB_DIR . "/assessment/$this->obj_id/$this->id/java/";
1371  }
1372 
1379  function getImagePath($question_id = null, $object_id = null)
1380  {
1381  if( $question_id === null)
1382  {
1383  $question_id = $this->id;
1384  }
1385 
1386  if( $object_id === null)
1387  {
1388  $object_id = $this->obj_id;
1389  }
1390 
1391  return CLIENT_WEB_DIR . "/assessment/$object_id/$question_id/images/";
1392  }
1393 
1400  function getFlashPath()
1401  {
1402  return CLIENT_WEB_DIR . "/assessment/$this->obj_id/$this->id/flash/";
1403  }
1404 
1411  function getJavaPathWeb()
1412  {
1413  include_once "./Services/Utilities/classes/class.ilUtil.php";
1414  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/java/";
1416  }
1417 
1424  {
1425  include_once "./Services/Utilities/classes/class.ilUtil.php";
1426  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/solution/";
1428  }
1429 
1436  function getImagePathWeb()
1437  {
1438  if(!$this->export_image_path)
1439  {
1440  include_once "./Services/Utilities/classes/class.ilUtil.php";
1441  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/images/";
1443  }
1444  else
1445  {
1446  return $this->export_image_path;
1447  }
1448  }
1449 
1456  function getFlashPathWeb()
1457  {
1458  include_once "./Services/Utilities/classes/class.ilUtil.php";
1459  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/flash/";
1461  }
1462 
1470  function &getSolutionValues($active_id, $pass = NULL)
1471  {
1472  global $ilDB;
1473 
1474  $values = array();
1475 
1476  if (is_null($pass))
1477  {
1478  $pass = $this->getSolutionMaxPass($active_id);
1479  }
1480 
1481  $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s ORDER BY solution_id",
1482  array('integer','integer','integer'),
1483  array($active_id, $this->getId(), $pass)
1484  );
1485  while ($row = $ilDB->fetchAssoc($result))
1486  {
1487  array_push($values, $row);
1488  }
1489 
1490  return $values;
1491  }
1492 
1499  function isInUse($question_id = "")
1500  {
1501  global $ilDB;
1502 
1503  if ($question_id < 1) $question_id = $this->getId();
1504  $result = $ilDB->queryF("SELECT COUNT(qpl_questions.question_id) question_count FROM qpl_questions, tst_test_question WHERE qpl_questions.original_id = %s AND qpl_questions.question_id = tst_test_question.question_fi",
1505  array('integer'),
1506  array($question_id)
1507  );
1508  $row = $ilDB->fetchAssoc($result);
1509  $count = $row["question_count"];
1510 
1511  $result = $ilDB->queryF("SELECT DISTINCT tst_active.test_fi, qpl_questions.question_id FROM qpl_questions, tst_test_rnd_qst, tst_active WHERE qpl_questions.original_id = %s AND qpl_questions.question_id = tst_test_rnd_qst.question_fi AND tst_test_rnd_qst.active_fi = tst_active.active_id",
1512  array('integer'),
1513  array($question_id)
1514  );
1515  $count += $result->numRows();
1516 
1517  return $count;
1518  }
1519 
1526  function isClone($question_id = "")
1527  {
1528  global $ilDB;
1529 
1530  if ($question_id < 1) $question_id = $this->id;
1531  $result = $ilDB->queryF("SELECT original_id FROM qpl_questions WHERE question_id = %s",
1532  array('integer'),
1533  array($question_id)
1534  );
1535  $row = $ilDB->fetchAssoc($result);
1536  return ($row["original_id"] > 0) ? TRUE : FALSE;
1537  }
1538 
1545  function pcArrayShuffle($array)
1546  {
1547  $keys = array_keys($array);
1548  shuffle($keys);
1549  $result = array();
1550  foreach ($keys as $key)
1551  {
1552  $result[$key] = $array[$key];
1553  }
1554  return $result;
1555  }
1556 
1562  function getQuestionTypeFromDb($question_id)
1563  {
1564  global $ilDB;
1565 
1566  $result = $ilDB->queryF("SELECT qpl_qst_type.type_tag FROM qpl_qst_type, qpl_questions WHERE qpl_questions.question_id = %s AND qpl_questions.question_type_fi = qpl_qst_type.question_type_id",
1567  array('integer'),
1568  array($question_id)
1569  );
1570  $data = $ilDB->fetchAssoc($result);
1571  return $data["type_tag"];
1572  }
1573 
1581  {
1582  return "";
1583  }
1584 
1592  {
1593  return "";
1594  }
1595 
1602  function deleteAnswers($question_id)
1603  {
1604  global $ilDB;
1605  $answer_table_name = $this->getAnswerTableName();
1606  if (is_array($answer_table_name))
1607  {
1608  foreach ($answer_table_name as $table)
1609  {
1610  if (strlen($table))
1611  {
1612  $affectedRows = $ilDB->manipulateF("DELETE FROM $table WHERE question_fi = %s",
1613  array('integer'),
1614  array($question_id)
1615  );
1616  }
1617  }
1618  }
1619  else
1620  {
1621  if (strlen($answer_table_name))
1622  {
1623  $affectedRows = $ilDB->manipulateF("DELETE FROM $answer_table_name WHERE question_fi = %s",
1624  array('integer'),
1625  array($question_id)
1626  );
1627  }
1628  }
1629  }
1630 
1637  function deleteAdditionalTableData($question_id)
1638  {
1639  global $ilDB;
1640  $additional_table_name = $this->getAdditionalTableName();
1641  if (is_array($additional_table_name))
1642  {
1643  foreach ($additional_table_name as $table)
1644  {
1645  if (strlen($table))
1646  {
1647  $affectedRows = $ilDB->manipulateF("DELETE FROM $table WHERE question_fi = %s",
1648  array('integer'),
1649  array($question_id)
1650  );
1651  }
1652  }
1653  }
1654  else
1655  {
1656  if (strlen($additional_table_name))
1657  {
1658  $affectedRows = $ilDB->manipulateF("DELETE FROM $additional_table_name WHERE question_fi = %s",
1659  array('integer'),
1660  array($question_id)
1661  );
1662  }
1663  }
1664  }
1665 
1672  protected function deletePageOfQuestion($question_id)
1673  {
1674  include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
1675  $page = new ilAssQuestionPage($question_id);
1676  $page->delete();
1677  return true;
1678  }
1679 
1686  public function delete($question_id)
1687  {
1688  global $ilDB, $ilLog;
1689 
1690  if ($question_id < 1) return true; // nothing to do
1691 
1692  $result = $ilDB->queryF("SELECT obj_fi FROM qpl_questions WHERE question_id = %s",
1693  array('integer'),
1694  array($question_id)
1695  );
1696  if ($result->numRows() == 1)
1697  {
1698  $row = $ilDB->fetchAssoc($result);
1699  $obj_id = $row["obj_fi"];
1700  }
1701  else
1702  {
1703  return true; // nothing to do
1704  }
1705  try
1706  {
1707  $this->deletePageOfQuestion($question_id);
1708  }
1709  catch (Exception $e)
1710  {
1711  $ilLog->write("EXCEPTION: Could not delete page of question $question_id: $e");
1712  return false;
1713  }
1714 
1715  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_questions WHERE question_id = %s",
1716  array('integer'),
1717  array($question_id)
1718  );
1719  if ($affectedRows == 0) return false;
1720 
1721  try
1722  {
1723  $this->deleteAdditionalTableData($question_id);
1724  $this->deleteAnswers($question_id);
1725  $this->feedbackOBJ->deleteGenericFeedbacks($question_id, $this->isAdditionalContentEditingModePageObject());
1726  $this->feedbackOBJ->deleteSpecificAnswerFeedbacks($question_id, $this->isAdditionalContentEditingModePageObject());
1727  }
1728  catch (Exception $e)
1729  {
1730  $ilLog->write("EXCEPTION: Could not delete additional table data of question $question_id: $e");
1731  return false;
1732  }
1733 
1734  try
1735  {
1736  // delete the question in the tst_test_question table (list of test questions)
1737  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE question_fi = %s",
1738  array('integer'),
1739  array($question_id)
1740  );
1741  }
1742  catch (Exception $e)
1743  {
1744  $ilLog->write("EXCEPTION: Could not delete delete question $question_id from a test: $e");
1745  return false;
1746  }
1747 
1748  try
1749  {
1750  // delete suggested solutions contained in the question
1751  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s",
1752  array('integer'),
1753  array($question_id)
1754  );
1755  }
1756  catch (Exception $e)
1757  {
1758  $ilLog->write("EXCEPTION: Could not delete suggested solutions of question $question_id: $e");
1759  return false;
1760  }
1761 
1762  try
1763  {
1764  $directory = CLIENT_WEB_DIR . "/assessment/" . $obj_id . "/$question_id";
1765  if (preg_match("/\d+/", $obj_id) and preg_match("/\d+/", $question_id) and is_dir($directory))
1766  {
1767  include_once "./Services/Utilities/classes/class.ilUtil.php";
1768  ilUtil::delDir($directory);
1769  }
1770  }
1771  catch (Exception $e)
1772  {
1773  $ilLog->write("EXCEPTION: Could not delete question file directory $directory of question $question_id: $e");
1774  return false;
1775  }
1776 
1777  try
1778  {
1779  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
1780  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $question_id);
1781  // remaining usages are not in text anymore -> delete them
1782  // and media objects (note: delete method of ilObjMediaObject
1783  // checks whether object is used in another context; if yes,
1784  // the object is not deleted!)
1785  foreach($mobs as $mob)
1786  {
1787  ilObjMediaObject::_removeUsage($mob, "qpl:html", $question_id);
1788  if (ilObjMediaObject::_exists($mob))
1789  {
1790  $mob_obj =& new ilObjMediaObject($mob);
1791  $mob_obj->delete();
1792  }
1793  }
1794  }
1795  catch (Exception $e)
1796  {
1797  $ilLog->write("EXCEPTION: Error deleting the media objects of question $question_id: $e");
1798  return false;
1799  }
1800 
1801  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
1803 
1804  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintList.php';
1806 
1807  try
1808  {
1809  // update question count of question pool
1810  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
1812  }
1813  catch (Exception $e)
1814  {
1815  $ilLog->write("EXCEPTION: Error updating the question pool question count of question pool " . $this->getObjId() . " when deleting question $question_id: $e");
1816  return false;
1817  }
1818 
1819  $this->notifyQuestionDeleted($this);
1820 
1821  return true;
1822  }
1823 
1827  function getTotalAnswers()
1828  {
1829  return $this->_getTotalAnswers($this->id);
1830  }
1831 
1838  function _getTotalAnswers($a_q_id)
1839  {
1840  global $ilDB;
1841 
1842  // get all question references to the question id
1843  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id = %s OR question_id = %s",
1844  array('integer','integer'),
1845  array($a_q_id, $a_q_id)
1846  );
1847  if ($result->numRows() == 0)
1848  {
1849  return 0;
1850  }
1851  $found_id = array();
1852  while ($row = $ilDB->fetchAssoc($result))
1853  {
1854  array_push($found_id, $row["question_id"]);
1855  }
1856 
1857  $result = $ilDB->query("SELECT * FROM tst_test_result WHERE " . $ilDB->in('question_fi', $found_id, false, 'integer'));
1858 
1859  return $result->numRows();
1860  }
1861 
1862 
1869  function _getTotalRightAnswers($a_q_id)
1870  {
1871  global $ilDB;
1872  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id = %s OR question_id = %s",
1873  array('integer','integer'),
1874  array($a_q_id, $a_q_id)
1875  );
1876  if ($result->numRows() == 0)
1877  {
1878  return 0;
1879  }
1880  $found_id = array();
1881  while ($row = $ilDB->fetchAssoc($result))
1882  {
1883  array_push($found_id, $row["question_id"]);
1884  }
1885  $result = $ilDB->query("SELECT * FROM tst_test_result WHERE " . $ilDB->in('question_fi', $found_id, false, 'integer'));
1886  $answers = array();
1887  while ($row = $ilDB->fetchAssoc($result))
1888  {
1889  $reached = $row["points"];
1890  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
1891  $max = assQuestion::_getMaximumPoints($row["question_fi"]);
1892  array_push($answers, array("reached" => $reached, "max" => $max));
1893  }
1894  $max = 0.0;
1895  $reached = 0.0;
1896  foreach ($answers as $key => $value)
1897  {
1898  $max += $value["max"];
1899  $reached += $value["reached"];
1900  }
1901  if ($max > 0)
1902  {
1903  return $reached / $max;
1904  }
1905  else
1906  {
1907  return 0;
1908  }
1909  }
1910 
1916  function _getTitle($a_q_id)
1917  {
1918  global $ilDB;
1919  $result = $ilDB->queryF("SELECT title FROM qpl_questions WHERE question_id = %s",
1920  array('integer'),
1921  array($a_q_id)
1922  );
1923  if ($result->numRows() == 1)
1924  {
1925  $row = $ilDB->fetchAssoc($result);
1926  return $row["title"];
1927  }
1928  else
1929  {
1930  return "";
1931  }
1932  }
1933 
1939  function _getQuestionText($a_q_id)
1940  {
1941  global $ilDB;
1942  $result = $ilDB->queryF("SELECT question_text FROM qpl_questions WHERE question_id = %s",
1943  array('integer'),
1944  array($a_q_id)
1945  );
1946  if ($result->numRows() == 1)
1947  {
1948  $row = $ilDB->fetchAssoc($result);
1949  return $row["question_text"];
1950  }
1951  else
1952  {
1953  return "";
1954  }
1955  }
1956 
1957 
1959  {
1960  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
1961  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $a_q_id);
1962  foreach ($mobs as $mob)
1963  {
1964  ilObjMediaObject::_saveUsage($mob, "qpl:html", $this->getId());
1965  }
1966  }
1967 
1969  {
1970  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
1971  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1972  foreach ($mobs as $mob)
1973  {
1974  ilObjMediaObject::_saveUsage($mob, "qpl:html", $this->original_id);
1975  }
1976  }
1977 
1981  function createPageObject()
1982  {
1983  $qpl_id = $this->getObjId();
1984 
1985  include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
1986  $this->page = new ilAssQuestionPage(0);
1987  $this->page->setId($this->getId());
1988  $this->page->setParentId($qpl_id);
1989  $this->page->setXMLContent("<PageObject><PageContent>".
1990  "<Question QRef=\"il__qst_".$this->getId()."\"/>".
1991  "</PageContent></PageObject>");
1992  $this->page->create();
1993  }
1994 
1995  function copyPageOfQuestion($a_q_id)
1996  {
1997  if ($a_q_id > 0)
1998  {
1999  include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
2000  $page = new ilAssQuestionPage($a_q_id);
2001 
2002  $xml = str_replace("il__qst_".$a_q_id, "il__qst_".$this->id, $page->getXMLContent());
2003  $this->page->setXMLContent($xml);
2004  $this->page->updateFromXML();
2005  }
2006  }
2007 
2009  {
2010  include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
2011  $page = new ilAssQuestionPage($this->id);
2012  return $page->getXMLContent();
2013  }
2014 
2022  function _getQuestionType($question_id)
2023  {
2024  global $ilDB;
2025 
2026  if ($question_id < 1) return "";
2027  $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",
2028  array('integer'),
2029  array($question_id)
2030  );
2031  if ($result->numRows() == 1)
2032  {
2033  $data = $ilDB->fetchAssoc($result);
2034  return $data["type_tag"];
2035  }
2036  else
2037  {
2038  return "";
2039  }
2040  }
2041 
2049  function _getQuestionTitle($question_id)
2050  {
2051  global $ilDB;
2052 
2053  if ($question_id < 1) return "";
2054 
2055  $result = $ilDB->queryF("SELECT title FROM qpl_questions WHERE qpl_questions.question_id = %s",
2056  array('integer'),
2057  array($question_id)
2058  );
2059  if ($result->numRows() == 1)
2060  {
2061  $data = $ilDB->fetchAssoc($result);
2062  return $data["title"];
2063  }
2064  else
2065  {
2066  return "";
2067  }
2068  }
2069 
2071  {
2072  $this->original_id = $original_id;
2073  }
2074 
2075  function getOriginalId()
2076  {
2077  return $this->original_id;
2078  }
2079 
2086  function loadFromDb($question_id)
2087  {
2088  global $ilDB;
2089 
2090  $result = $ilDB->queryF(
2091  "SELECT external_id FROM qpl_questions WHERE question_id = %s",
2092  array("integer"),
2093  array($question_id)
2094  );
2095  if($result->numRows() == 1)
2096  {
2097  $data = $ilDB->fetchAssoc($result);
2098  $this->external_id = $data['external_id'];
2099  }
2100 
2101  $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s",
2102  array('integer'),
2103  array($this->getId())
2104  );
2105  $this->suggested_solutions = array();
2106  if ($result->numRows())
2107  {
2108  include_once("./Services/RTE/classes/class.ilRTE.php");
2109  while ($row = $ilDB->fetchAssoc($result))
2110  {
2111  $value = (is_array(unserialize($row["value"]))) ? unserialize($row["value"]) : ilRTE::_replaceMediaObjectImageSrc($row["value"], 1);
2112  $this->suggested_solutions[$row["subquestion_index"]] = array(
2113  "type" => $row["type"],
2114  "value" => $value,
2115  "internal_link" => $row["internal_link"],
2116  "import_id" => $row["import_id"]
2117  );
2118  }
2119  }
2120  }
2121 
2128  public function createNewQuestion($a_create_page = true)
2129  {
2130  global $ilDB, $ilUser;
2131 
2132  $complete = "0";
2133  $estw_time = $this->getEstimatedWorkingTime();
2134  $estw_time = sprintf("%02d:%02d:%02d", $estw_time['h'], $estw_time['m'], $estw_time['s']);
2135  $obj_id = ($this->getObjId() <= 0) ? (ilObject::_lookupObjId((strlen($_GET["ref_id"])) ? $_GET["ref_id"] : $_POST["sel_qpl"])) : $this->getObjId();
2136  if ($obj_id > 0)
2137  {
2138  if($a_create_page)
2139  {
2140  $tstamp = 0;
2141  }
2142  else
2143  {
2144  // question pool must not try to purge
2145  $tstamp = time();
2146  }
2147 
2148  $next_id = $ilDB->nextId('qpl_questions');
2149  $affectedRows = $ilDB->insert("qpl_questions", array(
2150  "question_id" => array("integer", $next_id),
2151  "question_type_fi" => array("integer", $this->getQuestionTypeID()),
2152  "obj_fi" => array("integer", $obj_id),
2153  "title" => array("text", NULL),
2154  "description" => array("text", NULL),
2155  "author" => array("text", $this->getAuthor()),
2156  "owner" => array("integer", $ilUser->getId()),
2157  "question_text" => array("clob", NULL),
2158  "points" => array("float", 0),
2159  "nr_of_tries" => array("integer", 1),
2160  "working_time" => array("text", $estw_time),
2161  "complete" => array("text", $complete),
2162  "created" => array("integer", time()),
2163  "original_id" => array("integer", NULL),
2164  "tstamp" => array("integer", $tstamp),
2165  "external_id" => array("text", $this->getExternalId()),
2166  'add_cont_edit_mode' => array('text', $this->getAdditionalContentEditingMode())
2167  ));
2168  $this->setId($next_id);
2169 
2170  if($a_create_page)
2171  {
2172  // create page object of question
2173  $this->createPageObject();
2174  }
2175  }
2176 
2177  $this->notifyQuestionCreated();
2178 
2179  return $this->getId();
2180  }
2181 
2182  public function saveQuestionDataToDb($original_id = "")
2183  {
2184  global $ilDB;
2185 
2186  $estw_time = $this->getEstimatedWorkingTime();
2187  $estw_time = sprintf("%02d:%02d:%02d", $estw_time['h'], $estw_time['m'], $estw_time['s']);
2188 
2189  // cleanup RTE images which are not inserted into the question text
2190  include_once("./Services/RTE/classes/class.ilRTE.php");
2191  if ($this->getId() == -1)
2192  {
2193  // Neuen Datensatz schreiben
2194  $next_id = $ilDB->nextId('qpl_questions');
2195  $affectedRows = $ilDB->insert("qpl_questions", array(
2196  "question_id" => array("integer", $next_id),
2197  "question_type_fi" => array("integer", $this->getQuestionTypeID()),
2198  "obj_fi" => array("integer", $this->getObjId()),
2199  "title" => array("text", $this->getTitle()),
2200  "description" => array("text", $this->getComment()),
2201  "author" => array("text", $this->getAuthor()),
2202  "owner" => array("integer", $this->getOwner()),
2203  "question_text" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getQuestion(), 0)),
2204  "points" => array("float", $this->getMaximumPoints()),
2205  "working_time" => array("text", $estw_time),
2206  "nr_of_tries" => array("integer", (strlen($this->getNrOfTries())) ? $this->getNrOfTries() : 1),
2207  "created" => array("integer", time()),
2208  "original_id" => array("integer", ($original_id) ? $original_id : NULL),
2209  "tstamp" => array("integer", time()),
2210  "external_id" => array("text", $this->getExternalId()),
2211  'add_cont_edit_mode' => array('text', $this->getAdditionalContentEditingMode())
2212  ));
2213  $this->setId($next_id);
2214  // create page object of question
2215  $this->createPageObject();
2216  }
2217  else
2218  {
2219  // Vorhandenen Datensatz aktualisieren
2220  $affectedRows = $ilDB->update("qpl_questions", array(
2221  "obj_fi" => array("integer", $this->getObjId()),
2222  "title" => array("text", $this->getTitle()),
2223  "description" => array("text", $this->getComment()),
2224  "author" => array("text", $this->getAuthor()),
2225  "question_text" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getQuestion(), 0)),
2226  "points" => array("float", $this->getMaximumPoints()),
2227  "nr_of_tries" => array("integer", (strlen($this->getNrOfTries())) ? $this->getNrOfTries() : 1),
2228  "working_time" => array("text", $estw_time),
2229  "tstamp" => array("integer", time()),
2230  "external_id" => array("text", $this->getExternalId())
2231  ), array(
2232  "question_id" => array("integer", $this->getId())
2233  ));
2234  }
2235  }
2236 
2243  function saveToDb($original_id = "")
2244  {
2245  global $ilDB;
2246 
2247  $this->updateSuggestedSolutions();
2248 
2249  // remove unused media objects from ILIAS
2250  $this->cleanupMediaObjectUsage();
2251 
2252  $complete = "0";
2253  if ($this->isComplete())
2254  {
2255  $complete = "1";
2256  }
2257 
2258  // update the question time stamp and completion status
2259  $affectedRows = $ilDB->manipulateF("UPDATE qpl_questions SET tstamp = %s, owner = %s, complete = %s WHERE question_id = %s",
2260  array('integer','integer', 'integer','text'),
2261  array(time(), ($this->getOwner() <= 0) ? $this->ilias->account->id : $this->getOwner(), $complete, $this->getId())
2262  );
2263 
2264  // update question count of question pool
2265  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
2267 
2268  $this->notifyQuestionEdited($this);
2269  }
2270 
2271  public function setNewOriginalId($newId) {
2272  global $ilDB;
2273  $ilDB->manipulateF("UPDATE qpl_questions SET tstamp = %s, original_id = %s WHERE question_id = %s",
2274  array('integer','integer', 'text'),
2275  array(time(), $newId, $this->getId())
2276  );
2277  }
2278 
2282  protected function onDuplicate($originalParentId, $originalQuestionId, $duplicateParentId, $duplicateQuestionId)
2283  {
2284  $this->duplicateSuggestedSolutionFiles($originalParentId, $originalQuestionId);
2285 
2286  // duplicate question feeback
2287  $this->feedbackOBJ->duplicateFeedback($originalQuestionId, $duplicateQuestionId);
2288 
2289  // duplicate question hints
2290  $this->duplicateQuestionHints($originalQuestionId, $duplicateQuestionId);
2291  }
2292 
2293  protected function onSyncWithOriginal($originalQuestionId, $duplicateQuestionId)
2294  {
2295  // sync question feeback
2296  $this->feedbackOBJ->syncFeedback($originalQuestionId, $duplicateQuestionId);
2297  }
2298 
2302  protected function onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $targetQuestionId)
2303  {
2304  $this->copySuggestedSolutionFiles($sourceParentId, $sourceQuestionId);
2305 
2306  // duplicate question feeback
2307  $this->feedbackOBJ->duplicateFeedback($sourceQuestionId, $targetQuestionId);
2308 
2309  // duplicate question hints
2310  $this->duplicateQuestionHints($sourceQuestionId, $targetQuestionId);
2311  }
2312 
2316  public function deleteSuggestedSolutions()
2317  {
2318  global $ilDB;
2319  // delete the links in the qpl_sol_sug table
2320  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s",
2321  array('integer'),
2322  array($this->getId())
2323  );
2324  // delete the links in the int_link table
2325  include_once "./Services/COPage/classes/class.ilInternalLink.php";
2327  $this->suggested_solutions = array();
2329  }
2330 
2338  function getSuggestedSolution($subquestion_index = 0)
2339  {
2340  if (array_key_exists($subquestion_index, $this->suggested_solutions))
2341  {
2342  return $this->suggested_solutions[$subquestion_index];
2343  }
2344  else
2345  {
2346  return array();
2347  }
2348  }
2349 
2358  function getSuggestedSolutionTitle($subquestion_index = 0)
2359  {
2360  if (array_key_exists($subquestion_index, $this->suggested_solutions))
2361  {
2362  $title = $this->suggested_solutions[$subquestion_index]["internal_link"];
2363  // TO DO: resolve internal link an get link type and title
2364  }
2365  else
2366  {
2367  $title = "";
2368  }
2369  return $title;
2370  }
2371 
2381  function setSuggestedSolution($solution_id = "", $subquestion_index = 0, $is_import = false)
2382  {
2383  if (strcmp($solution_id, "") != 0)
2384  {
2385  $import_id = "";
2386  if ($is_import)
2387  {
2388  $import_id = $solution_id;
2389  $solution_id = $this->_resolveInternalLink($import_id);
2390  }
2391  $this->suggested_solutions[$subquestion_index] = array(
2392  "internal_link" => $solution_id,
2393  "import_id" => $import_id
2394  );
2395  }
2396  }
2397 
2401  protected function duplicateSuggestedSolutionFiles($parent_id, $question_id)
2402  {
2403  global $ilLog;
2404 
2405  foreach ($this->suggested_solutions as $index => $solution)
2406  {
2407  if (strcmp($solution["type"], "file") == 0)
2408  {
2409  $filepath = $this->getSuggestedSolutionPath();
2410  $filepath_original = str_replace(
2411  "/{$this->obj_id}/{$this->id}/solution",
2412  "/$parent_id/$question_id/solution",
2413  $filepath
2414  );
2415  if (!file_exists($filepath))
2416  {
2417  ilUtil::makeDirParents($filepath);
2418  }
2419  $filename = $solution["value"]["name"];
2420  if (strlen($filename))
2421  {
2422  if (!copy($filepath_original . $filename, $filepath . $filename))
2423  {
2424  $ilLog->write("File could not be duplicated!!!!", $ilLog->ERROR);
2425  $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
2426  }
2427  }
2428  }
2429  }
2430  }
2431 
2436  {
2437  global $ilLog;
2438 
2439  $filepath = $this->getSuggestedSolutionPath();
2440  $filepath_original = str_replace("/$this->id/solution", "/$original_id/solution", $filepath);
2441  ilUtil::delDir($filepath_original);
2442  foreach ($this->suggested_solutions as $index => $solution)
2443  {
2444  if (strcmp($solution["type"], "file") == 0)
2445  {
2446  if (!file_exists($filepath_original))
2447  {
2448  ilUtil::makeDirParents($filepath_original);
2449  }
2450  $filename = $solution["value"]["name"];
2451  if (strlen($filename))
2452  {
2453  if (!@copy($filepath . $filename, $filepath_original . $filename))
2454  {
2455  $ilLog->write("File could not be duplicated!!!!", $ilLog->ERROR);
2456  $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
2457  }
2458  }
2459  }
2460  }
2461  }
2462 
2463  protected function copySuggestedSolutionFiles($source_questionpool_id, $source_question_id)
2464  {
2465  global $ilLog;
2466 
2467  foreach ($this->suggested_solutions as $index => $solution)
2468  {
2469  if (strcmp($solution["type"], "file") == 0)
2470  {
2471  $filepath = $this->getSuggestedSolutionPath();
2472  $filepath_original = str_replace("/$this->obj_id/$this->id/solution", "/$source_questionpool_id/$source_question_id/solution", $filepath);
2473  if (!file_exists($filepath))
2474  {
2475  ilUtil::makeDirParents($filepath);
2476  }
2477  $filename = $solution["value"]["name"];
2478  if (strlen($filename))
2479  {
2480  if (!copy($filepath_original . $filename, $filepath . $filename))
2481  {
2482  $ilLog->write("File could not be copied!!!!", $ilLog->ERROR);
2483  $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
2484  }
2485  }
2486  }
2487  }
2488  }
2489 
2493  public function updateSuggestedSolutions($original_id = "")
2494  {
2495  global $ilDB;
2496 
2497  $id = (strlen($original_id) && is_numeric($original_id)) ? $original_id : $this->getId();
2498  include_once "./Services/COPage/classes/class.ilInternalLink.php";
2499  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s",
2500  array('integer'),
2501  array($id)
2502  );
2504  include_once("./Services/RTE/classes/class.ilRTE.php");
2505  foreach ($this->suggested_solutions as $index => $solution)
2506  {
2507  $next_id = $ilDB->nextId('qpl_sol_sug');
2509  $ilDB->insert('qpl_sol_sug', array(
2510  'suggested_solution_id' => array( 'integer', $next_id ),
2511  'question_fi' => array( 'integer', $id ),
2512  'type' => array( 'text', $solution['type'] ),
2513  'value' => array( 'clob', ilRTE::_replaceMediaObjectImageSrc( (is_array( $solution['value'] ) ) ? serialize( $solution[ 'value' ] ) : $solution['value'], 0 ) ),
2514  'internal_link' => array( 'text', $solution['internal_link'] ),
2515  'import_id' => array( 'text', null ),
2516  'subquestion_index' => array( 'integer', $index ),
2517  'tstamp' => array( 'integer', time() ),
2518  )
2519  );
2520  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
2521  {
2522  ilInternalLink::_saveLink("qst", $id, $matches[2], $matches[3], $matches[1]);
2523  }
2524  }
2525  if (strlen($original_id) && is_numeric($original_id)) $this->syncSuggestedSolutionFiles($id);
2526  $this->cleanupMediaObjectUsage();
2527  }
2528 
2538  function saveSuggestedSolution($type, $solution_id = "", $subquestion_index = 0, $value = "")
2539  {
2540  global $ilDB;
2541 
2542  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s AND subquestion_index = %s",
2543  array("integer", "integer"),
2544  array(
2545  $this->getId(),
2546  $subquestion_index
2547  )
2548  );
2549 
2550  $next_id = $ilDB->nextId('qpl_sol_sug');
2551  include_once("./Services/RTE/classes/class.ilRTE.php");
2553  $affectedRows = $ilDB->insert('qpl_sol_sug', array(
2554  'suggested_solution_id' => array( 'integer', $next_id ),
2555  'question_fi' => array( 'integer', $this->getId() ),
2556  'type' => array( 'text', $type ),
2557  'value' => array( 'clob', ilRTE::_replaceMediaObjectImageSrc( (is_array( $value ) ) ? serialize( $value ) : $value, 0 ) ),
2558  'internal_link' => array( 'text', $solution_id ),
2559  'import_id' => array( 'text', null ),
2560  'subquestion_index' => array( 'integer', $subquestion_index ),
2561  'tstamp' => array( 'integer', time() ),
2562  )
2563  );
2564  if ($affectedRows == 1)
2565  {
2566  $this->suggested_solutions["subquestion_index"] = array(
2567  "type" => $type,
2568  "value" => $value,
2569  "internal_link" => $solution_id,
2570  "import_id" => ""
2571  );
2572  }
2573  $this->cleanupMediaObjectUsage();
2574  }
2575 
2576  function _resolveInternalLink($internal_link)
2577  {
2578  if (preg_match("/il_(\d+)_(\w+)_(\d+)/", $internal_link, $matches))
2579  {
2580  include_once "./Services/COPage/classes/class.ilInternalLink.php";
2581  include_once "./Modules/LearningModule/classes/class.ilLMObject.php";
2582  include_once "./Modules/Glossary/classes/class.ilGlossaryTerm.php";
2583  switch ($matches[2])
2584  {
2585  case "lm":
2586  $resolved_link = ilLMObject::_getIdForImportId($internal_link);
2587  break;
2588  case "pg":
2589  $resolved_link = ilInternalLink::_getIdForImportId("PageObject", $internal_link);
2590  break;
2591  case "st":
2592  $resolved_link = ilInternalLink::_getIdForImportId("StructureObject", $internal_link);
2593  break;
2594  case "git":
2595  $resolved_link = ilInternalLink::_getIdForImportId("GlossaryItem", $internal_link);
2596  break;
2597  case "mob":
2598  $resolved_link = ilInternalLink::_getIdForImportId("MediaObject", $internal_link);
2599  break;
2600  }
2601  if (strcmp($resolved_link, "") == 0)
2602  {
2603  $resolved_link = $internal_link;
2604  }
2605  }
2606  else
2607  {
2608  $resolved_link = $internal_link;
2609  }
2610  return $resolved_link;
2611  }
2612 
2613  function _resolveIntLinks($question_id)
2614  {
2615  global $ilDB;
2616  $resolvedlinks = 0;
2617  $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s",
2618  array('integer'),
2619  array($question_id)
2620  );
2621  if ($result->numRows())
2622  {
2623  while ($row = $ilDB->fetchAssoc($result))
2624  {
2625  $internal_link = $row["internal_link"];
2626  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
2627  $resolved_link = assQuestion::_resolveInternalLink($internal_link);
2628  if (strcmp($internal_link, $resolved_link) != 0)
2629  {
2630  // internal link was resolved successfully
2631  $affectedRows = $ilDB->manipulateF("UPDATE qpl_sol_sug SET internal_link = %s WHERE suggested_solution_id = %s",
2632  array('text','integer'),
2633  array($resolved_link, $row["suggested_solution_id"])
2634  );
2635  $resolvedlinks++;
2636  }
2637  }
2638  }
2639  if ($resolvedlinks)
2640  {
2641  // there are resolved links -> reenter theses links to the database
2642 
2643  // delete all internal links from the database
2644  include_once "./Services/COPage/classes/class.ilInternalLink.php";
2645  ilInternalLink::_deleteAllLinksOfSource("qst", $question_id);
2646 
2647  $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s",
2648  array('integer'),
2649  array($question_id)
2650  );
2651  if ($result->numRows())
2652  {
2653  while ($row = $ilDB->fetchAssoc($result))
2654  {
2655  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $row["internal_link"], $matches))
2656  {
2657  ilInternalLink::_saveLink("qst", $question_id, $matches[2], $matches[3], $matches[1]);
2658  }
2659  }
2660  }
2661  }
2662  }
2663 
2664  function _getInternalLinkHref($target = "")
2665  {
2666  global $ilDB;
2667  $linktypes = array(
2668  "lm" => "LearningModule",
2669  "pg" => "PageObject",
2670  "st" => "StructureObject",
2671  "git" => "GlossaryItem",
2672  "mob" => "MediaObject"
2673  );
2674  $href = "";
2675  if (preg_match("/il__(\w+)_(\d+)/", $target, $matches))
2676  {
2677  $type = $matches[1];
2678  $target_id = $matches[2];
2679  include_once "./Services/Utilities/classes/class.ilUtil.php";
2680  switch($linktypes[$matches[1]])
2681  {
2682  case "LearningModule":
2683  $href = "./goto.php?target=" . $type . "_" . $target_id;
2684  break;
2685  case "PageObject":
2686  case "StructureObject":
2687  $href = "./goto.php?target=" . $type . "_" . $target_id;
2688  break;
2689  case "GlossaryItem":
2690  $href = "./goto.php?target=" . $type . "_" . $target_id;
2691  break;
2692  case "MediaObject":
2693  $href = "./ilias.php?baseClass=ilLMPresentationGUI&obj_type=" . $linktypes[$type] . "&cmd=media&ref_id=".$_GET["ref_id"]."&mob_id=".$target_id;
2694  break;
2695  }
2696  }
2697  return $href;
2698  }
2699 
2707  public static function _getOriginalId($question_id)
2708  {
2709  global $ilDB;
2710  $result = $ilDB->queryF("SELECT * FROM qpl_questions WHERE question_id = %s",
2711  array('integer'),
2712  array($question_id)
2713  );
2714  if ($result->numRows() > 0)
2715  {
2716  $row = $ilDB->fetchAssoc($result);
2717  if ($row["original_id"] > 0)
2718  {
2719  return $row["original_id"];
2720  }
2721  else
2722  {
2723  return $row["question_id"];
2724  }
2725  }
2726  else
2727  {
2728  return "";
2729  }
2730  }
2731 
2732  public static function originalQuestionExists($questionId)
2733  {
2734  global $ilDB;
2735 
2736  $query = "
2737  SELECT COUNT(dupl.question_id) cnt
2738  FROM qpl_questions dupl
2739  INNER JOIN qpl_questions orig
2740  ON orig.question_id = dupl.original_id
2741  WHERE dupl.question_id = %s
2742  ";
2743 
2744  $res = $ilDB->queryF($query, array('integer'), array($questionId));
2745  $row = $ilDB->fetchAssoc($res);
2746 
2747  return $row['cnt'] > 0;
2748  }
2749 
2750  function syncWithOriginal()
2751  {
2752  global $ilDB;
2753 
2754  if( !$this->getOriginalId() )
2755  {
2756  return;
2757  }
2758 
2759  $originalObjId = self::lookupOriginalParentObjId($this->getOriginalId());
2760 
2761  if ( !$originalObjId )
2762  {
2763  return;
2764  }
2765 
2766  $id = $this->getId();
2767  $original = $this->getOriginalId();
2768 
2769  $this->setId($this->getOriginalId());
2770  $this->setOriginalId(NULL);
2771  $this->setObjId($originalObjId);
2772  $this->saveToDb();
2773  $this->deletePageOfQuestion($original);
2774  $this->createPageObject();
2775  $this->copyPageOfQuestion($id);
2776 
2777  $this->setId($id);
2778  $this->setOriginalId($original);
2779  $this->updateSuggestedSolutions($original);
2781 
2782  $this->onSyncWithOriginal($original, $this->getId());
2783  $this->syncHints();
2784  }
2785 
2786  function createRandomSolution($test_id, $user_id)
2787  {
2788  }
2789 
2797  function _questionExists($question_id)
2798  {
2799  global $ilDB;
2800 
2801  if ($question_id < 1)
2802  {
2803  return false;
2804  }
2805 
2806  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE question_id = %s",
2807  array('integer'),
2808  array($question_id)
2809  );
2810  if ($result->numRows() == 1)
2811  {
2812  return true;
2813  }
2814  else
2815  {
2816  return false;
2817  }
2818  }
2819 
2827  function _questionExistsInPool($question_id)
2828  {
2829  global $ilDB;
2830 
2831  if ($question_id < 1)
2832  {
2833  return false;
2834  }
2835 
2836  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions INNER JOIN object_data ON obj_fi = obj_id WHERE question_id = %s AND type = 'qpl'",
2837  array('integer'),
2838  array($question_id)
2839  );
2840  if ($result->numRows() == 1)
2841  {
2842  return true;
2843  }
2844  else
2845  {
2846  return false;
2847  }
2848  }
2849 
2857  public static function _instanciateQuestion($question_id)
2858  {
2859  return self::_instantiateQuestion($question_id);
2860  }
2861 
2862  public static function _instantiateQuestion($question_id)
2863  {
2864  global $ilCtrl, $ilDB, $lng;
2865 
2866  if (strcmp($question_id, "") != 0)
2867  {
2868  $question_type = assQuestion::_getQuestionType($question_id);
2869  if (!strlen($question_type)) return null;
2870  assQuestion::_includeClass($question_type);
2871  $objectClassname = self::getObjectClassNameByQuestionType($question_type);
2872  $question = new $objectClassname();
2873  $question->loadFromDb($question_id);
2874 
2875  $feedbackObjectClassname = self::getFeedbackClassNameByQuestionType($question_type);
2876  $question->feedbackOBJ = new $feedbackObjectClassname($question, $ilCtrl, $ilDB, $lng);
2877 
2878  return $question;
2879  }
2880  }
2881 
2888  function getPoints()
2889  {
2890  if (strcmp($this->points, "") == 0)
2891  {
2892  return 0;
2893  }
2894  else
2895  {
2896  return $this->points;
2897  }
2898  }
2899 
2900 
2907  function setPoints($a_points)
2908  {
2909  $this->points = $a_points;
2910  }
2911 
2918  function getSolutionMaxPass($active_id)
2919  {
2920  return $this->_getSolutionMaxPass($this->getId(), $active_id);
2921  }
2922 
2929  function _getSolutionMaxPass($question_id, $active_id)
2930  {
2931 /* include_once "./Modules/Test/classes/class.ilObjTest.php";
2932  $pass = ilObjTest::_getPass($active_id);
2933  return $pass;*/
2934 
2935  // the following code was the old solution which added the non answered
2936  // questions of a pass from the answered questions of the previous pass
2937  // with the above solution, only the answered questions of the last pass are counted
2938  global $ilDB;
2939 
2940  $result = $ilDB->queryF("SELECT MAX(pass) maxpass FROM tst_test_result WHERE active_fi = %s AND question_fi = %s",
2941  array('integer','integer'),
2942  array($active_id, $question_id)
2943  );
2944  if ($result->numRows() == 1)
2945  {
2946  $row = $ilDB->fetchAssoc($result);
2947  return $row["maxpass"];
2948  }
2949  else
2950  {
2951  return 0;
2952  }
2953  }
2954 
2963  function _isWriteable($question_id, $user_id)
2964  {
2965  global $ilDB;
2966 
2967  if (($question_id < 1) || ($user_id < 1))
2968  {
2969  return false;
2970  }
2971 
2972  $result = $ilDB->queryF("SELECT obj_fi FROM qpl_questions WHERE question_id = %s",
2973  array('integer'),
2974  array($question_id)
2975  );
2976  if ($result->numRows() == 1)
2977  {
2978  $row = $ilDB->fetchAssoc($result);
2979  $qpl_object_id = $row["obj_fi"];
2980  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
2981  return ilObjQuestionPool::_isWriteable($qpl_object_id, $user_id);
2982  }
2983  else
2984  {
2985  return false;
2986  }
2987  }
2988 
2995  function _isUsedInRandomTest($question_id = "")
2996  {
2997  global $ilDB;
2998 
2999  if ($question_id < 1) return 0;
3000  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE question_fi = %s",
3001  array('integer'),
3002  array($question_id)
3003  );
3004  return $result->numRows();
3005  }
3006 
3018  abstract public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE);
3019 
3030  final public function adjustReachedPointsByScoringOptions($points, $active_id, $pass = NULL)
3031  {
3032  include_once "./Modules/Test/classes/class.ilObjTest.php";
3033  $count_system = ilObjTest::_getCountSystem($active_id);
3034  if ($count_system == 1)
3035  {
3036  if (abs($this->getMaximumPoints() - $points) > 0.0000000001)
3037  {
3038  $points = 0;
3039  }
3040  }
3041  $score_cutting = ilObjTest::_getScoreCutting($active_id);
3042  if ($score_cutting == 0)
3043  {
3044  if ($points < 0)
3045  {
3046  $points = 0;
3047  }
3048  }
3049  return $points;
3050  }
3051 
3060  public static function _isWorkedThrough($active_id, $question_id, $pass = NULL)
3061  {
3062  global $ilDB;
3063 
3064  $points = 0;
3065  if (is_null($pass))
3066  {
3067  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
3068  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
3069  }
3070  $result = $ilDB->queryF("SELECT solution_id FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
3071  array('integer','integer','integer'),
3072  array($active_id, $question_id, $pass)
3073  );
3074  if ($result->numRows())
3075  {
3076  return TRUE;
3077  }
3078  else
3079  {
3080  return FALSE;
3081  }
3082  }
3083 
3091  public static function _areAnswered($a_user_id,$a_question_ids)
3092  {
3093  global $ilDB;
3094 
3095  $res = $ilDB->queryF("SELECT DISTINCT(question_fi) FROM tst_test_result JOIN tst_active ".
3096  "ON (active_id = active_fi) ".
3097  "WHERE " . $ilDB->in('question_fi', $a_question_ids, false, 'integer') .
3098  " AND user_fi = %s",
3099  array('integer'),
3100  array($a_user_id)
3101  );
3102  return ($res->numRows() == count($a_question_ids)) ? true : false;
3103  }
3104 
3113  function isHTML($a_text)
3114  {
3115  if (preg_match("/<[^>]*?>/", $a_text))
3116  {
3117  return TRUE;
3118  }
3119  else
3120  {
3121  return FALSE;
3122  }
3123  }
3124 
3131  function prepareTextareaOutput($txt_output, $prepare_for_latex_output = FALSE)
3132  {
3133  include_once "./Services/Utilities/classes/class.ilUtil.php";
3134  return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output);
3135  }
3136 
3144  function QTIMaterialToString($a_material)
3145  {
3146  $result = "";
3147  for ($i = 0; $i < $a_material->getMaterialCount(); $i++)
3148  {
3149  $material = $a_material->getMaterial($i);
3150  if (strcmp($material["type"], "mattext") == 0)
3151  {
3152  $result .= $material["material"]->getContent();
3153  }
3154  if (strcmp($material["type"], "matimage") == 0)
3155  {
3156  $matimage = $material["material"];
3157  if (preg_match("/(il_([0-9]+)_mob_([0-9]+))/", $matimage->getLabel(), $matches))
3158  {
3159  // import an mediaobject which was inserted using tiny mce
3160  if (!is_array($_SESSION["import_mob_xhtml"])) $_SESSION["import_mob_xhtml"] = array();
3161  array_push($_SESSION["import_mob_xhtml"], array("mob" => $matimage->getLabel(), "uri" => $matimage->getUri()));
3162  }
3163  }
3164  }
3165  return $result;
3166  }
3167 
3176  function addQTIMaterial(&$a_xml_writer, $a_material, $close_material_tag = TRUE, $add_mobs = TRUE)
3177  {
3178  include_once "./Services/RTE/classes/class.ilRTE.php";
3179  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
3180 
3181  $a_xml_writer->xmlStartTag("material");
3182  $attrs = array(
3183  "texttype" => "text/plain"
3184  );
3185  if ($this->isHTML($a_material))
3186  {
3187  $attrs["texttype"] = "text/xhtml";
3188  }
3189  $a_xml_writer->xmlElement("mattext", $attrs, ilRTE::_replaceMediaObjectImageSrc($a_material, 0));
3190  if ($add_mobs)
3191  {
3192  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
3193  foreach ($mobs as $mob)
3194  {
3195  $moblabel = "il_" . IL_INST_ID . "_mob_" . $mob;
3196  if (strpos($a_material, "mm_$mob") !== FALSE)
3197  {
3198  if (ilObjMediaObject::_exists($mob))
3199  {
3200  $mob_obj =& new ilObjMediaObject($mob);
3201  $imgattrs = array(
3202  "label" => $moblabel,
3203  "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle()
3204  );
3205  }
3206  $a_xml_writer->xmlElement("matimage", $imgattrs, NULL);
3207  }
3208  }
3209  }
3210  if ($close_material_tag) $a_xml_writer->xmlEndTag("material");
3211  }
3212 
3213  function createNewImageFileName($image_filename)
3214  {
3215  $extension = "";
3216  if (preg_match("/.*\.(png|jpg|gif|jpeg)$/i", $image_filename, $matches))
3217  {
3218  $extension = "." . $matches[1];
3219  }
3220  $image_filename = md5($image_filename) . $extension;
3221  return $image_filename;
3222  }
3223 
3234  function _setReachedPoints($active_id, $question_id, $points, $maxpoints, $pass, $manualscoring, $obligationsEnabled)
3235  {
3236  global $ilDB;
3237 
3238  if ($points <= $maxpoints)
3239  {
3240  if (is_null($pass))
3241  {
3242  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
3243  }
3244 
3245  // retrieve the already given points
3246  $old_points = 0;
3247  $result = $ilDB->queryF("SELECT points FROM tst_test_result WHERE active_fi = %s AND question_fi = %s AND pass = %s",
3248  array('integer','integer','integer'),
3249  array($active_id, $question_id, $pass)
3250  );
3251  $manual = ($manualscoring) ? 1 : 0;
3252  $rowsnum = $result->numRows();
3253  if($rowsnum)
3254  {
3255  $row = $ilDB->fetchAssoc($result);
3256  $old_points = $row["points"];
3257  if($old_points != $points)
3258  {
3259  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_result SET points = %s, manual = %s, tstamp = %s WHERE active_fi = %s AND question_fi = %s AND pass = %s",
3260  array('float', 'integer', 'integer', 'integer', 'integer', 'integer'),
3261  array($points, $manual, time(), $active_id, $question_id, $pass)
3262  );
3263  }
3264  }
3265  else
3266  {
3267  $next_id = $ilDB->nextId('tst_test_result');
3268  $affectedRows = $ilDB->manipulateF("INSERT INTO tst_test_result (test_result_id, active_fi, question_fi, points, pass, manual, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s)",
3269  array('integer', 'integer','integer', 'float', 'integer', 'integer','integer'),
3270  array($next_id, $active_id, $question_id, $points, $pass, $manual, time())
3271  );
3272  }
3273 
3274  if($old_points != $points || !$rowsnum)
3275  {
3276  assQuestion::_updateTestPassResults($active_id, $pass, $obligationsEnabled);
3277  // finally update objective result
3278  include_once "./Modules/Test/classes/class.ilObjTest.php";
3279  include_once './Modules/Course/classes/class.ilCourseObjectiveResult.php';
3281 
3282  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3284  {
3285  global $lng, $ilUser;
3286  include_once "./Modules/Test/classes/class.ilObjTestAccess.php";
3287  $username = ilObjTestAccess::_getParticipantData($active_id);
3288  assQuestion::_logAction(sprintf($lng->txtlng("assessment", "log_answer_changed_points", ilObjAssessmentFolder::_getLogLanguage()), $username, $old_points, $points, $ilUser->getFullname() . " (" . $ilUser->getLogin() . ")"), $active_id, $question_id);
3289  }
3290  }
3291 
3292  return TRUE;
3293  }
3294  else
3295  {
3296  return FALSE;
3297  }
3298  }
3299 
3307  function getQuestion()
3308  {
3309  return $this->question;
3310  }
3311 
3319  function setQuestion($question = "")
3320  {
3321  $this->question = $question;
3322  }
3323 
3330  function getQuestionType()
3331  {
3332  // must be overwritten in every parent class
3333  return "";
3334  }
3335 
3345  {
3346  global $ilDB;
3347 
3348  $result = $ilDB->queryF("SELECT question_type_id FROM qpl_qst_type WHERE type_tag = %s",
3349  array('text'),
3350  array($this->getQuestionType())
3351  );
3352  if ($result->numRows() == 1)
3353  {
3354  $row = $ilDB->fetchAssoc($result);
3355  return $row["question_type_id"];
3356  }
3357  return 0;
3358  }
3359 
3360  public function syncHints()
3361  {
3362  global $ilDB;
3363 
3364  // delete hints of the original
3365  $ilDB->manipulateF("DELETE FROM qpl_hints WHERE qht_question_fi = %s",
3366  array('integer'),
3367  array($this->original_id)
3368  );
3369 
3370  // get hints of the actual question
3371  $result = $ilDB->queryF("SELECT * FROM qpl_hints WHERE qht_question_fi = %s",
3372  array('integer'),
3373  array($this->getId())
3374  );
3375 
3376  // save hints to the original
3377  if ($result->numRows())
3378  {
3379  while ($row = $ilDB->fetchAssoc($result))
3380  {
3381  $next_id = $ilDB->nextId('qpl_hints');
3383  $ilDB->insert('qpl_hints', array(
3384  'qht_hint_id' => array('integer', $next_id),
3385  'qht_question_fi' => array('integer', $this->original_id),
3386  'qht_hint_index' => array('integer', $row["qht_hint_index"]),
3387  'qht_hint_points' => array('integer', $row["qht_hint_points"]),
3388  'qht_hint_text' => array('text', $row["qht_hint_text"]),
3389  )
3390  );
3391  }
3392  }
3393  }
3394 
3400  {
3401  // must be called in parent classes. add additional RTE text in the parent
3402  // classes and call this method to add the standard RTE text
3403  $collected = $this->getQuestion();
3404  $collected .= $this->feedbackOBJ->getGenericFeedbackContent($this->getId(), false);
3405  $collected .= $this->feedbackOBJ->getGenericFeedbackContent($this->getId(), true);
3406  for( $i = 0; $i <= $this->getTotalAnswers(); $i++ )
3407  {
3408  $collected .= $this->feedbackOBJ->getSpecificAnswerFeedbackContent($this->getId(), $i);
3409  }
3410  foreach ($this->suggested_solutions as $solution_array)
3411  {
3412  $collected .= $solution_array["value"];
3413  }
3414  return $collected;
3415  }
3416 
3422  {
3423  $combinedtext = $this->getRTETextWithMediaObjects();
3424  include_once("./Services/RTE/classes/class.ilRTE.php");
3425  ilRTE::_cleanupMediaObjectUsage($combinedtext, "qpl:html", $this->getId());
3426  }
3427 
3433  function &getInstances()
3434  {
3435  global $ilDB;
3436 
3437  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id = %s",
3438  array("integer"),
3439  array($this->getId())
3440  );
3441  $instances = array();
3442  $ids = array();
3443  while ($row = $ilDB->fetchAssoc($result))
3444  {
3445  array_push($ids, $row["question_id"]);
3446  }
3447  foreach ($ids as $question_id)
3448  {
3449  // check non random tests
3450  $result = $ilDB->queryF("SELECT tst_tests.obj_fi FROM tst_tests, tst_test_question WHERE tst_test_question.question_fi = %s AND tst_test_question.test_fi = tst_tests.test_id",
3451  array("integer"),
3452  array($question_id)
3453  );
3454  while ($row = $ilDB->fetchAssoc($result))
3455  {
3456  $instances[$row['obj_fi']] = ilObject::_lookupTitle($row['obj_fi']);
3457  }
3458  // check random tests
3459  $result = $ilDB->queryF("SELECT tst_tests.obj_fi FROM tst_tests, tst_test_rnd_qst, tst_active WHERE tst_test_rnd_qst.active_fi = tst_active.active_id AND tst_test_rnd_qst.question_fi = %s AND tst_tests.test_id = tst_active.test_fi",
3460  array("integer"),
3461  array($question_id)
3462  );
3463  while ($row = $ilDB->fetchAssoc($result))
3464  {
3465  $instances[$row['obj_fi']] = ilObject::_lookupTitle($row['obj_fi']);
3466  }
3467  }
3468  include_once "./Modules/Test/classes/class.ilObjTest.php";
3469  foreach ($instances as $key => $value)
3470  {
3471  $instances[$key] = array("obj_id" => $key, "title" => $value, "author" => ilObjTest::_lookupAuthor($key), "refs" => ilObject::_getAllReferences($key));
3472  }
3473  return $instances;
3474  }
3475 
3476  function _needsManualScoring($question_id)
3477  {
3478  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
3480  $questiontype = assQuestion::_getQuestionType($question_id);
3481  if (in_array($questiontype, $scoring))
3482  {
3483  return TRUE;
3484  }
3485  else
3486  {
3487  return FALSE;
3488  }
3489  }
3490 
3498  function getActiveUserData($active_id)
3499  {
3500  global $ilDB;
3501  $result = $ilDB->queryF("SELECT * FROM tst_active WHERE active_id = %s",
3502  array('integer'),
3503  array($active_id)
3504  );
3505  if ($result->numRows())
3506  {
3507  $row = $ilDB->fetchAssoc($result);
3508  return array("user_id" => $row["user_fi"], "test_id" => $row["test_fi"]);
3509  }
3510  else
3511  {
3512  return array();
3513  }
3514  }
3515 
3523  static function _includeClass($question_type, $gui = 0)
3524  {
3525  if( self::isCoreQuestionType($question_type) )
3526  {
3527  self::includeCoreClass($question_type, $gui);
3528  }
3529  else
3530  {
3531  self::includePluginClass($question_type, $gui);
3532  }
3533  }
3534 
3535  public static function getGuiClassNameByQuestionType($questionType)
3536  {
3537  return $questionType.'GUI';
3538  }
3539 
3540  public static function getObjectClassNameByQuestionType($questionType)
3541  {
3542  return $questionType;
3543  }
3544 
3545  public static function getFeedbackClassNameByQuestionType($questionType)
3546  {
3547  return str_replace('ass', 'ilAss', $questionType).'Feedback';
3548  }
3549 
3550  public static function isCoreQuestionType($questionType)
3551  {
3552  $guiClassName = self::getGuiClassNameByQuestionType($questionType);
3553  return file_exists("Modules/TestQuestionPool/classes/class.{$guiClassName}.php");
3554  }
3555 
3556  public static function includeCoreClass($questionType, $withGuiClass)
3557  {
3558  if( $withGuiClass )
3559  {
3560  $guiClassName = self::getGuiClassNameByQuestionType($questionType);
3561  require_once "Modules/TestQuestionPool/classes/class.{$guiClassName}.php";
3562 
3563  // object class is included by gui classes constructor
3564  }
3565  else
3566  {
3567  $objectClassName = self::getObjectClassNameByQuestionType($questionType);
3568  require_once "Modules/TestQuestionPool/classes/class.{$objectClassName}.php";
3569  }
3570 
3571  $feedbackClassName = self::getFeedbackClassNameByQuestionType($questionType);
3572  require_once "Modules/TestQuestionPool/classes/feedback/class.{$feedbackClassName}.php";
3573  }
3574 
3575  public static function includePluginClass($questionType, $withGuiClass)
3576  {
3577  global $ilPluginAdmin;
3578 
3579  $classes = array(
3580  self::getObjectClassNameByQuestionType($questionType),
3581  self::getFeedbackClassNameByQuestionType($questionType)
3582  );
3583 
3584  if( $withGuiClass )
3585  {
3586  $classes[] = self::getGuiClassNameByQuestionType($questionType);
3587  }
3588 
3589  $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
3590  foreach ($pl_names as $pl_name)
3591  {
3592  $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
3593  if (strcmp($pl->getQuestionType(), $questionType) == 0)
3594  {
3595  foreach($classes as $class)
3596  {
3597  $pl->includeClass("class.{$class}.php");
3598  }
3599 
3600  break;
3601  }
3602  }
3603  }
3604 
3611  static function _getQuestionTypeName($type_tag)
3612  {
3613  if (file_exists("./Modules/TestQuestionPool/classes/class.".$type_tag.".php"))
3614  {
3615  global $lng;
3616  return $lng->txt($type_tag);
3617  }
3618  else
3619  {
3620  global $ilPluginAdmin;
3621  $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
3622  foreach ($pl_names as $pl_name)
3623  {
3624  $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
3625  if (strcmp($pl->getQuestionType(), $type_tag) == 0)
3626  {
3627  return $pl->getQuestionTypeTranslation();
3628  }
3629  }
3630  }
3631  return "";
3632  }
3633 
3643  public static function &_instanciateQuestionGUI($question_id)
3644  {
3645  return self::instantiateQuestionGUI($question_id);
3646  }
3647 
3655  public static function instantiateQuestionGUI($a_question_id)
3656  {
3657  global $ilCtrl, $ilDB, $lng, $ilUser;
3658 
3659  if (strcmp($a_question_id, "") != 0)
3660  {
3661  $question_type = assQuestion::_getQuestionType($a_question_id);
3662 
3663  assQuestion::_includeClass($question_type, 1);
3664 
3665  $question_type_gui = self::getGuiClassNameByQuestionType($question_type);
3666  $question_gui = new $question_type_gui();
3667  $question_gui->object->loadFromDb($a_question_id);
3668 
3669  $feedbackObjectClassname = self::getFeedbackClassNameByQuestionType($question_type);
3670  $question_gui->object->feedbackOBJ = new $feedbackObjectClassname($question_gui->object, $ilCtrl, $ilDB, $lng);
3671 
3672  $assSettings = new ilSetting('assessment');
3673  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionProcessLockerFactory.php';
3674  $processLockerFactory = new ilAssQuestionProcessLockerFactory($assSettings, $ilDB);
3675  $processLockerFactory->setQuestionId($question_gui->object->getId());
3676  $processLockerFactory->setUserId($ilUser->getId());
3677  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3678  $processLockerFactory->setAssessmentLogEnabled(ilObjAssessmentFolder::_enabledAssessmentLogging());
3679  $question_gui->object->setProcessLocker($processLockerFactory->getLocker());
3680  }
3681  else
3682  {
3683  global $ilLog;
3684  $ilLog->write('Instantiate question called without question id. (instantiateQuestionGUI@assQuestion)', $ilLog->WARNING);
3685  return null;
3686  }
3687  return $question_gui;
3688  }
3689 
3702  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
3703  {
3704  return $startrow;
3705  }
3706 
3710  public function __get($value)
3711  {
3712  switch ($value)
3713  {
3714  case "id":
3715  return $this->getId();
3716  break;
3717  case "title":
3718  return $this->getTitle();
3719  break;
3720  case "comment":
3721  return $this->getComment();
3722  break;
3723  case "owner":
3724  return $this->getOwner();
3725  break;
3726  case "author":
3727  return $this->getAuthor();
3728  break;
3729  case "question":
3730  return $this->getQuestion();
3731  break;
3732  case "points":
3733  return $this->getPoints();
3734  break;
3735  case "est_working_time":
3736  return $this->getEstimatedWorkingTime();
3737  break;
3738  case "shuffle":
3739  return $this->getShuffle();
3740  break;
3741  case "test_id":
3742  return $this->getTestId();
3743  break;
3744  case "obj_id":
3745  return $this->getObjId();
3746  break;
3747  case "ilias":
3748  return $this->ilias;
3749  break;
3750  case "tpl":
3751  return $this->tpl;
3752  break;
3753  case "page":
3754  return $this->page;
3755  break;
3756  case "outputType":
3757  return $this->getOutputType();
3758  break;
3759  case "suggested_solutions":
3760  return $this->getSuggestedSolutions();
3761  break;
3762  case "original_id":
3763  return $this->getOriginalId();
3764  break;
3765  default:
3766  if (array_key_exists($value, $this->arrData))
3767  {
3768  return $this->arrData[$value];
3769  }
3770  else
3771  {
3772  return null;
3773  }
3774  break;
3775  }
3776  }
3777 
3781  public function __set($key, $value)
3782  {
3783  switch ($key)
3784  {
3785  case "id":
3786  $this->setId($value);
3787  break;
3788  case "title":
3789  $this->setTitle($value);
3790  break;
3791  case "comment":
3792  $this->setComment($value);
3793  break;
3794  case "owner":
3795  $this->setOwner($value);
3796  break;
3797  case "author":
3798  $this->setAuthor($value);
3799  break;
3800  case "question":
3801  $this->setQuestion($value);
3802  break;
3803  case "points":
3804  $this->setPoints($value);
3805  break;
3806  case "est_working_time":
3807  if (is_array($value))
3808  {
3809  $this->setEstimatedWorkingTime($value["h"], $value["m"], $value["s"]);
3810  }
3811  break;
3812  case "shuffle":
3813  $this->setShuffle($value);
3814  break;
3815  case "test_id":
3816  $this->setTestId($value);
3817  break;
3818  case "obj_id":
3819  $this->setObjId($value);
3820  break;
3821  case "outputType":
3822  $this->setOutputType($value);
3823  break;
3824  case "original_id":
3825  $this->setOriginalId($value);
3826  break;
3827  case "page":
3828  $this->page =& $value;
3829  break;
3830  default:
3831  $this->arrData[$key] = $value;
3832  break;
3833  }
3834  }
3835 
3836  public function getNrOfTries()
3837  {
3838  return $this->nr_of_tries;
3839  }
3840 
3841  public function setNrOfTries($a_nr_of_tries)
3842  {
3843  $this->nr_of_tries = $a_nr_of_tries;
3844  }
3845 
3846  public function setExportImagePath($a_path)
3847  {
3848  $this->export_image_path = (string)$a_path;
3849  }
3850 
3851  function _questionExistsInTest($question_id, $test_id)
3852  {
3853  global $ilDB;
3854 
3855  if ($question_id < 1)
3856  {
3857  return false;
3858  }
3859 
3860  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE question_fi = %s AND test_fi = %s",
3861  array('integer', 'integer'),
3862  array($question_id, $test_id)
3863  );
3864  if ($result->numRows() == 1)
3865  {
3866  return true;
3867  }
3868  else
3869  {
3870  return false;
3871  }
3872  }
3873 
3880  function formatSAQuestion($a_q)
3881  {
3882  include_once("./Services/RTE/classes/class.ilRTE.php");
3883  $a_q = nl2br((string) ilRTE::_replaceMediaObjectImageSrc($a_q, 0));
3884  $a_q = str_replace("</li><br />", "</li>", $a_q);
3885  $a_q = str_replace("</li><br>", "</li>", $a_q);
3886 
3887  $a_q = ilUtil::insertLatexImages($a_q, "\[tex\]", "\[\/tex\]");
3888  $a_q = ilUtil::insertLatexImages($a_q, "<span class\=\"latex\">", "<\/span>");
3889 
3890  $a_q = str_replace('{', '&#123;', $a_q);
3891  $a_q = str_replace('}', '&#125;', $a_q);
3892 
3893  return $a_q;
3894  }
3895 
3896  // scorm2004-start ???
3897 
3903  function setPreventRteUsage($a_val)
3904  {
3905  $this->prevent_rte_usage = $a_val;
3906  }
3907 
3914  {
3915  return $this->prevent_rte_usage;
3916  }
3917 
3923  function setSelfAssessmentEditingMode($a_selfassessmenteditingmode)
3924  {
3925  $this->selfassessmenteditingmode = $a_selfassessmenteditingmode;
3926  }
3927 
3934  {
3936  }
3937 
3943  function setDefaultNrOfTries($a_defaultnroftries)
3944  {
3945  $this->defaultnroftries = $a_defaultnroftries;
3946  }
3947 
3954  {
3955  return $this->defaultnroftries;
3956  }
3957 
3958  // scorm2004-end ???
3959 
3965  public static function lookupParentObjId($questionId)
3966  {
3967  global $ilDB;
3968 
3969  $query = "SELECT obj_fi FROM qpl_questions WHERE question_id = %s";
3970 
3971  $res = $ilDB->queryF($query, array('integer'), array((int)$questionId));
3972  $row = $ilDB->fetchAssoc($res);
3973 
3974  return $row['obj_fi'];
3975  }
3976 
3987  public static function lookupOriginalParentObjId($originalQuestionId)
3988  {
3989  return self::lookupParentObjId($originalQuestionId);
3990  }
3991 
3992  protected function duplicateQuestionHints($originalQuestionId, $duplicateQuestionId)
3993  {
3994  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintList.php';
3995  $hintIds = ilAssQuestionHintList::duplicateListForQuestion($originalQuestionId, $duplicateQuestionId);
3996 
3998  {
3999  require_once 'Modules/TestQuestionPool/classes/class.ilAssHintPage.php';
4000 
4001  foreach($hintIds as $originalHintId => $duplicateHintId)
4002  {
4003  $originalPageObject = new ilAssHintPage($originalHintId);
4004  $originalXML = $originalPageObject->getXMLContent();
4005 
4006  $duplicatePageObject = new ilAssHintPage();
4007  $duplicatePageObject->setId($duplicateHintId);
4008  $duplicatePageObject->setParentId($this->getId());
4009  $duplicatePageObject->setXMLContent($originalXML);
4010  $duplicatePageObject->createFromXML();
4011  }
4012  }
4013  }
4014 
4027  public function isAnswered($active_id, $pass = null)
4028  {
4029  return true;
4030  }
4031 
4044  public static function isObligationPossible($questionId)
4045  {
4046  return false;
4047  }
4048 
4055  {
4056  $this->obligationsToBeConsidered = (bool)$obligationsToBeConsidered;
4057  }
4058 
4065  {
4066  return (bool)$this->obligationsToBeConsidered;
4067  }
4068 
4069  public function isAutosaveable()
4070  {
4071  return TRUE;
4072  }
4073 
4086  protected static function doesSolutionRecordsExist($activeId, $pass, $questionId)
4087  {
4088  // check if a solution was stored in tst_solution
4089 
4090  global $ilDB;
4091 
4092  $query = "
4093  SELECT count(active_fi) cnt
4094 
4095  FROM tst_solutions
4096 
4097  WHERE active_fi = %s
4098  AND question_fi = %s
4099  AND pass = %s
4100  ";
4101 
4102  $res = $ilDB->queryF(
4103  $query, array('integer','integer','integer'),
4104  array($activeId, $questionId, $pass)
4105  );
4106 
4107  $row = $ilDB->fetchAssoc($res);
4108 
4109  $solutionRecordsExist = (
4110  0 < (int)$row['cnt'] ? true : false
4111  );
4112 
4113  return $solutionRecordsExist;
4114  }
4115 
4123  {
4125  }
4126 
4134  {
4136  {
4137  require_once 'Modules/TestQuestionPool/exceptions/class.ilTestQuestionPoolException.php';
4138  throw new ilTestQuestionPoolException('invalid additional content editing mode given: '.$additinalContentEditingMode);
4139  }
4140 
4141  $this->additinalContentEditingMode = $additinalContentEditingMode;
4142  }
4143 
4151  {
4153  }
4154 
4162  public function isValidAdditionalContentEditingMode($additionalContentEditingMode)
4163  {
4164  if( in_array($additionalContentEditingMode, $this->getValidAdditionalContentEditingModes()) )
4165  {
4166  return true;
4167  }
4168 
4169  return false;
4170  }
4171 
4179  {
4180  return array(
4181  self::ADDITIONAL_CONTENT_EDITING_MODE_DEFAULT,
4182  self::ADDITIONAL_CONTENT_EDITING_MODE_PAGE_OBJECT
4183  );
4184  }
4185 
4190  {
4191  $this->questionChangeListeners[] = $listener;
4192  }
4193 
4197  public function getQuestionChangeListeners()
4198  {
4200  }
4201 
4202  private function notifyQuestionCreated()
4203  {
4204  foreach($this->getQuestionChangeListeners() as $listener)
4205  {
4206  $listener->notifyQuestionCreated($this);
4207  }
4208  }
4209 
4210  private function notifyQuestionEdited()
4211  {
4212  foreach($this->getQuestionChangeListeners() as $listener)
4213  {
4214  $listener->notifyQuestionEdited($this);
4215  }
4216  }
4217 
4218  private function notifyQuestionDeleted()
4219  {
4220  foreach($this->getQuestionChangeListeners() as $listener)
4221  {
4222  $listener->notifyQuestionDeleted($this);
4223  }
4224  }
4225 
4230  {
4231  require_once 'Services/Html/classes/class.ilHtmlPurifierFactory.php';
4232  return ilHtmlPurifierFactory::_getInstanceByType('qpl_usersolution');
4233  }
4234 
4235  abstract public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null);
4236 }