ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
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 {
22  const IMG_MIME_TYPE_JPG = 'image/jpeg';
23  const IMG_MIME_TYPE_PNG = 'image/png';
24  const IMG_MIME_TYPE_GIF = 'image/gif';
25 
26  protected static $allowedFileExtensionsByMimeType = array(
27  self::IMG_MIME_TYPE_JPG => array('jpg', 'jpeg'),
28  self::IMG_MIME_TYPE_PNG => array('png'),
29  self::IMG_MIME_TYPE_GIF => array('gif')
30  );
31 
32  protected static $allowedCharsetsByMimeType = array(
33  self::IMG_MIME_TYPE_JPG => array('binary'),
34  self::IMG_MIME_TYPE_PNG => array('binary'),
35  self::IMG_MIME_TYPE_GIF => array('binary')
36  );
37 
43  protected $id;
44 
50  protected $title;
51 
57  protected $comment;
58 
64  protected $owner;
65 
71  protected $author;
72 
78  protected $question;
79 
85  protected $points;
86 
92  protected $est_working_time;
93 
99  protected $shuffle;
100 
106  protected $test_id;
107 
113  protected $obj_id;
114 
120  protected $ilias;
121 
125  protected $tpl;
126 
130  protected $lng;
131 
135  protected $db;
136 
142  protected $outputType;
143 
150 
156  protected $original_id;
157 
163  protected $page;
164 
168  private $nr_of_tries;
169 
173  private $arrData;
174 
179 
184  protected $external_id = '';
185 
190 
195 
202 
208  public $feedbackOBJ = null;
209 
215  var $prevent_rte_usage = false;
216 
223 
230 
234  protected $questionChangeListeners = array();
235 
239  protected $processLocker;
240 
241  public $questionActionCmd = 'handleQuestionAction';
242 
246  private static $resultGateway = null;
247 
251  protected $step = null;
252 
253  protected $lastChange;
254 
258  protected $shuffler;
259 
263  private $obligationsToBeConsidered = false;
264 
275  function __construct(
276  $title = "",
277  $comment = "",
278  $author = "",
279  $owner = -1,
280  $question = ""
281  )
282  {
283  global $ilias, $lng, $tpl, $ilDB;
284 
285  $this->ilias = $ilias;
286  $this->lng = $lng;
287  $this->tpl = $tpl;
288  $this->db = $ilDB;
289 
290  $this->original_id = null;
291  $this->title = $title;
292  $this->comment = $comment;
293  $this->page = null;
294  $this->author = $author;
295  $this->setQuestion($question);
296  if (!$this->author)
297  {
298  $this->author = $this->ilias->account->fullname;
299  }
300  $this->owner = $owner;
301  if ($this->owner <= 0)
302  {
303  $this->owner = $this->ilias->account->id;
304  }
305  $this->id = -1;
306  $this->test_id = -1;
307  $this->suggested_solutions = array();
308  $this->shuffle = 1;
309  $this->nr_of_tries = 0;
310  $this->setEstimatedWorkingTime(0,1,0);
311  $this->outputType = OUTPUT_JAVASCRIPT;
312  $this->arrData = array();
313  $this->setExternalId('');
314 
315  $this->questionActionCmd = 'handleQuestionAction';
316 
317  $this->lastChange = null;
318 
319  require_once 'Services/Randomization/classes/class.ilArrayElementOrderKeeper.php';
320  $this->shuffler = new ilArrayElementOrderKeeper();
321  }
322 
323  protected static $forcePassResultsUpdateEnabled = false;
324 
326  {
327  self::$forcePassResultsUpdateEnabled = $forcePassResultsUpdateEnabled;
328  }
329 
330  public static function isForcePassResultUpdateEnabled()
331  {
332  return self::$forcePassResultsUpdateEnabled;
333  }
334 
335  public static function isAllowedImageMimeType($mimeType)
336  {
337  return (bool)count(self::getAllowedFileExtensionsForMimeType($mimeType));
338  }
339 
340  public static function fetchMimeTypeIdentifier($contentTypeString)
341  {
342  return current(explode(';', $contentTypeString));
343  }
344 
345  public static function getAllowedFileExtensionsForMimeType($mimeType)
346  {
347  foreach(self::$allowedFileExtensionsByMimeType as $allowedMimeType => $extensions)
348  {
349  $rexCharsets = implode('|', self::$allowedCharsetsByMimeType[$allowedMimeType]);
350  $rexMimeType = preg_quote($allowedMimeType, '/');
351 
352  $rex = '/^'.$rexMimeType.'(;(\s)*charset=('.$rexCharsets.'))*$/';
353 
354  if( !preg_match($rex, $mimeType) )
355  {
356  continue;
357  }
358 
359  return $extensions;
360  }
361 
362  return array();
363  }
364 
365  public static function isAllowedImageFileExtension($mimeType, $fileExtension)
366  {
367  return in_array(
368  strtolower($fileExtension), self::getAllowedFileExtensionsForMimeType($mimeType)
369  );
370  }
371 
372  public static function getAllowedImageFileExtensions()
373  {
374  $allowedExtensions = array();
375 
376  foreach(self::$allowedFileExtensionsByMimeType as $mimeType => $fileExtensions)
377  {
378  $allowedExtensions = array_merge($allowedExtensions, $fileExtensions);
379  }
380 
381  return $allowedExtensions;
382  }
383 
387  public function getShuffler()
388  {
389  return $this->shuffler;
390  }
391 
396  {
397  $this->shuffler = $shuffler;
398  }
399 
404  {
405  $this->processLocker = $processLocker;
406  }
407 
411  public function getProcessLocker()
412  {
413  return $this->processLocker;
414  }
415 
427  function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
428  {
429  include_once "./Modules/TestQuestionPool/classes/import/qti12/class." . $this->getQuestionType() . "Import.php";
430  $classname = $this->getQuestionType() . "Import";
431  $import = new $classname($this);
432  $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
433  }
434 
441  function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
442  {
443  include_once "./Modules/TestQuestionPool/classes/export/qti12/class." . $this->getQuestionType() . "Export.php";
444  $classname = $this->getQuestionType() . "Export";
445  $export = new $classname($this);
446  return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
447  }
448 
455  function isComplete()
456  {
457  return false;
458  }
459 
467  function questionTitleExists($questionpool_id, $title)
468  {
469  global $ilDB;
470 
471  $result = $ilDB->queryF("SELECT * FROM qpl_questions WHERE obj_fi = %s AND title = %s",
472  array('integer','text'),
473  array($questionpool_id, $title)
474  );
475  return ($result->numRows() > 0) ? TRUE : FALSE;
476  }
477 
485  function setTitle($title = "")
486  {
487  $this->title = $title;
488  }
489 
497  function setId($id = -1)
498  {
499  $this->id = $id;
500  }
501 
509  function setTestId($id = -1)
510  {
511  $this->test_id = $id;
512  }
513 
521  function setComment($comment = "")
522  {
523  $this->comment = $comment;
524  }
525 
534  {
535  $this->outputType = $outputType;
536  }
537 
538 
546  function setShuffle($shuffle = true)
547  {
548  if ($shuffle)
549  {
550  $this->shuffle = 1;
551  }
552  else
553  {
554  $this->shuffle = 0;
555  }
556  }
557 
568  function setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
569  {
570  $this->est_working_time = array("h" => (int)$hour, "m" => (int)$min, "s" => (int)$sec);
571  }
572 
580  {
581  $this->est_working_time = array(
582  'h' => (int)substr($durationString, 0, 2),
583  'm' => (int)substr($durationString, 3, 2),
584  's' => (int)substr($durationString, 6, 2)
585  );
586  }
587 
595  function keyInArray($searchkey, $array)
596  {
597  if ($searchkey)
598  {
599  foreach ($array as $key => $value)
600  {
601  if (strcmp($key, $searchkey)==0)
602  {
603  return true;
604  }
605  }
606  }
607  return false;
608  }
609 
617  function setAuthor($author = "")
618  {
619  if (!$author)
620  {
621  $author = $this->ilias->account->fullname;
622  }
623  $this->author = $author;
624  }
625 
633  function setOwner($owner = "")
634  {
635  $this->owner = $owner;
636  }
637 
645  function getTitle()
646  {
647  return $this->title;
648  }
649 
657  function getId()
658  {
659  return $this->id;
660  }
661 
669  function getShuffle()
670  {
671  return $this->shuffle;
672  }
673 
681  function getTestId()
682  {
683  return $this->test_id;
684  }
685 
693  function getComment()
694  {
695  return $this->comment;
696  }
697 
705  function getOutputType()
706  {
707  return $this->outputType;
708  }
709 
716  public function supportsJavascriptOutput()
717  {
718  return FALSE;
719  }
720 
721  public function supportsNonJsOutput()
722  {
723  return true;
724  }
725 
726  public function requiresJsSwitch()
727  {
728  return $this->supportsJavascriptOutput() && $this->supportsNonJsOutput();
729  }
730 
739  {
740  if (!$this->est_working_time)
741  {
742  $this->est_working_time = array("h" => 0, "m" => 0, "s" => 0);
743  }
745  }
746 
754  function getAuthor()
755  {
756  return $this->author;
757  }
758 
766  function getOwner()
767  {
768  return $this->owner;
769  }
770 
778  function getObjId()
779  {
780  return $this->obj_id;
781  }
782 
790  function setObjId($obj_id = 0)
791  {
792  $this->obj_id = $obj_id;
793  }
794 
798  public function setExternalId($external_id)
799  {
800  $this->external_id = $external_id;
801  }
802 
806  public function getExternalId()
807  {
808  if(!strlen($this->external_id))
809  {
810  if($this->getId() > 0)
811  {
812  return 'il_' . IL_INST_ID . '_qst_' . $this->getId();
813  }
814  else
815  {
816  return uniqid('', true);
817  }
818  }
819  else
820  {
821  return $this->external_id;
822  }
823  }
824 
831  function _getMaximumPoints($question_id)
832  {
833  global $ilDB;
834 
835  $points = 0;
836  $result = $ilDB->queryF("SELECT points FROM qpl_questions WHERE question_id = %s",
837  array('integer'),
838  array($question_id)
839  );
840  if ($result->numRows() == 1)
841  {
842  $row = $ilDB->fetchAssoc($result);
843  $points = $row["points"];
844  }
845  return $points;
846  }
847 
854  function &_getQuestionInfo($question_id)
855  {
856  global $ilDB;
857 
858  $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",
859  array('integer'),
860  array($question_id)
861  );
862  if ($result->numRows())
863  {
864  return $ilDB->fetchAssoc($result);
865  }
866  else return array();
867  }
868 
875  public static function _getSuggestedSolutionCount($question_id)
876  {
877  global $ilDB;
878 
879  $result = $ilDB->queryF("SELECT suggested_solution_id FROM qpl_sol_sug WHERE question_fi = %s",
880  array('integer'),
881  array($question_id)
882  );
883  return $result->numRows();
884  }
885 
892  public static function _getSuggestedSolutionOutput($question_id)
893  {
895  if (!is_object($question)) return "";
896  return $question->getSuggestedSolutionOutput();
897  }
898 
899  public function getSuggestedSolutionOutput()
900  {
901  $output = array();
902  foreach ($this->suggested_solutions as $solution)
903  {
904  switch ($solution["type"])
905  {
906  case "lm":
907  case "st":
908  case "pg":
909  case "git":
910  array_push($output, '<a href="' . assQuestion::_getInternalLinkHref($solution["internal_link"]) . '">' . $this->lng->txt("solution_hint") . '</a>');
911  break;
912  case "file":
913  $possible_texts = array_values(array_filter(array(
914  ilUtil::prepareFormOutput($solution['value']['filename']),
915  ilUtil::prepareFormOutput($solution['value']['name']),
916  $this->lng->txt('tst_show_solution_suggested')
917  )));
918  array_push($output, '<a href="' . $this->getSuggestedSolutionPathWeb() . $solution["value"]["name"] . '">' . $possible_texts[0] . '</a>');
919  break;
920  case "text":
921  $solutionValue = $solution["value"];
922  $solutionValue = $this->fixSvgToPng($solutionValue);
923  $solutionValue = $this->fixUnavailableSkinImageSources($solutionValue);
924  $output[] = $this->prepareTextareaOutput($solutionValue, true);
925  break;
926  }
927  }
928  return join($output, "<br />");
929  }
930 
939  function &_getSuggestedSolution($question_id, $subquestion_index = 0)
940  {
941  global $ilDB;
942 
943  $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s AND subquestion_index = %s",
944  array('integer','integer'),
945  array($question_id, $subquestion_index)
946  );
947  if ($result->numRows() == 1)
948  {
949  $row = $ilDB->fetchAssoc($result);
950  return array(
951  "internal_link" => $row["internal_link"],
952  "import_id" => $row["import_id"]
953  );
954  }
955  else
956  {
957  return array();
958  }
959  }
960 
966  public function getSuggestedSolutions()
967  {
969  }
970 
978  function _getReachedPoints($active_id, $question_id, $pass = NULL)
979  {
980  global $ilDB;
981 
982  $points = 0;
983  if (is_null($pass))
984  {
985  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
986  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
987  }
988  $result = $ilDB->queryF("SELECT * FROM tst_test_result WHERE active_fi = %s AND question_fi = %s AND pass = %s",
989  array('integer','integer','integer'),
990  array($active_id, $question_id, $pass)
991  );
992  if ($result->numRows() == 1)
993  {
994  $row = $ilDB->fetchAssoc($result);
995  $points = $row["points"];
996  }
997  return $points;
998  }
999 
1008  function getReachedPoints($active_id, $pass = NULL)
1009  {
1010  return round($this->_getReachedPoints($active_id, $this->getId(), $pass), 2);
1011  }
1012 
1019  function getMaximumPoints()
1020  {
1021  return $this->points;
1022  }
1023 
1035  final public function getAdjustedReachedPoints($active_id, $pass = NULL, $authorizedSolution = true)
1036  {
1037  if (is_null($pass))
1038  {
1039  include_once "./Modules/Test/classes/class.ilObjTest.php";
1040  $pass = ilObjTest::_getPass($active_id);
1041  }
1042 
1043  // determine reached points for submitted solution
1044  $reached_points = $this->calculateReachedPoints($active_id, $pass, $authorizedSolution);
1045 
1046 
1047 
1048  // deduct points for requested hints from reached points
1049  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
1050  $hintTracking = new ilAssQuestionHintTracking($this->getId(), $active_id, $pass);
1051  $requestsStatisticData = $hintTracking->getRequestStatisticDataByQuestionAndTestpass();
1052  $reached_points = $reached_points - $requestsStatisticData->getRequestsPoints();
1053 
1054  // adjust reached points regarding to tests scoring options
1055  $reached_points = $this->adjustReachedPointsByScoringOptions($reached_points, $active_id, $pass);
1056 
1057  return $reached_points;
1058  }
1059 
1069  final public function calculateResultsFromSolution($active_id, $pass = NULL, $obligationsEnabled = false)
1070  {
1071  global $ilDB, $ilUser;
1072 
1073  if( is_null($pass) )
1074  {
1075  include_once "./Modules/Test/classes/class.ilObjTest.php";
1076  $pass = ilObjTest::_getPass($active_id);
1077  }
1078 
1079  // determine reached points for submitted solution
1080  $reached_points = $this->calculateReachedPoints($active_id, $pass);
1081 
1082  // deduct points for requested hints from reached points
1083  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
1084  $questionHintTracking = new ilAssQuestionHintTracking($this->getId(), $active_id, $pass);
1085  $requestsStatisticData = $questionHintTracking->getRequestStatisticDataByQuestionAndTestpass();
1086  $reached_points = $reached_points - $requestsStatisticData->getRequestsPoints();
1087 
1088  // adjust reached points regarding to tests scoring options
1089  $reached_points = $this->adjustReachedPointsByScoringOptions($reached_points, $active_id, $pass);
1090 
1091  if( $obligationsEnabled && ilObjTest::isQuestionObligatory($this->getId()) )
1092  {
1093  $isAnswered = $this->isAnswered($active_id, $pass);
1094  }
1095  else
1096  {
1097  $isAnswered = true;
1098  }
1099 
1100  if( is_null($reached_points) ) $reached_points = 0;
1101 
1102  $this->getProcessLocker()->requestUserQuestionResultUpdateLock();
1103 
1104  $query = "
1105  DELETE FROM tst_test_result
1106 
1107  WHERE active_fi = %s
1108  AND question_fi = %s
1109  AND pass = %s
1110  ";
1111 
1112  $types = array('integer', 'integer', 'integer');
1113  $values = array($active_id, $this->getId(), $pass);
1114 
1115  if( $this->getStep() !== NULL )
1116  {
1117  $query .= "
1118  AND step = %s
1119  ";
1120 
1121  $types[] = 'integer';
1122  $values[] = $this->getStep();
1123  }
1124 
1125  $affectedRows = $ilDB->manipulateF($query, $types, $values);
1126 
1127  $next_id = $ilDB->nextId("tst_test_result");
1128 
1129  $fieldData = array(
1130  'test_result_id' => array('integer', $next_id),
1131  'active_fi' => array('integer', $active_id),
1132  'question_fi' => array('integer', $this->getId()),
1133  'pass' => array('integer', $pass),
1134  'points' => array('float', $reached_points),
1135  'tstamp' => array('integer', time()),
1136  'hint_count' => array('integer', $requestsStatisticData->getRequestsCount()),
1137  'hint_points' => array('float', $requestsStatisticData->getRequestsPoints()),
1138  'answered' => array('integer', $isAnswered)
1139  );
1140 
1141  if( $this->getStep() !== NULL )
1142  {
1143  $fieldData['step'] = array('integer', $this->getStep());
1144  }
1145 
1146  $ilDB->insert('tst_test_result', $fieldData);
1147 
1148  $this->getProcessLocker()->releaseUserQuestionResultUpdateLock();
1149 
1150  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1151 
1153  {
1154  $this->logAction(
1155  sprintf(
1156  $this->lng->txtlng(
1157  "assessment", "log_user_answered_question", ilObjAssessmentFolder::_getLogLanguage()
1158  ),
1159  $reached_points
1160  ),
1161  $active_id,
1162  $this->getId()
1163  );
1164  }
1165 
1166  // update test pass results
1167  $this->_updateTestPassResults($active_id, $pass, $obligationsEnabled, $this->getProcessLocker());
1168 
1169  // Update objective status
1170  include_once 'Modules/Course/classes/class.ilCourseObjectiveResult.php';
1171  ilCourseObjectiveResult::_updateObjectiveResult($ilUser->getId(),$active_id,$this->getId());
1172  }
1173 
1182  final public function persistWorkingState($active_id, $pass = NULL, $obligationsEnabled = false, $authorized = true)
1183  {
1184  if( $pass === null )
1185  {
1186  require_once 'Modules/Test/classes/class.ilObjTest.php';
1187  $pass = ilObjTest::_getPass($active_id);
1188  }
1189 
1190  $this->getProcessLocker()->requestPersistWorkingStateLock();
1191 
1192  $saveStatus = $this->saveWorkingData($active_id, $pass, $authorized);
1193 
1194  if( $authorized )
1195  {
1196  $this->calculateResultsFromSolution($active_id, $pass, $obligationsEnabled);
1197  }
1198 
1199  $this->reworkWorkingData($active_id, $pass, $obligationsEnabled, $authorized);
1200 
1201  $this->getProcessLocker()->releasePersistWorkingStateLock();
1202 
1203  return $saveStatus;
1204  }
1205 
1209  final public function persistPreviewState(ilAssQuestionPreviewSession $previewSession)
1210  {
1211  if( !$this->validateSolutionSubmit() )
1212  {
1213  return false;
1214  }
1215 
1216  $this->savePreviewData($previewSession);
1217  }
1218 
1228  abstract public function saveWorkingData($active_id, $pass = NULL, $authorized = true);
1229 
1239  //abstract protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $intermediate);
1240 
1241  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
1242  {
1243  $previewSession->setParticipantsSolution($this->getSolutionSubmit());
1244  }
1245 
1248  {
1249  global $ilDB;
1250 
1251  include_once "./Modules/Test/classes/class.ilObjTest.php";
1252  include_once "./Modules/Test/classes/class.assMarkSchema.php";
1253 
1254  $pass = ilObjTest::_getResultPass($active_id);
1255 
1256  $query = "
1257  SELECT tst_pass_result.*
1258  FROM tst_pass_result
1259  WHERE active_fi = %s
1260  AND pass = %s
1261  ";
1262 
1263  $result = $ilDB->queryF(
1264  $query, array('integer','integer'), array($active_id, $pass)
1265  );
1266 
1267  $row = $ilDB->fetchAssoc($result);
1268 
1269  $max = $row['maxpoints'];
1270  $reached = $row['points'];
1271 
1272  $obligationsAnswered = (int)$row['obligations_answered'];
1273 
1274  include_once "./Modules/Test/classes/class.assMarkSchema.php";
1275 
1276  $percentage = (!$max) ? 0 : ($reached / $max) * 100.0;
1277 
1278  $mark = ASS_MarkSchema::_getMatchingMarkFromActiveId($active_id, $percentage);
1279 
1280  $isPassed = ( $mark["passed"] ? 1 : 0 );
1281  $isFailed = ( !$mark["passed"] ? 1 : 0 );
1282 
1283  if( is_object($processLocker) )
1284  {
1285  $processLocker->requestUserTestResultUpdateLock();
1286  }
1287 
1288  $query = "
1289  DELETE FROM tst_result_cache
1290  WHERE active_fi = %s
1291  ";
1292 
1293  $affectedRows = $ilDB->manipulateF(
1294  $query, array('integer'), array($active_id)
1295  );
1296 
1297  $ilDB->insert('tst_result_cache', array(
1298  'active_fi'=> array('integer', $active_id),
1299  'pass'=> array('integer', strlen($pass) ? $pass : 0),
1300  'max_points'=> array('float', strlen($max) ? $max : 0),
1301  'reached_points'=> array('float', strlen($reached) ? $reached : 0),
1302  'mark_short'=> array('text', strlen($mark["short_name"]) ? $mark["short_name"] : " "),
1303  'mark_official'=> array('text', strlen($mark["official_name"]) ? $mark["official_name"] : " "),
1304  'passed'=> array('integer', $isPassed),
1305  'failed'=> array('integer', $isFailed),
1306  'tstamp'=> array('integer', time()),
1307  'hint_count'=> array('integer', $row['hint_count']),
1308  'hint_points'=> array('float', $row['hint_points']),
1309  'obligations_answered' => array('integer', $obligationsAnswered)
1310  ));
1311 
1312  if( is_object($processLocker) )
1313  {
1314  $processLocker->releaseUserTestResultUpdateLock();
1315  }
1316  }
1317 
1319  function _updateTestPassResults($active_id, $pass, $obligationsEnabled = false, ilAssQuestionProcessLocker $processLocker = null, $test_obj_id = null)
1320  {
1321  global $ilDB;
1322 
1323  include_once "./Modules/Test/classes/class.ilObjTest.php";
1324 
1325  if( self::getResultGateway() !== null )
1326  {
1327  $data = self::getResultGateway()->getQuestionCountAndPointsForPassOfParticipant($active_id, $pass);
1328  $time = self::getResultGateway()->getWorkingTimeOfParticipantForPass($active_id, $pass);
1329  }
1330  else
1331  {
1334  }
1335 
1336 
1337  // update test pass results
1338 
1339  $result = $ilDB->queryF("
1340  SELECT SUM(points) reachedpoints,
1341  SUM(hint_count) hint_count,
1342  SUM(hint_points) hint_points,
1343  COUNT(DISTINCT(question_fi)) answeredquestions
1344  FROM tst_test_result
1345  WHERE active_fi = %s
1346  AND pass = %s
1347  ",
1348  array('integer','integer'),
1349  array($active_id, $pass)
1350  );
1351 
1352  if ($result->numRows() > 0)
1353  {
1354  if( $obligationsEnabled )
1355  {
1356  $query = '
1357  SELECT count(*) cnt,
1358  min( answered ) answ
1359  FROM tst_test_question
1360  INNER JOIN tst_active
1361  ON active_id = %s
1362  AND tst_test_question.test_fi = tst_active.test_fi
1363  LEFT JOIN tst_test_result
1364  ON tst_test_result.active_fi = %s
1365  AND tst_test_result.pass = %s
1366  AND tst_test_question.question_fi = tst_test_result.question_fi
1367  WHERE obligatory = 1';
1368 
1369  $result_obligatory = $ilDB->queryF(
1370  $query, array('integer','integer','integer'), array($active_id, $active_id, $pass)
1371  );
1372 
1373  $row_obligatory = $ilDB->fetchAssoc($result_obligatory);
1374 
1375  if ($row_obligatory['cnt'] == 0)
1376  {
1377  $obligations_answered = 1;
1378  }
1379  else
1380  {
1381  $obligations_answered = (int) $row_obligatory['answ'];
1382  }
1383  }
1384  else
1385  {
1386  $obligations_answered = 1;
1387  }
1388 
1389  $row = $ilDB->fetchAssoc($result);
1390 
1391  if( $row['hint_count'] === null ) $row['hint_count'] = 0;
1392  if( $row['hint_points'] === null ) $row['hint_points'] = 0;
1393 
1394  $exam_identifier = ilObjTest::buildExamId( $active_id, $pass, $test_obj_id);
1395 
1396  if( is_object($processLocker) )
1397  {
1398  $processLocker->requestUserPassResultUpdateLock();
1399  }
1400 
1401  /*
1402  $query = "
1403  DELETE FROM tst_pass_result
1404 
1405  WHERE active_fi = %s
1406  AND pass = %s
1407  ";
1408 
1409  $affectedRows = $ilDB->manipulateF(
1410  $query, array('integer','integer'), array($active_id, $pass)
1411  );
1412  */
1414  $ilDB->replace('tst_pass_result',
1415  array(
1416  'active_fi' => array('integer', $active_id),
1417  'pass' => array('integer', strlen($pass) ? $pass : 0)),
1418  array(
1419  'points' => array('float', $row['reachedpoints'] ? $row['reachedpoints'] : 0),
1420  'maxpoints' => array('float', $data['points']),
1421  'questioncount' => array('integer', $data['count']),
1422  'answeredquestions' => array('integer', $row['answeredquestions']),
1423  'workingtime' => array('integer', $time),
1424  'tstamp' => array('integer', time()),
1425  'hint_count' => array('integer', $row['hint_count']),
1426  'hint_points' => array('float', $row['hint_points']),
1427  'obligations_answered' => array('integer', $obligations_answered),
1428  'exam_id' => array('text', $exam_identifier)
1429  )
1430  );
1431 
1432  /*
1433  $ilDB->insert('tst_pass_result', array(
1434  'active_fi' => array('integer', $active_id),
1435  'pass' => array('integer', strlen($pass) ? $pass : 0),
1436  'points' => array('float', $row['reachedpoints'] ? $row['reachedpoints'] : 0),
1437  'maxpoints' => array('float', $data['points']),
1438  'questioncount' => array('integer', $data['count']),
1439  'answeredquestions' => array('integer', $row['answeredquestions']),
1440  'workingtime' => array('integer', $time),
1441  'tstamp' => array('integer', time()),
1442  'hint_count' => array('integer', $row['hint_count']),
1443  'hint_points' => array('float', $row['hint_points']),
1444  'obligations_answered' => array('integer', $obligations_answered),
1445  'exam_id' => array('text', $exam_identifier)
1446  ));
1447  */
1448 
1449  if( is_object($processLocker) )
1450  {
1451  $this->getProcessLocker()->releaseUserPassResultUpdateLock();
1452  }
1453  }
1454 
1456 
1457  return array(
1458  'active_fi' => $active_id,
1459  'pass' => $pass,
1460  'points' => ($row["reachedpoints"]) ? $row["reachedpoints"] : 0,
1461  'maxpoints' => $data["points"],
1462  'questioncount' => $data["count"],
1463  'answeredquestions' => $row["answeredquestions"],
1464  'workingtime' => $time,
1465  'tstamp' => time(),
1466  'hint_count' => $row['hint_count'],
1467  'hint_points' => $row['hint_points'],
1468  'obligations_answered' => $obligations_answered,
1469  'exam_id' => $exam_identifier
1470  );
1471  }
1472 
1480  function logAction($logtext = "", $active_id = "", $question_id = "")
1481  {
1482  global $ilUser;
1483 
1484  $original_id = "";
1485  if (strcmp($question_id, "") != 0)
1486  {
1487  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
1488  $original_id = assQuestion::_getOriginalId($question_id);
1489  }
1490  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
1491  include_once "./Modules/Test/classes/class.ilObjTest.php";
1492  ilObjAssessmentFolder::_addLog($ilUser->id, ilObjTest::_getObjectIDFromActiveID($active_id), $logtext, $question_id, $original_id);
1493  }
1494 
1502  function _logAction($logtext = "", $active_id = "", $question_id = "")
1503  {
1504  global $ilUser;
1505 
1506  $original_id = "";
1507  if (strcmp($question_id, "") != 0)
1508  {
1509  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
1510  $original_id = assQuestion::_getOriginalId($question_id);
1511  }
1512  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
1513  include_once "./Modules/Test/classes/class.ilObjTest.php";
1514  ilObjAssessmentFolder::_addLog($ilUser->id, ilObjTest::_getObjectIDFromActiveID($active_id), $logtext, $question_id, $original_id);
1515  }
1516 
1524  function moveUploadedMediaFile($file, $name)
1525  {
1526  $mediatempdir = CLIENT_WEB_DIR . "/assessment/temp";
1527  if (!@is_dir($mediatempdir)) ilUtil::createDirectory($mediatempdir);
1528  $temp_name = tempnam($mediatempdir, $name . "_____");
1529  $temp_name = str_replace("\\", "/", $temp_name);
1530  @unlink($temp_name);
1531  if (!ilUtil::moveUploadedFile($file, $name, $temp_name))
1532  {
1533  return FALSE;
1534  }
1535  else
1536  {
1537  return $temp_name;
1538  }
1539  }
1540 
1547  return CLIENT_WEB_DIR . "/assessment/$this->obj_id/$this->id/solution/";
1548  }
1549 
1556  function getJavaPath() {
1557  return CLIENT_WEB_DIR . "/assessment/$this->obj_id/$this->id/java/";
1558  }
1559 
1566  function getImagePath($question_id = null, $object_id = null)
1567  {
1568  if( $question_id === null)
1569  {
1570  $question_id = $this->id;
1571  }
1572 
1573  if( $object_id === null)
1574  {
1575  $object_id = $this->obj_id;
1576  }
1577 
1578  return $this->buildImagePath($question_id, $object_id);
1579  }
1580 
1581  public function buildImagePath($questionId, $parentObjectId)
1582  {
1583  return CLIENT_WEB_DIR . "/assessment/{$parentObjectId}/{$questionId}/images/";
1584  }
1585 
1592  function getFlashPath()
1593  {
1594  return CLIENT_WEB_DIR . "/assessment/$this->obj_id/$this->id/flash/";
1595  }
1596 
1603  function getJavaPathWeb()
1604  {
1605  include_once "./Services/Utilities/classes/class.ilUtil.php";
1606  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/java/";
1608  }
1609 
1616  {
1617  include_once "./Services/Utilities/classes/class.ilUtil.php";
1618  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/solution/";
1620  }
1621 
1628  function getImagePathWeb()
1629  {
1630  if(!$this->export_image_path)
1631  {
1632  include_once "./Services/Utilities/classes/class.ilUtil.php";
1633  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/images/";
1635  }
1636  else
1637  {
1638  return $this->export_image_path;
1639  }
1640  }
1641 
1648  function getFlashPathWeb()
1649  {
1650  include_once "./Services/Utilities/classes/class.ilUtil.php";
1651  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/flash/";
1653  }
1654 
1655  public function getUserSolutionPreferingIntermediate($active_id, $pass = NULL)
1656  {
1657  $solution = $this->getSolutionValues($active_id, $pass, false);
1658 
1659  if( !count($solution) )
1660  {
1661  $solution = $this->getSolutionValues($active_id, $pass, true);
1662  }
1663 
1664  return $solution;
1665  }
1666 
1670  public function getSolutionValues($active_id, $pass = NULL, $authorized = true)
1671  {
1672  global $ilDB;
1673 
1674  if (is_null($pass))
1675  {
1676  $pass = $this->getSolutionMaxPass($active_id);
1677  }
1678 
1679  if( $this->getStep() !== NULL )
1680  {
1681  $query = "
1682  SELECT *
1683  FROM tst_solutions
1684  WHERE active_fi = %s
1685  AND question_fi = %s
1686  AND pass = %s
1687  AND step = %s
1688  AND authorized = %s
1689  ORDER BY solution_id";
1690 
1691  $result = $ilDB->queryF($query, array('integer', 'integer', 'integer', 'integer', 'integer'),
1692  array($active_id, $this->getId(), $pass, $this->getStep(), (int)$authorized)
1693  );
1694  }
1695  else
1696  {
1697  $query = "
1698  SELECT *
1699  FROM tst_solutions
1700  WHERE active_fi = %s
1701  AND question_fi = %s
1702  AND pass = %s
1703  AND authorized = %s
1704  ORDER BY solution_id
1705  ";
1706 
1707  $result = $ilDB->queryF($query, array('integer', 'integer', 'integer', 'integer'),
1708  array($active_id, $this->getId(), $pass, (int)$authorized)
1709  );
1710  }
1711 
1712  $values = array();
1713 
1714  while( $row = $ilDB->fetchAssoc($result) )
1715  {
1716  $values[] = $row;
1717  }
1718 
1719  return $values;
1720  }
1721 
1728  function isInUse($question_id = "")
1729  {
1730  global $ilDB;
1731 
1732  if ($question_id < 1) $question_id = $this->getId();
1733  $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",
1734  array('integer'),
1735  array($question_id)
1736  );
1737  $row = $ilDB->fetchAssoc($result);
1738  $count = $row["question_count"];
1739 
1740  $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",
1741  array('integer'),
1742  array($question_id)
1743  );
1744  $count += $result->numRows();
1745 
1746  return $count;
1747  }
1748 
1755  function isClone($question_id = "")
1756  {
1757  global $ilDB;
1758 
1759  if ($question_id < 1) $question_id = $this->id;
1760  $result = $ilDB->queryF("SELECT original_id FROM qpl_questions WHERE question_id = %s",
1761  array('integer'),
1762  array($question_id)
1763  );
1764  $row = $ilDB->fetchAssoc($result);
1765  return ($row["original_id"] > 0) ? TRUE : FALSE;
1766  }
1767 
1774  function pcArrayShuffle($array)
1775  {
1776  $keys = array_keys($array);
1777  shuffle($keys);
1778  $result = array();
1779  foreach ($keys as $key)
1780  {
1781  $result[$key] = $array[$key];
1782  }
1783  return $result;
1784  }
1785 
1791  function getQuestionTypeFromDb($question_id)
1792  {
1793  global $ilDB;
1794 
1795  $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",
1796  array('integer'),
1797  array($question_id)
1798  );
1799  $data = $ilDB->fetchAssoc($result);
1800  return $data["type_tag"];
1801  }
1802 
1810  {
1811  return "";
1812  }
1813 
1821  {
1822  return "";
1823  }
1824 
1831  function deleteAnswers($question_id)
1832  {
1833  global $ilDB;
1834  $answer_table_name = $this->getAnswerTableName();
1835 
1836  if( !is_array($answer_table_name) )
1837  {
1838  $answer_table_name = array($answer_table_name);
1839  }
1840 
1841  foreach ($answer_table_name as $table)
1842  {
1843  if (strlen($table))
1844  {
1845  $affectedRows = $ilDB->manipulateF("DELETE FROM $table WHERE question_fi = %s",
1846  array('integer'),
1847  array($question_id)
1848  );
1849  }
1850  }
1851  }
1852 
1859  function deleteAdditionalTableData($question_id)
1860  {
1861  global $ilDB;
1862 
1863  $additional_table_name = $this->getAdditionalTableName();
1864 
1865  if( !is_array($additional_table_name) )
1866  {
1867  $additional_table_name = array($additional_table_name);
1868  }
1869 
1870  foreach ($additional_table_name as $table)
1871  {
1872  if (strlen($table))
1873  {
1874  $affectedRows = $ilDB->manipulateF("DELETE FROM $table WHERE question_fi = %s",
1875  array('integer'),
1876  array($question_id)
1877  );
1878  }
1879  }
1880  }
1881 
1888  protected function deletePageOfQuestion($question_id)
1889  {
1890  include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
1891  $page = new ilAssQuestionPage($question_id);
1892  $page->delete();
1893  return true;
1894  }
1895 
1902  public function delete($question_id)
1903  {
1904  global $ilDB, $ilLog;
1905 
1906  if ($question_id < 1) return true; // nothing to do
1907 
1908  $result = $ilDB->queryF("SELECT obj_fi FROM qpl_questions WHERE question_id = %s",
1909  array('integer'),
1910  array($question_id)
1911  );
1912  if ($result->numRows() == 1)
1913  {
1914  $row = $ilDB->fetchAssoc($result);
1915  $obj_id = $row["obj_fi"];
1916  }
1917  else
1918  {
1919  return true; // nothing to do
1920  }
1921  try
1922  {
1923  $this->deletePageOfQuestion($question_id);
1924  }
1925  catch (Exception $e)
1926  {
1927  $ilLog->write("EXCEPTION: Could not delete page of question $question_id: $e");
1928  return false;
1929  }
1930 
1931  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_questions WHERE question_id = %s",
1932  array('integer'),
1933  array($question_id)
1934  );
1935  if ($affectedRows == 0) return false;
1936 
1937  try
1938  {
1939  $this->deleteAdditionalTableData($question_id);
1940  $this->deleteAnswers($question_id);
1941  $this->feedbackOBJ->deleteGenericFeedbacks($question_id, $this->isAdditionalContentEditingModePageObject());
1942  $this->feedbackOBJ->deleteSpecificAnswerFeedbacks($question_id, $this->isAdditionalContentEditingModePageObject());
1943  }
1944  catch (Exception $e)
1945  {
1946  $ilLog->write("EXCEPTION: Could not delete additional table data of question $question_id: $e");
1947  return false;
1948  }
1949 
1950  try
1951  {
1952  // delete the question in the tst_test_question table (list of test questions)
1953  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE question_fi = %s",
1954  array('integer'),
1955  array($question_id)
1956  );
1957  }
1958  catch (Exception $e)
1959  {
1960  $ilLog->write("EXCEPTION: Could not delete delete question $question_id from a test: $e");
1961  return false;
1962  }
1963 
1964  try
1965  {
1966  // delete suggested solutions contained in the question
1967  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s",
1968  array('integer'),
1969  array($question_id)
1970  );
1971  }
1972  catch (Exception $e)
1973  {
1974  $ilLog->write("EXCEPTION: Could not delete suggested solutions of question $question_id: $e");
1975  return false;
1976  }
1977 
1978  try
1979  {
1980  $directory = CLIENT_WEB_DIR . "/assessment/" . $obj_id . "/$question_id";
1981  if (preg_match("/\d+/", $obj_id) and preg_match("/\d+/", $question_id) and is_dir($directory))
1982  {
1983  include_once "./Services/Utilities/classes/class.ilUtil.php";
1984  ilUtil::delDir($directory);
1985  }
1986  }
1987  catch (Exception $e)
1988  {
1989  $ilLog->write("EXCEPTION: Could not delete question file directory $directory of question $question_id: $e");
1990  return false;
1991  }
1992 
1993  try
1994  {
1995  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
1996  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $question_id);
1997  // remaining usages are not in text anymore -> delete them
1998  // and media objects (note: delete method of ilObjMediaObject
1999  // checks whether object is used in another context; if yes,
2000  // the object is not deleted!)
2001  foreach($mobs as $mob)
2002  {
2003  ilObjMediaObject::_removeUsage($mob, "qpl:html", $question_id);
2004  if (ilObjMediaObject::_exists($mob))
2005  {
2006  $mob_obj =& new ilObjMediaObject($mob);
2007  $mob_obj->delete();
2008  }
2009  }
2010  }
2011  catch (Exception $e)
2012  {
2013  $ilLog->write("EXCEPTION: Error deleting the media objects of question $question_id: $e");
2014  return false;
2015  }
2016 
2017  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
2019 
2020  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintList.php';
2022 
2023  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionSkillAssignmentList.php';
2024  $assignmentList = new ilAssQuestionSkillAssignmentList($ilDB);
2025  $assignmentList->setParentObjId($obj_id);
2026  $assignmentList->setQuestionIdFilter($question_id);
2027  $assignmentList->loadFromDb();
2028  foreach($assignmentList->getAssignmentsByQuestionId($question_id) as $assignment)
2029  {
2030  /* @var ilAssQuestionSkillAssignment $assignment */
2031  $assignment->deleteFromDb();
2032  }
2033 
2034  $this->deleteTaxonomyAssignments();
2035 
2036  try
2037  {
2038  // update question count of question pool
2039  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
2041  }
2042  catch (Exception $e)
2043  {
2044  $ilLog->write("EXCEPTION: Error updating the question pool question count of question pool " . $this->getObjId() . " when deleting question $question_id: $e");
2045  return false;
2046  }
2047 
2048  $this->notifyQuestionDeleted($this);
2049 
2050  return true;
2051  }
2052 
2053  private function deleteTaxonomyAssignments()
2054  {
2055  require_once 'Services/Taxonomy/classes/class.ilObjTaxonomy.php';
2056  require_once 'Services/Taxonomy/classes/class.ilTaxNodeAssignment.php';
2057  $taxIds = ilObjTaxonomy::getUsageOfObject($this->getObjId());
2058 
2059  foreach($taxIds as $taxId)
2060  {
2061  $taxNodeAssignment = new ilTaxNodeAssignment('qpl', $this->getObjId(), 'quest', $taxId);
2062  $taxNodeAssignment->deleteAssignmentsOfItem($this->getId());
2063  }
2064  }
2065 
2069  function getTotalAnswers()
2070  {
2071  return $this->_getTotalAnswers($this->id);
2072  }
2073 
2080  function _getTotalAnswers($a_q_id)
2081  {
2082  global $ilDB;
2083 
2084  // get all question references to the question id
2085  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id = %s OR question_id = %s",
2086  array('integer','integer'),
2087  array($a_q_id, $a_q_id)
2088  );
2089  if ($result->numRows() == 0)
2090  {
2091  return 0;
2092  }
2093  $found_id = array();
2094  while ($row = $ilDB->fetchAssoc($result))
2095  {
2096  array_push($found_id, $row["question_id"]);
2097  }
2098 
2099  $result = $ilDB->query("SELECT * FROM tst_test_result WHERE " . $ilDB->in('question_fi', $found_id, false, 'integer'));
2100 
2101  return $result->numRows();
2102  }
2103 
2104 
2111  public static function _getTotalRightAnswers($a_q_id)
2112  {
2113  global $ilDB;
2114  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id = %s OR question_id = %s",
2115  array('integer','integer'),
2116  array($a_q_id, $a_q_id)
2117  );
2118  if ($result->numRows() == 0)
2119  {
2120  return 0;
2121  }
2122  $found_id = array();
2123  while ($row = $ilDB->fetchAssoc($result))
2124  {
2125  array_push($found_id, $row["question_id"]);
2126  }
2127  $result = $ilDB->query("SELECT * FROM tst_test_result WHERE " . $ilDB->in('question_fi', $found_id, false, 'integer'));
2128  $answers = array();
2129  while ($row = $ilDB->fetchAssoc($result))
2130  {
2131  $reached = $row["points"];
2132  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
2133  $max = assQuestion::_getMaximumPoints($row["question_fi"]);
2134  array_push($answers, array("reached" => $reached, "max" => $max));
2135  }
2136  $max = 0.0;
2137  $reached = 0.0;
2138  foreach ($answers as $key => $value)
2139  {
2140  $max += $value["max"];
2141  $reached += $value["reached"];
2142  }
2143  if ($max > 0)
2144  {
2145  return $reached / $max;
2146  }
2147  else
2148  {
2149  return 0;
2150  }
2151  }
2152 
2158  function _getTitle($a_q_id)
2159  {
2160  global $ilDB;
2161  $result = $ilDB->queryF("SELECT title FROM qpl_questions WHERE question_id = %s",
2162  array('integer'),
2163  array($a_q_id)
2164  );
2165  if ($result->numRows() == 1)
2166  {
2167  $row = $ilDB->fetchAssoc($result);
2168  return $row["title"];
2169  }
2170  else
2171  {
2172  return "";
2173  }
2174  }
2175 
2181  function _getQuestionText($a_q_id)
2182  {
2183  global $ilDB;
2184  $result = $ilDB->queryF("SELECT question_text FROM qpl_questions WHERE question_id = %s",
2185  array('integer'),
2186  array($a_q_id)
2187  );
2188  if ($result->numRows() == 1)
2189  {
2190  $row = $ilDB->fetchAssoc($result);
2191  return $row["question_text"];
2192  }
2193  else
2194  {
2195  return "";
2196  }
2197  }
2198 
2199  public static function isFileAvailable($file)
2200  {
2201  if( !file_exists($file) )
2202  {
2203  return false;
2204  }
2205 
2206  if( !is_file($file) )
2207  {
2208  return false;
2209  }
2210 
2211  if( !is_readable($file) )
2212  {
2213  return false;
2214  }
2215 
2216  return true;
2217  }
2218 
2220  {
2221  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
2222  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $a_q_id);
2223  foreach ($mobs as $mob)
2224  {
2225  ilObjMediaObject::_saveUsage($mob, "qpl:html", $this->getId());
2226  }
2227  }
2228 
2230  {
2231  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
2232  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
2233  foreach ($mobs as $mob)
2234  {
2235  ilObjMediaObject::_saveUsage($mob, "qpl:html", $this->original_id);
2236  }
2237  }
2238 
2242  function createPageObject()
2243  {
2244  $qpl_id = $this->getObjId();
2245 
2246  include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
2247  $this->page = new ilAssQuestionPage(0);
2248  $this->page->setId($this->getId());
2249  $this->page->setParentId($qpl_id);
2250  $this->page->setXMLContent("<PageObject><PageContent>".
2251  "<Question QRef=\"il__qst_".$this->getId()."\"/>".
2252  "</PageContent></PageObject>");
2253  $this->page->create();
2254  }
2255 
2256  function copyPageOfQuestion($a_q_id)
2257  {
2258  if ($a_q_id > 0)
2259  {
2260  include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
2261  $page = new ilAssQuestionPage($a_q_id);
2262 
2263  $xml = str_replace("il__qst_".$a_q_id, "il__qst_".$this->id, $page->getXMLContent());
2264  $this->page->setXMLContent($xml);
2265  $this->page->updateFromXML();
2266  }
2267  }
2268 
2270  {
2271  include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
2272  $page = new ilAssQuestionPage($this->id);
2273  return $page->getXMLContent();
2274  }
2275 
2283  function _getQuestionType($question_id)
2284  {
2285  global $ilDB;
2286 
2287  if ($question_id < 1) return "";
2288  $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",
2289  array('integer'),
2290  array($question_id)
2291  );
2292  if ($result->numRows() == 1)
2293  {
2294  $data = $ilDB->fetchAssoc($result);
2295  return $data["type_tag"];
2296  }
2297  else
2298  {
2299  return "";
2300  }
2301  }
2302 
2310  function _getQuestionTitle($question_id)
2311  {
2312  global $ilDB;
2313 
2314  if ($question_id < 1) return "";
2315 
2316  $result = $ilDB->queryF("SELECT title FROM qpl_questions WHERE qpl_questions.question_id = %s",
2317  array('integer'),
2318  array($question_id)
2319  );
2320  if ($result->numRows() == 1)
2321  {
2322  $data = $ilDB->fetchAssoc($result);
2323  return $data["title"];
2324  }
2325  else
2326  {
2327  return "";
2328  }
2329  }
2330 
2332  {
2333  $this->original_id = $original_id;
2334  }
2335 
2336  function getOriginalId()
2337  {
2338  return $this->original_id;
2339  }
2340 
2341  protected static $imageSourceFixReplaceMap = array(
2342  'ok.svg' => 'ok.png', 'not_ok.svg' => 'not_ok.png',
2343  'checkbox_checked.svg' => 'checkbox_checked.png',
2344  'checkbox_unchecked.svg' => 'checkbox_unchecked.png',
2345  'radiobutton_checked.svg' => 'radiobutton_checked.png',
2346  'radiobutton_unchecked.svg' => 'radiobutton_unchecked.png'
2347  );
2348 
2349  public function fixSvgToPng($imageFilenameContainingString)
2350  {
2351  $needles = array_keys(self::$imageSourceFixReplaceMap);
2352  $replacements = array_values(self::$imageSourceFixReplaceMap);
2353  return str_replace($needles, $replacements, $imageFilenameContainingString);
2354  }
2355 
2356 
2358  {
2359  $matches = null;
2360  if( preg_match_all('/src="(.*?)"/m', $html, $matches) )
2361  {
2362  $sources = $matches[1];
2363 
2364  $needleReplacementMap = array();
2365 
2366  foreach($sources as $src)
2367  {
2368  $file = ilUtil::removeTrailingPathSeparators( ILIAS_ABSOLUTE_PATH ) . DIRECTORY_SEPARATOR . $src;
2369 
2370  if( file_exists($file) )
2371  {
2372  continue;
2373  }
2374 
2375  $levels = explode(DIRECTORY_SEPARATOR, $src);
2376  if( count($levels) < 5 || $levels[0] != 'Customizing' || $levels[2] != 'skin' )
2377  {
2378  continue;
2379  }
2380 
2381  $component = '';
2382 
2383  if( $levels[4] == 'Modules' || $levels[4] == 'Services' )
2384  {
2385  $component = $levels[4] . DIRECTORY_SEPARATOR . $levels[5];
2386  }
2387 
2388  $needleReplacementMap[$src] = ilUtil::getImagePath(basename($src), $component);
2389  }
2390 
2391  if( count($needleReplacementMap) )
2392  {
2393  $html = str_replace(array_keys($needleReplacementMap), array_values($needleReplacementMap), $html);
2394  }
2395  }
2396 
2397  return $html;
2398  }
2399 
2406  function loadFromDb($question_id)
2407  {
2408  global $ilDB;
2409 
2410  $result = $ilDB->queryF(
2411  "SELECT external_id FROM qpl_questions WHERE question_id = %s",
2412  array("integer"),
2413  array($question_id)
2414  );
2415  if($result->numRows() == 1)
2416  {
2417  $data = $ilDB->fetchAssoc($result);
2418  $this->external_id = $data['external_id'];
2419  }
2420 
2421  $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s",
2422  array('integer'),
2423  array($this->getId())
2424  );
2425  $this->suggested_solutions = array();
2426  if ($result->numRows())
2427  {
2428  include_once("./Services/RTE/classes/class.ilRTE.php");
2429  while ($row = $ilDB->fetchAssoc($result))
2430  {
2431  $value = (is_array(unserialize($row["value"]))) ? unserialize($row["value"]) : ilRTE::_replaceMediaObjectImageSrc($row["value"], 1);
2432  $this->suggested_solutions[$row["subquestion_index"]] = array(
2433  "type" => $row["type"],
2434  "value" => $value,
2435  "internal_link" => $row["internal_link"],
2436  "import_id" => $row["import_id"]
2437  );
2438  }
2439  }
2440  }
2441 
2448  public function createNewQuestion($a_create_page = true)
2449  {
2450  global $ilDB, $ilUser;
2451 
2452  $complete = "0";
2453  $estw_time = $this->getEstimatedWorkingTime();
2454  $estw_time = sprintf("%02d:%02d:%02d", $estw_time['h'], $estw_time['m'], $estw_time['s']);
2455  $obj_id = ($this->getObjId() <= 0) ? (ilObject::_lookupObjId((strlen($_GET["ref_id"])) ? $_GET["ref_id"] : $_POST["sel_qpl"])) : $this->getObjId();
2456  if ($obj_id > 0)
2457  {
2458  if($a_create_page)
2459  {
2460  $tstamp = 0;
2461  }
2462  else
2463  {
2464  // question pool must not try to purge
2465  $tstamp = time();
2466  }
2467 
2468  $next_id = $ilDB->nextId('qpl_questions');
2469  $affectedRows = $ilDB->insert("qpl_questions", array(
2470  "question_id" => array("integer", $next_id),
2471  "question_type_fi" => array("integer", $this->getQuestionTypeID()),
2472  "obj_fi" => array("integer", $obj_id),
2473  "title" => array("text", NULL),
2474  "description" => array("text", NULL),
2475  "author" => array("text", $this->getAuthor()),
2476  "owner" => array("integer", $ilUser->getId()),
2477  "question_text" => array("clob", NULL),
2478  "points" => array("float", 0),
2479  "nr_of_tries" => array("integer", $this->getDefaultNrOfTries()), // #10771
2480  "working_time" => array("text", $estw_time),
2481  "complete" => array("text", $complete),
2482  "created" => array("integer", time()),
2483  "original_id" => array("integer", NULL),
2484  "tstamp" => array("integer", $tstamp),
2485  "external_id" => array("text", $this->getExternalId()),
2486  'add_cont_edit_mode' => array('text', $this->getAdditionalContentEditingMode())
2487  ));
2488  $this->setId($next_id);
2489 
2490  if($a_create_page)
2491  {
2492  // create page object of question
2493  $this->createPageObject();
2494  }
2495  }
2496 
2497  $this->notifyQuestionCreated();
2498 
2499  return $this->getId();
2500  }
2501 
2502  public function saveQuestionDataToDb($original_id = "")
2503  {
2504  global $ilDB;
2505 
2506  $estw_time = $this->getEstimatedWorkingTime();
2507  $estw_time = sprintf("%02d:%02d:%02d", $estw_time['h'], $estw_time['m'], $estw_time['s']);
2508 
2509  // cleanup RTE images which are not inserted into the question text
2510  include_once("./Services/RTE/classes/class.ilRTE.php");
2511  if ($this->getId() == -1)
2512  {
2513  // Neuen Datensatz schreiben
2514  $next_id = $ilDB->nextId('qpl_questions');
2515  $affectedRows = $ilDB->insert("qpl_questions", array(
2516  "question_id" => array("integer", $next_id),
2517  "question_type_fi" => array("integer", $this->getQuestionTypeID()),
2518  "obj_fi" => array("integer", $this->getObjId()),
2519  "title" => array("text", $this->getTitle()),
2520  "description" => array("text", $this->getComment()),
2521  "author" => array("text", $this->getAuthor()),
2522  "owner" => array("integer", $this->getOwner()),
2523  "question_text" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getQuestion(), 0)),
2524  "points" => array("float", $this->getMaximumPoints()),
2525  "working_time" => array("text", $estw_time),
2526  "nr_of_tries" => array("integer", $this->getNrOfTries()),
2527  "created" => array("integer", time()),
2528  "original_id" => array("integer", ($original_id) ? $original_id : NULL),
2529  "tstamp" => array("integer", time()),
2530  "external_id" => array("text", $this->getExternalId()),
2531  'add_cont_edit_mode' => array('text', $this->getAdditionalContentEditingMode())
2532  ));
2533  $this->setId($next_id);
2534  // create page object of question
2535  $this->createPageObject();
2536  }
2537  else
2538  {
2539  // Vorhandenen Datensatz aktualisieren
2540  $affectedRows = $ilDB->update("qpl_questions", array(
2541  "obj_fi" => array("integer", $this->getObjId()),
2542  "title" => array("text", $this->getTitle()),
2543  "description" => array("text", $this->getComment()),
2544  "author" => array("text", $this->getAuthor()),
2545  "question_text" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getQuestion(), 0)),
2546  "points" => array("float", $this->getMaximumPoints()),
2547  "nr_of_tries" => array("integer", $this->getNrOfTries()),
2548  "working_time" => array("text", $estw_time),
2549  "tstamp" => array("integer", time()),
2550  'complete' => array('integer', $this->isComplete()),
2551  "external_id" => array("text", $this->getExternalId())
2552  ), array(
2553  "question_id" => array("integer", $this->getId())
2554  ));
2555  }
2556  }
2557 
2564  function saveToDb($original_id = "")
2565  {
2566  global $ilDB;
2567 
2568  $this->updateSuggestedSolutions();
2569 
2570  // remove unused media objects from ILIAS
2571  $this->cleanupMediaObjectUsage();
2572 
2573  $complete = "0";
2574  if ($this->isComplete())
2575  {
2576  $complete = "1";
2577  }
2578 
2579  // update the question time stamp and completion status
2580  $affectedRows = $ilDB->manipulateF("UPDATE qpl_questions SET tstamp = %s, owner = %s, complete = %s WHERE question_id = %s",
2581  array('integer','integer', 'integer','text'),
2582  array(time(), ($this->getOwner() <= 0) ? $this->ilias->account->id : $this->getOwner(), $complete, $this->getId())
2583  );
2584 
2585  // update question count of question pool
2586  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
2588 
2589  $this->notifyQuestionEdited($this);
2590  }
2591 
2592  public function setNewOriginalId($newId) {
2593  global $ilDB;
2594  $ilDB->manipulateF("UPDATE qpl_questions SET tstamp = %s, original_id = %s WHERE question_id = %s",
2595  array('integer','integer', 'text'),
2596  array(time(), $newId, $this->getId())
2597  );
2598  }
2599 
2603  protected function onDuplicate($originalParentId, $originalQuestionId, $duplicateParentId, $duplicateQuestionId)
2604  {
2605  $this->duplicateSuggestedSolutionFiles($originalParentId, $originalQuestionId);
2606 
2607  // duplicate question feeback
2608  $this->feedbackOBJ->duplicateFeedback($originalQuestionId, $duplicateQuestionId);
2609 
2610  // duplicate question hints
2611  $this->duplicateQuestionHints($originalQuestionId, $duplicateQuestionId);
2612 
2613  // duplicate skill assignments
2614  $this->duplicateSkillAssignments($originalParentId, $originalQuestionId, $duplicateParentId, $duplicateQuestionId);
2615  }
2616 
2617  protected function beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
2618  {
2619 
2620  }
2621 
2622  protected function afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
2623  {
2624  // sync question feeback
2625  $this->feedbackOBJ->syncFeedback($origQuestionId, $dupQuestionId);
2626  }
2627 
2631  protected function onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $targetQuestionId)
2632  {
2633  $this->copySuggestedSolutionFiles($sourceParentId, $sourceQuestionId);
2634 
2635  // duplicate question feeback
2636  $this->feedbackOBJ->duplicateFeedback($sourceQuestionId, $targetQuestionId);
2637 
2638  // duplicate question hints
2639  $this->duplicateQuestionHints($sourceQuestionId, $targetQuestionId);
2640 
2641  // duplicate skill assignments
2642  $this->duplicateSkillAssignments($sourceParentId, $sourceQuestionId, $targetParentId, $targetQuestionId);
2643  }
2644 
2648  public function deleteSuggestedSolutions()
2649  {
2650  global $ilDB;
2651  // delete the links in the qpl_sol_sug table
2652  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s",
2653  array('integer'),
2654  array($this->getId())
2655  );
2656  // delete the links in the int_link table
2657  include_once "./Services/Link/classes/class.ilInternalLink.php";
2659  $this->suggested_solutions = array();
2661  }
2662 
2670  function getSuggestedSolution($subquestion_index = 0)
2671  {
2672  if (array_key_exists($subquestion_index, $this->suggested_solutions))
2673  {
2674  return $this->suggested_solutions[$subquestion_index];
2675  }
2676  else
2677  {
2678  return array();
2679  }
2680  }
2681 
2690  function getSuggestedSolutionTitle($subquestion_index = 0)
2691  {
2692  if (array_key_exists($subquestion_index, $this->suggested_solutions))
2693  {
2694  $title = $this->suggested_solutions[$subquestion_index]["internal_link"];
2695  // TO DO: resolve internal link an get link type and title
2696  }
2697  else
2698  {
2699  $title = "";
2700  }
2701  return $title;
2702  }
2703 
2713  function setSuggestedSolution($solution_id = "", $subquestion_index = 0, $is_import = false)
2714  {
2715  if (strcmp($solution_id, "") != 0)
2716  {
2717  $import_id = "";
2718  if ($is_import)
2719  {
2720  $import_id = $solution_id;
2721  $solution_id = $this->_resolveInternalLink($import_id);
2722  }
2723  $this->suggested_solutions[$subquestion_index] = array(
2724  "internal_link" => $solution_id,
2725  "import_id" => $import_id
2726  );
2727  }
2728  }
2729 
2733  protected function duplicateSuggestedSolutionFiles($parent_id, $question_id)
2734  {
2735  global $ilLog;
2736 
2737  foreach ($this->suggested_solutions as $index => $solution)
2738  {
2739  if (strcmp($solution["type"], "file") == 0)
2740  {
2741  $filepath = $this->getSuggestedSolutionPath();
2742  $filepath_original = str_replace(
2743  "/{$this->obj_id}/{$this->id}/solution",
2744  "/$parent_id/$question_id/solution",
2745  $filepath
2746  );
2747  if (!file_exists($filepath))
2748  {
2749  ilUtil::makeDirParents($filepath);
2750  }
2751  $filename = $solution["value"]["name"];
2752  if (strlen($filename))
2753  {
2754  if (!copy($filepath_original . $filename, $filepath . $filename))
2755  {
2756  $ilLog->write("File could not be duplicated!!!!", $ilLog->ERROR);
2757  $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
2758  }
2759  }
2760  }
2761  }
2762  }
2763 
2768  {
2769  global $ilLog;
2770 
2771  $filepath = $this->getSuggestedSolutionPath();
2772  $filepath_original = str_replace("/$this->id/solution", "/$original_id/solution", $filepath);
2773  ilUtil::delDir($filepath_original);
2774  foreach ($this->suggested_solutions as $index => $solution)
2775  {
2776  if (strcmp($solution["type"], "file") == 0)
2777  {
2778  if (!file_exists($filepath_original))
2779  {
2780  ilUtil::makeDirParents($filepath_original);
2781  }
2782  $filename = $solution["value"]["name"];
2783  if (strlen($filename))
2784  {
2785  if (!@copy($filepath . $filename, $filepath_original . $filename))
2786  {
2787  $ilLog->write("File could not be duplicated!!!!", $ilLog->ERROR);
2788  $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
2789  }
2790  }
2791  }
2792  }
2793  }
2794 
2795  protected function copySuggestedSolutionFiles($source_questionpool_id, $source_question_id)
2796  {
2797  global $ilLog;
2798 
2799  foreach ($this->suggested_solutions as $index => $solution)
2800  {
2801  if (strcmp($solution["type"], "file") == 0)
2802  {
2803  $filepath = $this->getSuggestedSolutionPath();
2804  $filepath_original = str_replace("/$this->obj_id/$this->id/solution", "/$source_questionpool_id/$source_question_id/solution", $filepath);
2805  if (!file_exists($filepath))
2806  {
2807  ilUtil::makeDirParents($filepath);
2808  }
2809  $filename = $solution["value"]["name"];
2810  if (strlen($filename))
2811  {
2812  if (!copy($filepath_original . $filename, $filepath . $filename))
2813  {
2814  $ilLog->write("File could not be copied!!!!", $ilLog->ERROR);
2815  $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
2816  }
2817  }
2818  }
2819  }
2820  }
2821 
2825  public function updateSuggestedSolutions($original_id = "")
2826  {
2827  global $ilDB;
2828 
2829  $id = (strlen($original_id) && is_numeric($original_id)) ? $original_id : $this->getId();
2830  include_once "./Services/Link/classes/class.ilInternalLink.php";
2831  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s",
2832  array('integer'),
2833  array($id)
2834  );
2836  include_once("./Services/RTE/classes/class.ilRTE.php");
2837  foreach ($this->suggested_solutions as $index => $solution)
2838  {
2839  $next_id = $ilDB->nextId('qpl_sol_sug');
2841  $ilDB->insert('qpl_sol_sug', array(
2842  'suggested_solution_id' => array( 'integer', $next_id ),
2843  'question_fi' => array( 'integer', $id ),
2844  'type' => array( 'text', $solution['type'] ),
2845  'value' => array( 'clob', ilRTE::_replaceMediaObjectImageSrc( (is_array( $solution['value'] ) ) ? serialize( $solution[ 'value' ] ) : $solution['value'], 0 ) ),
2846  'internal_link' => array( 'text', $solution['internal_link'] ),
2847  'import_id' => array( 'text', null ),
2848  'subquestion_index' => array( 'integer', $index ),
2849  'tstamp' => array( 'integer', time() ),
2850  )
2851  );
2852  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
2853  {
2854  ilInternalLink::_saveLink("qst", $id, $matches[2], $matches[3], $matches[1]);
2855  }
2856  }
2857  if (strlen($original_id) && is_numeric($original_id)) $this->syncSuggestedSolutionFiles($id);
2858  $this->cleanupMediaObjectUsage();
2859  }
2860 
2870  function saveSuggestedSolution($type, $solution_id = "", $subquestion_index = 0, $value = "")
2871  {
2872  global $ilDB;
2873 
2874  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s AND subquestion_index = %s",
2875  array("integer", "integer"),
2876  array(
2877  $this->getId(),
2878  $subquestion_index
2879  )
2880  );
2881 
2882  $next_id = $ilDB->nextId('qpl_sol_sug');
2883  include_once("./Services/RTE/classes/class.ilRTE.php");
2885  $affectedRows = $ilDB->insert('qpl_sol_sug', array(
2886  'suggested_solution_id' => array( 'integer', $next_id ),
2887  'question_fi' => array( 'integer', $this->getId() ),
2888  'type' => array( 'text', $type ),
2889  'value' => array( 'clob', ilRTE::_replaceMediaObjectImageSrc( (is_array( $value ) ) ? serialize( $value ) : $value, 0 ) ),
2890  'internal_link' => array( 'text', $solution_id ),
2891  'import_id' => array( 'text', null ),
2892  'subquestion_index' => array( 'integer', $subquestion_index ),
2893  'tstamp' => array( 'integer', time() ),
2894  )
2895  );
2896  if ($affectedRows == 1)
2897  {
2898  $this->suggested_solutions[$subquestion_index] = array(
2899  "type" => $type,
2900  "value" => $value,
2901  "internal_link" => $solution_id,
2902  "import_id" => ""
2903  );
2904  }
2905  $this->cleanupMediaObjectUsage();
2906  }
2907 
2908  function _resolveInternalLink($internal_link)
2909  {
2910  if (preg_match("/il_(\d+)_(\w+)_(\d+)/", $internal_link, $matches))
2911  {
2912  include_once "./Services/Link/classes/class.ilInternalLink.php";
2913  include_once "./Modules/LearningModule/classes/class.ilLMObject.php";
2914  include_once "./Modules/Glossary/classes/class.ilGlossaryTerm.php";
2915  switch ($matches[2])
2916  {
2917  case "lm":
2918  $resolved_link = ilLMObject::_getIdForImportId($internal_link);
2919  break;
2920  case "pg":
2921  $resolved_link = ilInternalLink::_getIdForImportId("PageObject", $internal_link);
2922  break;
2923  case "st":
2924  $resolved_link = ilInternalLink::_getIdForImportId("StructureObject", $internal_link);
2925  break;
2926  case "git":
2927  $resolved_link = ilInternalLink::_getIdForImportId("GlossaryItem", $internal_link);
2928  break;
2929  case "mob":
2930  $resolved_link = ilInternalLink::_getIdForImportId("MediaObject", $internal_link);
2931  break;
2932  }
2933  if (strcmp($resolved_link, "") == 0)
2934  {
2935  $resolved_link = $internal_link;
2936  }
2937  }
2938  else
2939  {
2940  $resolved_link = $internal_link;
2941  }
2942  return $resolved_link;
2943  }
2944 
2945  function _resolveIntLinks($question_id)
2946  {
2947  global $ilDB;
2948  $resolvedlinks = 0;
2949  $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s",
2950  array('integer'),
2951  array($question_id)
2952  );
2953  if ($result->numRows())
2954  {
2955  while ($row = $ilDB->fetchAssoc($result))
2956  {
2957  $internal_link = $row["internal_link"];
2958  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
2959  $resolved_link = assQuestion::_resolveInternalLink($internal_link);
2960  if (strcmp($internal_link, $resolved_link) != 0)
2961  {
2962  // internal link was resolved successfully
2963  $affectedRows = $ilDB->manipulateF("UPDATE qpl_sol_sug SET internal_link = %s WHERE suggested_solution_id = %s",
2964  array('text','integer'),
2965  array($resolved_link, $row["suggested_solution_id"])
2966  );
2967  $resolvedlinks++;
2968  }
2969  }
2970  }
2971  if ($resolvedlinks)
2972  {
2973  // there are resolved links -> reenter theses links to the database
2974 
2975  // delete all internal links from the database
2976  include_once "./Services/Link/classes/class.ilInternalLink.php";
2977  ilInternalLink::_deleteAllLinksOfSource("qst", $question_id);
2978 
2979  $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s",
2980  array('integer'),
2981  array($question_id)
2982  );
2983  if ($result->numRows())
2984  {
2985  while ($row = $ilDB->fetchAssoc($result))
2986  {
2987  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $row["internal_link"], $matches))
2988  {
2989  ilInternalLink::_saveLink("qst", $question_id, $matches[2], $matches[3], $matches[1]);
2990  }
2991  }
2992  }
2993  }
2994  }
2995 
2996  function _getInternalLinkHref($target = "")
2997  {
2998  global $ilDB;
2999  $linktypes = array(
3000  "lm" => "LearningModule",
3001  "pg" => "PageObject",
3002  "st" => "StructureObject",
3003  "git" => "GlossaryItem",
3004  "mob" => "MediaObject"
3005  );
3006  $href = "";
3007  if (preg_match("/il__(\w+)_(\d+)/", $target, $matches))
3008  {
3009  $type = $matches[1];
3010  $target_id = $matches[2];
3011  include_once "./Services/Utilities/classes/class.ilUtil.php";
3012  switch($linktypes[$matches[1]])
3013  {
3014  case "LearningModule":
3015  $href = "./goto.php?target=" . $type . "_" . $target_id;
3016  break;
3017  case "PageObject":
3018  case "StructureObject":
3019  $href = "./goto.php?target=" . $type . "_" . $target_id;
3020  break;
3021  case "GlossaryItem":
3022  $href = "./goto.php?target=" . $type . "_" . $target_id;
3023  break;
3024  case "MediaObject":
3025  $href = "./ilias.php?baseClass=ilLMPresentationGUI&obj_type=" . $linktypes[$type] . "&cmd=media&ref_id=".$_GET["ref_id"]."&mob_id=".$target_id;
3026  break;
3027  }
3028  }
3029  return $href;
3030  }
3031 
3039  public static function _getOriginalId($question_id)
3040  {
3041  global $ilDB;
3042  $result = $ilDB->queryF("SELECT * FROM qpl_questions WHERE question_id = %s",
3043  array('integer'),
3044  array($question_id)
3045  );
3046  if ($result->numRows() > 0)
3047  {
3048  $row = $ilDB->fetchAssoc($result);
3049  if ($row["original_id"] > 0)
3050  {
3051  return $row["original_id"];
3052  }
3053  else
3054  {
3055  return $row["question_id"];
3056  }
3057  }
3058  else
3059  {
3060  return "";
3061  }
3062  }
3063 
3064  public static function originalQuestionExists($questionId)
3065  {
3066  global $ilDB;
3067 
3068  $query = "
3069  SELECT COUNT(dupl.question_id) cnt
3070  FROM qpl_questions dupl
3071  INNER JOIN qpl_questions orig
3072  ON orig.question_id = dupl.original_id
3073  WHERE dupl.question_id = %s
3074  ";
3075 
3076  $res = $ilDB->queryF($query, array('integer'), array($questionId));
3077  $row = $ilDB->fetchAssoc($res);
3078 
3079  return $row['cnt'] > 0;
3080  }
3081 
3082  function syncWithOriginal()
3083  {
3084  global $ilDB;
3085 
3086  if( !$this->getOriginalId() )
3087  {
3088  return;
3089  }
3090 
3091  $originalObjId = self::lookupOriginalParentObjId($this->getOriginalId());
3092 
3093  if ( !$originalObjId )
3094  {
3095  return;
3096  }
3097 
3098  $id = $this->getId();
3099  $objId = $this->getObjId();
3100  $original = $this->getOriginalId();
3101 
3102  $this->beforeSyncWithOriginal($original, $id, $originalObjId, $objId);
3103 
3104  $this->setId($original);
3105  $this->setOriginalId(NULL);
3106  $this->setObjId($originalObjId);
3107 
3108  $this->saveToDb();
3109 
3110  $this->deletePageOfQuestion($original);
3111  $this->createPageObject();
3112  $this->copyPageOfQuestion($id);
3113 
3114  $this->setId($id);
3115  $this->setOriginalId($original);
3116  $this->setObjId($objId);
3117 
3118  $this->updateSuggestedSolutions($original);
3120 
3121  $this->afterSyncWithOriginal($original, $id, $originalObjId, $objId);
3122  $this->syncHints();
3123  }
3124 
3125  function createRandomSolution($test_id, $user_id)
3126  {
3127  }
3128 
3136  function _questionExists($question_id)
3137  {
3138  global $ilDB;
3139 
3140  if ($question_id < 1)
3141  {
3142  return false;
3143  }
3144 
3145  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE question_id = %s",
3146  array('integer'),
3147  array($question_id)
3148  );
3149  if ($result->numRows() == 1)
3150  {
3151  return true;
3152  }
3153  else
3154  {
3155  return false;
3156  }
3157  }
3158 
3166  function _questionExistsInPool($question_id)
3167  {
3168  global $ilDB;
3169 
3170  if ($question_id < 1)
3171  {
3172  return false;
3173  }
3174 
3175  $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'",
3176  array('integer'),
3177  array($question_id)
3178  );
3179  if ($result->numRows() == 1)
3180  {
3181  return true;
3182  }
3183  else
3184  {
3185  return false;
3186  }
3187  }
3188 
3196  public static function _instanciateQuestion($question_id)
3197  {
3198  return self::_instantiateQuestion($question_id);
3199  }
3200 
3205  public static function _instantiateQuestion($question_id)
3206  {
3207  global $ilCtrl, $ilDB, $lng;
3208 
3209  if (strcmp($question_id, "") != 0)
3210  {
3211  $question_type = assQuestion::_getQuestionType($question_id);
3212  if (!strlen($question_type)) return null;
3213  assQuestion::_includeClass($question_type);
3214  $objectClassname = self::getObjectClassNameByQuestionType($question_type);
3215  $question = new $objectClassname();
3216  $question->loadFromDb($question_id);
3217 
3218  $feedbackObjectClassname = self::getFeedbackClassNameByQuestionType($question_type);
3219  $question->feedbackOBJ = new $feedbackObjectClassname($question, $ilCtrl, $ilDB, $lng);
3220 
3221  return $question;
3222  }
3223  }
3224 
3231  function getPoints()
3232  {
3233  if (strcmp($this->points, "") == 0)
3234  {
3235  return 0;
3236  }
3237  else
3238  {
3239  return $this->points;
3240  }
3241  }
3242 
3243 
3250  function setPoints($a_points)
3251  {
3252  $this->points = $a_points;
3253  }
3254 
3261  function getSolutionMaxPass($active_id)
3262  {
3263  return $this->_getSolutionMaxPass($this->getId(), $active_id);
3264  }
3265 
3272  function _getSolutionMaxPass($question_id, $active_id)
3273  {
3274 /* include_once "./Modules/Test/classes/class.ilObjTest.php";
3275  $pass = ilObjTest::_getPass($active_id);
3276  return $pass;*/
3277 
3278  // the following code was the old solution which added the non answered
3279  // questions of a pass from the answered questions of the previous pass
3280  // with the above solution, only the answered questions of the last pass are counted
3281  global $ilDB;
3282 
3283  $result = $ilDB->queryF("SELECT MAX(pass) maxpass FROM tst_test_result WHERE active_fi = %s AND question_fi = %s",
3284  array('integer','integer'),
3285  array($active_id, $question_id)
3286  );
3287  if ($result->numRows() == 1)
3288  {
3289  $row = $ilDB->fetchAssoc($result);
3290  return $row["maxpass"];
3291  }
3292  else
3293  {
3294  return 0;
3295  }
3296  }
3297 
3306  function _isWriteable($question_id, $user_id)
3307  {
3308  global $ilDB;
3309 
3310  if (($question_id < 1) || ($user_id < 1))
3311  {
3312  return false;
3313  }
3314 
3315  $result = $ilDB->queryF("SELECT obj_fi FROM qpl_questions WHERE question_id = %s",
3316  array('integer'),
3317  array($question_id)
3318  );
3319  if ($result->numRows() == 1)
3320  {
3321  $row = $ilDB->fetchAssoc($result);
3322  $qpl_object_id = $row["obj_fi"];
3323  include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
3324  return ilObjQuestionPool::_isWriteable($qpl_object_id, $user_id);
3325  }
3326  else
3327  {
3328  return false;
3329  }
3330  }
3331 
3338  function _isUsedInRandomTest($question_id = "")
3339  {
3340  global $ilDB;
3341 
3342  if ($question_id < 1) return 0;
3343  $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE question_fi = %s",
3344  array('integer'),
3345  array($question_id)
3346  );
3347  return $result->numRows();
3348  }
3349 
3361  abstract public function calculateReachedPoints($active_id, $pass = NULL, $authorizedSolution = true, $returndetails = FALSE);
3362 
3364  {
3365  return $this->calculateReachedPointsForSolution($previewSession->getParticipantsSolution());
3366  }
3367 
3369  {
3370  $reachedPoints = $this->calculateReachedPointsFromPreviewSession($previewSession);
3371 
3372  if( $reachedPoints < $this->getMaximumPoints() )
3373  {
3374  return false;
3375  }
3376 
3377  return true;
3378  }
3379 
3380 
3391  final public function adjustReachedPointsByScoringOptions($points, $active_id, $pass = NULL)
3392  {
3393  include_once "./Modules/Test/classes/class.ilObjTest.php";
3394  $count_system = ilObjTest::_getCountSystem($active_id);
3395  if ($count_system == 1)
3396  {
3397  if (abs($this->getMaximumPoints() - $points) > 0.0000000001)
3398  {
3399  $points = 0;
3400  }
3401  }
3402  $score_cutting = ilObjTest::_getScoreCutting($active_id);
3403  if ($score_cutting == 0)
3404  {
3405  if ($points < 0)
3406  {
3407  $points = 0;
3408  }
3409  }
3410  return $points;
3411  }
3412 
3421  public static function _isWorkedThrough($active_id, $question_id, $pass = NULL)
3422  {
3423  return self::lookupResultRecordExist($active_id, $question_id, $pass);
3424 
3425  // oldschool "workedthru"
3426 
3427  global $ilDB;
3428 
3429  $points = 0;
3430  if (is_null($pass))
3431  {
3432  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
3433  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
3434  }
3435  $result = $ilDB->queryF("SELECT solution_id FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
3436  array('integer','integer','integer'),
3437  array($active_id, $question_id, $pass)
3438  );
3439  if ($result->numRows())
3440  {
3441  return TRUE;
3442  }
3443  else
3444  {
3445  return FALSE;
3446  }
3447  }
3448 
3456  public static function _areAnswered($a_user_id,$a_question_ids)
3457  {
3458  global $ilDB;
3459 
3460  $res = $ilDB->queryF("SELECT DISTINCT(question_fi) FROM tst_test_result JOIN tst_active ".
3461  "ON (active_id = active_fi) ".
3462  "WHERE " . $ilDB->in('question_fi', $a_question_ids, false, 'integer') .
3463  " AND user_fi = %s",
3464  array('integer'),
3465  array($a_user_id)
3466  );
3467  return ($res->numRows() == count($a_question_ids)) ? true : false;
3468  }
3469 
3478  function isHTML($a_text)
3479  {
3480  return ilUtil::isHTML($a_text);
3481  }
3482 
3489  function prepareTextareaOutput($txt_output, $prepare_for_latex_output = FALSE, $omitNl2BrWhenTextArea = false)
3490  {
3491  include_once "./Services/Utilities/classes/class.ilUtil.php";
3492  return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output, $omitNl2BrWhenTextArea);
3493  }
3494 
3502  function QTIMaterialToString($a_material)
3503  {
3504  $result = "";
3505  for ($i = 0; $i < $a_material->getMaterialCount(); $i++)
3506  {
3507  $material = $a_material->getMaterial($i);
3508  if (strcmp($material["type"], "mattext") == 0)
3509  {
3510  $result .= $material["material"]->getContent();
3511  }
3512  if (strcmp($material["type"], "matimage") == 0)
3513  {
3514  $matimage = $material["material"];
3515  if (preg_match("/(il_([0-9]+)_mob_([0-9]+))/", $matimage->getLabel(), $matches))
3516  {
3517  // import an mediaobject which was inserted using tiny mce
3518  if (!is_array($_SESSION["import_mob_xhtml"])) $_SESSION["import_mob_xhtml"] = array();
3519  array_push($_SESSION["import_mob_xhtml"], array("mob" => $matimage->getLabel(), "uri" => $matimage->getUri()));
3520  }
3521  }
3522  }
3523  return $result;
3524  }
3525 
3534  function addQTIMaterial(&$a_xml_writer, $a_material, $close_material_tag = TRUE, $add_mobs = TRUE)
3535  {
3536  include_once "./Services/RTE/classes/class.ilRTE.php";
3537  include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
3538 
3539  $a_xml_writer->xmlStartTag("material");
3540  $attrs = array(
3541  "texttype" => "text/plain"
3542  );
3543  if ($this->isHTML($a_material))
3544  {
3545  $attrs["texttype"] = "text/xhtml";
3546  }
3547  $a_xml_writer->xmlElement("mattext", $attrs, ilRTE::_replaceMediaObjectImageSrc($a_material, 0));
3548  if ($add_mobs)
3549  {
3550  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
3551  foreach ($mobs as $mob)
3552  {
3553  $moblabel = "il_" . IL_INST_ID . "_mob_" . $mob;
3554  if (strpos($a_material, "mm_$mob") !== FALSE)
3555  {
3556  if (ilObjMediaObject::_exists($mob))
3557  {
3558  $mob_obj =& new ilObjMediaObject($mob);
3559  $imgattrs = array(
3560  "label" => $moblabel,
3561  "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle()
3562  );
3563  }
3564  $a_xml_writer->xmlElement("matimage", $imgattrs, NULL);
3565  }
3566  }
3567  }
3568  if ($close_material_tag) $a_xml_writer->xmlEndTag("material");
3569  }
3570 
3571  function createNewImageFileName($image_filename, $unique = false)
3572  {
3573  $extension = "";
3574 
3575  if (preg_match("/.*\.(png|jpg|gif|jpeg)$/i", $image_filename, $matches))
3576  {
3577  $extension = "." . $matches[1];
3578  }
3579 
3580  if($unique)
3581  {
3582  $image_filename = uniqid($image_filename.microtime(true));
3583  }
3584 
3585  $image_filename = md5($image_filename) . $extension;
3586 
3587  return $image_filename;
3588  }
3589 
3600  function _setReachedPoints($active_id, $question_id, $points, $maxpoints, $pass, $manualscoring, $obligationsEnabled)
3601  {
3602  global $ilDB;
3603 
3604  if ($points <= $maxpoints)
3605  {
3606  if (is_null($pass))
3607  {
3608  $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
3609  }
3610 
3611  // retrieve the already given points
3612  $old_points = 0;
3613  $result = $ilDB->queryF("SELECT points FROM tst_test_result WHERE active_fi = %s AND question_fi = %s AND pass = %s",
3614  array('integer','integer','integer'),
3615  array($active_id, $question_id, $pass)
3616  );
3617  $manual = ($manualscoring) ? 1 : 0;
3618  $rowsnum = $result->numRows();
3619  if($rowsnum)
3620  {
3621  $row = $ilDB->fetchAssoc($result);
3622  $old_points = $row["points"];
3623  if($old_points != $points)
3624  {
3625  $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",
3626  array('float', 'integer', 'integer', 'integer', 'integer', 'integer'),
3627  array($points, $manual, time(), $active_id, $question_id, $pass)
3628  );
3629  }
3630  }
3631  else
3632  {
3633  $next_id = $ilDB->nextId('tst_test_result');
3634  $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)",
3635  array('integer', 'integer','integer', 'float', 'integer', 'integer','integer'),
3636  array($next_id, $active_id, $question_id, $points, $pass, $manual, time())
3637  );
3638  }
3639 
3640  if(self::isForcePassResultUpdateEnabled() || $old_points != $points || !$rowsnum)
3641  {
3642  assQuestion::_updateTestPassResults($active_id, $pass, $obligationsEnabled);
3643  // finally update objective result
3644  include_once "./Modules/Test/classes/class.ilObjTest.php";
3645  include_once './Modules/Course/classes/class.ilCourseObjectiveResult.php';
3647 
3648  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3650  {
3651  global $lng, $ilUser;
3652  include_once "./Modules/Test/classes/class.ilObjTestAccess.php";
3653  $username = ilObjTestAccess::_getParticipantData($active_id);
3654  assQuestion::_logAction(sprintf($lng->txtlng("assessment", "log_answer_changed_points", ilObjAssessmentFolder::_getLogLanguage()), $username, $old_points, $points, $ilUser->getFullname() . " (" . $ilUser->getLogin() . ")"), $active_id, $question_id);
3655  }
3656  }
3657 
3658  return TRUE;
3659  }
3660  else
3661  {
3662  return FALSE;
3663  }
3664  }
3665 
3673  function getQuestion()
3674  {
3675  return $this->question;
3676  }
3677 
3685  function setQuestion($question = "")
3686  {
3687  $this->question = $question;
3688  }
3689 
3695  abstract public function getQuestionType();
3696 
3706  {
3707  global $ilDB;
3708 
3709  $result = $ilDB->queryF("SELECT question_type_id FROM qpl_qst_type WHERE type_tag = %s",
3710  array('text'),
3711  array($this->getQuestionType())
3712  );
3713  if ($result->numRows() == 1)
3714  {
3715  $row = $ilDB->fetchAssoc($result);
3716  return $row["question_type_id"];
3717  }
3718  return 0;
3719  }
3720 
3721  public function syncHints()
3722  {
3723  global $ilDB;
3724 
3725  // delete hints of the original
3726  $ilDB->manipulateF("DELETE FROM qpl_hints WHERE qht_question_fi = %s",
3727  array('integer'),
3728  array($this->original_id)
3729  );
3730 
3731  // get hints of the actual question
3732  $result = $ilDB->queryF("SELECT * FROM qpl_hints WHERE qht_question_fi = %s",
3733  array('integer'),
3734  array($this->getId())
3735  );
3736 
3737  // save hints to the original
3738  if ($result->numRows())
3739  {
3740  while ($row = $ilDB->fetchAssoc($result))
3741  {
3742  $next_id = $ilDB->nextId('qpl_hints');
3744  $ilDB->insert('qpl_hints', array(
3745  'qht_hint_id' => array('integer', $next_id),
3746  'qht_question_fi' => array('integer', $this->original_id),
3747  'qht_hint_index' => array('integer', $row["qht_hint_index"]),
3748  'qht_hint_points' => array('integer', $row["qht_hint_points"]),
3749  'qht_hint_text' => array('text', $row["qht_hint_text"]),
3750  )
3751  );
3752  }
3753  }
3754  }
3755 
3760  protected function getRTETextWithMediaObjects()
3761  {
3762  // must be called in parent classes. add additional RTE text in the parent
3763  // classes and call this method to add the standard RTE text
3764  $collected = $this->getQuestion();
3765  $collected .= $this->feedbackOBJ->getGenericFeedbackContent($this->getId(), false);
3766  $collected .= $this->feedbackOBJ->getGenericFeedbackContent($this->getId(), true);
3767  $collected .= $this->feedbackOBJ->getAllSpecificAnswerFeedbackContents($this->getId());
3768 
3769  foreach ($this->suggested_solutions as $solution_array)
3770  {
3771  $collected .= $solution_array["value"];
3772  }
3773 
3774  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintList.php';
3775  $questionHintList = ilAssQuestionHintList::getListByQuestionId($this->getId());
3776  foreach($questionHintList as $questionHint)
3777  {
3778  /* @var $questionHint ilAssQuestionHint */
3779  $collected .= $questionHint->getText();
3780  }
3781 
3782  return $collected;
3783  }
3784 
3790  {
3791  $combinedtext = $this->getRTETextWithMediaObjects();
3792  include_once("./Services/RTE/classes/class.ilRTE.php");
3793  ilRTE::_cleanupMediaObjectUsage($combinedtext, "qpl:html", $this->getId());
3794  }
3795 
3801  function &getInstances()
3802  {
3803  global $ilDB;
3804 
3805  $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id = %s",
3806  array("integer"),
3807  array($this->getId())
3808  );
3809  $instances = array();
3810  $ids = array();
3811  while ($row = $ilDB->fetchAssoc($result))
3812  {
3813  array_push($ids, $row["question_id"]);
3814  }
3815  foreach ($ids as $question_id)
3816  {
3817  // check non random tests
3818  $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",
3819  array("integer"),
3820  array($question_id)
3821  );
3822  while ($row = $ilDB->fetchAssoc($result))
3823  {
3824  $instances[$row['obj_fi']] = ilObject::_lookupTitle($row['obj_fi']);
3825  }
3826  // check random tests
3827  $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",
3828  array("integer"),
3829  array($question_id)
3830  );
3831  while ($row = $ilDB->fetchAssoc($result))
3832  {
3833  $instances[$row['obj_fi']] = ilObject::_lookupTitle($row['obj_fi']);
3834  }
3835  }
3836  include_once "./Modules/Test/classes/class.ilObjTest.php";
3837  foreach ($instances as $key => $value)
3838  {
3839  $instances[$key] = array("obj_id" => $key, "title" => $value, "author" => ilObjTest::_lookupAuthor($key), "refs" => ilObject::_getAllReferences($key));
3840  }
3841  return $instances;
3842  }
3843 
3844  function _needsManualScoring($question_id)
3845  {
3846  include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
3848  $questiontype = assQuestion::_getQuestionType($question_id);
3849  if (in_array($questiontype, $scoring))
3850  {
3851  return TRUE;
3852  }
3853  else
3854  {
3855  return FALSE;
3856  }
3857  }
3858 
3866  function getActiveUserData($active_id)
3867  {
3868  global $ilDB;
3869  $result = $ilDB->queryF("SELECT * FROM tst_active WHERE active_id = %s",
3870  array('integer'),
3871  array($active_id)
3872  );
3873  if ($result->numRows())
3874  {
3875  $row = $ilDB->fetchAssoc($result);
3876  return array("user_id" => $row["user_fi"], "test_id" => $row["test_fi"]);
3877  }
3878  else
3879  {
3880  return array();
3881  }
3882  }
3883 
3891  static function _includeClass($question_type, $gui = 0)
3892  {
3893  if( self::isCoreQuestionType($question_type) )
3894  {
3895  self::includeCoreClass($question_type, $gui);
3896  }
3897  else
3898  {
3899  self::includePluginClass($question_type, $gui);
3900  }
3901  }
3902 
3903  public static function getGuiClassNameByQuestionType($questionType)
3904  {
3905  return $questionType.'GUI';
3906  }
3907 
3908  public static function getObjectClassNameByQuestionType($questionType)
3909  {
3910  return $questionType;
3911  }
3912 
3913  public static function getFeedbackClassNameByQuestionType($questionType)
3914  {
3915  return str_replace('ass', 'ilAss', $questionType).'Feedback';
3916  }
3917 
3918  public static function isCoreQuestionType($questionType)
3919  {
3920  $guiClassName = self::getGuiClassNameByQuestionType($questionType);
3921  return file_exists("Modules/TestQuestionPool/classes/class.{$guiClassName}.php");
3922  }
3923 
3924  public static function includeCoreClass($questionType, $withGuiClass)
3925  {
3926  if( $withGuiClass )
3927  {
3928  $guiClassName = self::getGuiClassNameByQuestionType($questionType);
3929  require_once "Modules/TestQuestionPool/classes/class.{$guiClassName}.php";
3930 
3931  // object class is included by gui classes constructor
3932  }
3933  else
3934  {
3935  $objectClassName = self::getObjectClassNameByQuestionType($questionType);
3936  require_once "Modules/TestQuestionPool/classes/class.{$objectClassName}.php";
3937  }
3938 
3939  $feedbackClassName = self::getFeedbackClassNameByQuestionType($questionType);
3940  require_once "Modules/TestQuestionPool/classes/feedback/class.{$feedbackClassName}.php";
3941  }
3942 
3943  public static function includePluginClass($questionType, $withGuiClass)
3944  {
3945  global $ilPluginAdmin;
3946 
3947  $classes = array(
3948  self::getObjectClassNameByQuestionType($questionType),
3949  self::getFeedbackClassNameByQuestionType($questionType)
3950  );
3951 
3952  if( $withGuiClass )
3953  {
3954  $classes[] = self::getGuiClassNameByQuestionType($questionType);
3955  }
3956 
3957  $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
3958  foreach ($pl_names as $pl_name)
3959  {
3960  $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
3961  if (strcmp($pl->getQuestionType(), $questionType) == 0)
3962  {
3963  foreach($classes as $class)
3964  {
3965  $pl->includeClass("class.{$class}.php");
3966  }
3967 
3968  break;
3969  }
3970  }
3971  }
3972 
3979  static function _getQuestionTypeName($type_tag)
3980  {
3981  if (file_exists("./Modules/TestQuestionPool/classes/class.".$type_tag.".php"))
3982  {
3983  global $lng;
3984  return $lng->txt($type_tag);
3985  }
3986  else
3987  {
3988  global $ilPluginAdmin;
3989  $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
3990  foreach ($pl_names as $pl_name)
3991  {
3992  $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
3993  if (strcmp($pl->getQuestionType(), $type_tag) == 0)
3994  {
3995  return $pl->getQuestionTypeTranslation();
3996  }
3997  }
3998  }
3999  return "";
4000  }
4001 
4011  public static function &_instanciateQuestionGUI($question_id)
4012  {
4013  return self::instantiateQuestionGUI($question_id);
4014  }
4015 
4023  public static function instantiateQuestionGUI($a_question_id)
4024  {
4025  global $ilCtrl, $ilDB, $lng, $ilUser;
4026 
4027  if (strcmp($a_question_id, "") != 0)
4028  {
4029  $question_type = assQuestion::_getQuestionType($a_question_id);
4030 
4031  assQuestion::_includeClass($question_type, 1);
4032 
4033  $question_type_gui = self::getGuiClassNameByQuestionType($question_type);
4034  $question_gui = new $question_type_gui();
4035  $question_gui->object->loadFromDb($a_question_id);
4036 
4037  $feedbackObjectClassname = self::getFeedbackClassNameByQuestionType($question_type);
4038  $question_gui->object->feedbackOBJ = new $feedbackObjectClassname($question_gui->object, $ilCtrl, $ilDB, $lng);
4039 
4040  $assSettings = new ilSetting('assessment');
4041  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionProcessLockerFactory.php';
4042  $processLockerFactory = new ilAssQuestionProcessLockerFactory($assSettings, $ilDB);
4043  $processLockerFactory->setQuestionId($question_gui->object->getId());
4044  $processLockerFactory->setUserId($ilUser->getId());
4045  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
4046  $processLockerFactory->setAssessmentLogEnabled(ilObjAssessmentFolder::_enabledAssessmentLogging());
4047  $question_gui->object->setProcessLocker($processLockerFactory->getLocker());
4048  }
4049  else
4050  {
4051  global $ilLog;
4052  $ilLog->write('Instantiate question called without question id. (instantiateQuestionGUI@assQuestion)', $ilLog->WARNING);
4053  return null;
4054  }
4055  return $question_gui;
4056  }
4057 
4070  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
4071  {
4072  return $startrow;
4073  }
4074 
4078  public function __get($value)
4079  {
4080  switch ($value)
4081  {
4082  case "id":
4083  return $this->getId();
4084  break;
4085  case "title":
4086  return $this->getTitle();
4087  break;
4088  case "comment":
4089  return $this->getComment();
4090  break;
4091  case "owner":
4092  return $this->getOwner();
4093  break;
4094  case "author":
4095  return $this->getAuthor();
4096  break;
4097  case "question":
4098  return $this->getQuestion();
4099  break;
4100  case "points":
4101  return $this->getPoints();
4102  break;
4103  case "est_working_time":
4104  return $this->getEstimatedWorkingTime();
4105  break;
4106  case "shuffle":
4107  return $this->getShuffle();
4108  break;
4109  case "test_id":
4110  return $this->getTestId();
4111  break;
4112  case "obj_id":
4113  return $this->getObjId();
4114  break;
4115  case "ilias":
4116  return $this->ilias;
4117  break;
4118  case "tpl":
4119  return $this->tpl;
4120  break;
4121  case "page":
4122  return $this->page;
4123  break;
4124  case "outputType":
4125  return $this->getOutputType();
4126  break;
4127  case "suggested_solutions":
4128  return $this->getSuggestedSolutions();
4129  break;
4130  case "original_id":
4131  return $this->getOriginalId();
4132  break;
4133  default:
4134  if (array_key_exists($value, $this->arrData))
4135  {
4136  return $this->arrData[$value];
4137  }
4138  else
4139  {
4140  return null;
4141  }
4142  break;
4143  }
4144  }
4145 
4149  public function __set($key, $value)
4150  {
4151  switch ($key)
4152  {
4153  case "id":
4154  $this->setId($value);
4155  break;
4156  case "title":
4157  $this->setTitle($value);
4158  break;
4159  case "comment":
4160  $this->setComment($value);
4161  break;
4162  case "owner":
4163  $this->setOwner($value);
4164  break;
4165  case "author":
4166  $this->setAuthor($value);
4167  break;
4168  case "question":
4169  $this->setQuestion($value);
4170  break;
4171  case "points":
4172  $this->setPoints($value);
4173  break;
4174  case "est_working_time":
4175  if (is_array($value))
4176  {
4177  $this->setEstimatedWorkingTime($value["h"], $value["m"], $value["s"]);
4178  }
4179  break;
4180  case "shuffle":
4181  $this->setShuffle($value);
4182  break;
4183  case "test_id":
4184  $this->setTestId($value);
4185  break;
4186  case "obj_id":
4187  $this->setObjId($value);
4188  break;
4189  case "outputType":
4190  $this->setOutputType($value);
4191  break;
4192  case "original_id":
4193  $this->setOriginalId($value);
4194  break;
4195  case "page":
4196  $this->page =& $value;
4197  break;
4198  default:
4199  $this->arrData[$key] = $value;
4200  break;
4201  }
4202  }
4203 
4204  public function getNrOfTries()
4205  {
4206  return (int)$this->nr_of_tries;
4207  }
4208 
4209  public function setNrOfTries($a_nr_of_tries)
4210  {
4211  $this->nr_of_tries = $a_nr_of_tries;
4212  }
4213 
4214  public function setExportImagePath($a_path)
4215  {
4216  $this->export_image_path = (string)$a_path;
4217  }
4218 
4219  function _questionExistsInTest($question_id, $test_id)
4220  {
4221  global $ilDB;
4222 
4223  if ($question_id < 1)
4224  {
4225  return false;
4226  }
4227 
4228  $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE question_fi = %s AND test_fi = %s",
4229  array('integer', 'integer'),
4230  array($question_id, $test_id)
4231  );
4232  if ($result->numRows() == 1)
4233  {
4234  return true;
4235  }
4236  else
4237  {
4238  return false;
4239  }
4240  }
4241 
4248  function formatSAQuestion($a_q)
4249  {
4250  return $this->getSelfAssessmentFormatter()->format($a_q);
4251  }
4252 
4253  // scorm2004-start ???
4254 
4260  function setPreventRteUsage($a_val)
4261  {
4262  $this->prevent_rte_usage = $a_val;
4263  }
4264 
4271  {
4272  return $this->prevent_rte_usage;
4273  }
4274 
4279  {
4280  $this->lmMigrateQuestionTypeGenericContent($migrator);
4281  $this->lmMigrateQuestionTypeSpecificContent($migrator);
4282  $this->saveToDb();
4283 
4284  $this->feedbackOBJ->migrateContentForLearningModule($migrator, $this->getId());
4285  }
4286 
4291  {
4292  $this->setQuestion( $migrator->migrateToLmContent( $this->getQuestion() ) );
4293  }
4294 
4299  {
4300  // overwrite if any question type specific content except feedback needs to be migrated
4301  }
4302 
4308  function setSelfAssessmentEditingMode($a_selfassessmenteditingmode)
4309  {
4310  $this->selfassessmenteditingmode = $a_selfassessmenteditingmode;
4311  }
4312 
4319  {
4321  }
4322 
4328  function setDefaultNrOfTries($a_defaultnroftries)
4329  {
4330  $this->defaultnroftries = $a_defaultnroftries;
4331  }
4332 
4339  {
4340  return (int)$this->defaultnroftries;
4341  }
4342 
4343  // scorm2004-end ???
4344 
4350  public static function lookupParentObjId($questionId)
4351  {
4352  global $ilDB;
4353 
4354  $query = "SELECT obj_fi FROM qpl_questions WHERE question_id = %s";
4355 
4356  $res = $ilDB->queryF($query, array('integer'), array((int)$questionId));
4357  $row = $ilDB->fetchAssoc($res);
4358 
4359  return $row['obj_fi'];
4360  }
4361 
4372  public static function lookupOriginalParentObjId($originalQuestionId)
4373  {
4374  return self::lookupParentObjId($originalQuestionId);
4375  }
4376 
4377  protected function duplicateQuestionHints($originalQuestionId, $duplicateQuestionId)
4378  {
4379  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintList.php';
4380  $hintIds = ilAssQuestionHintList::duplicateListForQuestion($originalQuestionId, $duplicateQuestionId);
4381 
4383  {
4384  require_once 'Modules/TestQuestionPool/classes/class.ilAssHintPage.php';
4385 
4386  foreach($hintIds as $originalHintId => $duplicateHintId)
4387  {
4388  $originalPageObject = new ilAssHintPage($originalHintId);
4389  $originalXML = $originalPageObject->getXMLContent();
4390 
4391  $duplicatePageObject = new ilAssHintPage();
4392  $duplicatePageObject->setId($duplicateHintId);
4393  $duplicatePageObject->setParentId($this->getId());
4394  $duplicatePageObject->setXMLContent($originalXML);
4395  $duplicatePageObject->createFromXML();
4396  }
4397  }
4398  }
4399 
4400  protected function duplicateSkillAssignments($srcParentId, $srcQuestionId, $trgParentId, $trgQuestionId)
4401  {
4402  global $ilDB;
4403 
4404  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionSkillAssignmentList.php';
4405  $assignmentList = new ilAssQuestionSkillAssignmentList($ilDB);
4406  $assignmentList->setParentObjId($srcParentId);
4407  $assignmentList->setQuestionIdFilter($srcQuestionId);
4408  $assignmentList->loadFromDb();
4409 
4410  foreach($assignmentList->getAssignmentsByQuestionId($srcQuestionId) as $assignment)
4411  {
4412  /* @var ilAssQuestionSkillAssignment $assignment */
4413 
4414  $assignment->setParentObjId($trgParentId);
4415  $assignment->setQuestionId($trgQuestionId);
4416  $assignment->saveToDb();
4417  }
4418  }
4419 
4420  public function syncSkillAssignments($srcParentId, $srcQuestionId, $trgParentId, $trgQuestionId)
4421  {
4422  global $ilDB;
4423 
4424  require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionSkillAssignmentList.php';
4425  $assignmentList = new ilAssQuestionSkillAssignmentList($ilDB);
4426  $assignmentList->setParentObjId($trgParentId);
4427  $assignmentList->setQuestionIdFilter($trgQuestionId);
4428  $assignmentList->loadFromDb();
4429 
4430  foreach($assignmentList->getAssignmentsByQuestionId($trgQuestionId) as $assignment)
4431  {
4432  /* @var ilAssQuestionSkillAssignment $assignment */
4433 
4434  $assignment->deleteFromDb();
4435  }
4436 
4437  $this->duplicateSkillAssignments($srcParentId, $srcQuestionId, $trgParentId, $trgQuestionId);
4438  }
4439 
4452  public function isAnswered($active_id, $pass = null)
4453  {
4454  return true;
4455  }
4456 
4469  public static function isObligationPossible($questionId)
4470  {
4471  return false;
4472  }
4473 
4474  public function isAutosaveable()
4475  {
4476  return TRUE;
4477  }
4478 
4491  protected static function getNumExistingSolutionRecords($activeId, $pass, $questionId)
4492  {
4493  global $ilDB;
4494 
4495  $query = "
4496  SELECT count(active_fi) cnt
4497 
4498  FROM tst_solutions
4499 
4500  WHERE active_fi = %s
4501  AND question_fi = %s
4502  AND pass = %s
4503  ";
4504 
4505  $res = $ilDB->queryF(
4506  $query, array('integer','integer','integer'),
4507  array($activeId, $questionId, $pass)
4508  );
4509 
4510  $row = $ilDB->fetchAssoc($res);
4511 
4512  return (int)$row['cnt'];
4513  }
4514 
4522  {
4524  }
4525 
4533  {
4535  {
4536  require_once 'Modules/TestQuestionPool/exceptions/class.ilTestQuestionPoolException.php';
4537  throw new ilTestQuestionPoolException('invalid additional content editing mode given: '.$additinalContentEditingMode);
4538  }
4539 
4540  $this->additinalContentEditingMode = $additinalContentEditingMode;
4541  }
4542 
4550  {
4552  }
4553 
4561  public function isValidAdditionalContentEditingMode($additionalContentEditingMode)
4562  {
4563  if( in_array($additionalContentEditingMode, $this->getValidAdditionalContentEditingModes()) )
4564  {
4565  return true;
4566  }
4567 
4568  return false;
4569  }
4570 
4578  {
4579  return array(
4580  self::ADDITIONAL_CONTENT_EDITING_MODE_DEFAULT,
4581  self::ADDITIONAL_CONTENT_EDITING_MODE_PAGE_OBJECT
4582  );
4583  }
4584 
4589  {
4590  $this->questionChangeListeners[] = $listener;
4591  }
4592 
4596  public function getQuestionChangeListeners()
4597  {
4599  }
4600 
4601  private function notifyQuestionCreated()
4602  {
4603  foreach($this->getQuestionChangeListeners() as $listener)
4604  {
4605  $listener->notifyQuestionCreated($this);
4606  }
4607  }
4608 
4609  private function notifyQuestionEdited()
4610  {
4611  foreach($this->getQuestionChangeListeners() as $listener)
4612  {
4613  $listener->notifyQuestionEdited($this);
4614  }
4615  }
4616 
4617  private function notifyQuestionDeleted()
4618  {
4619  foreach($this->getQuestionChangeListeners() as $listener)
4620  {
4621  $listener->notifyQuestionDeleted($this);
4622  }
4623  }
4624 
4629  {
4630  require_once 'Services/Html/classes/class.ilHtmlPurifierFactory.php';
4631  return ilHtmlPurifierFactory::_getInstanceByType('qpl_usersolution');
4632  }
4633 
4638  {
4639  require_once 'Services/Html/classes/class.ilHtmlPurifierFactory.php';
4640  return ilHtmlPurifierFactory::_getInstanceByType('qpl_usersolution');
4641  }
4642 
4643  protected function buildQuestionDataQuery()
4644  {
4645  return "
4646  SELECT qpl_questions.*,
4647  {$this->getAdditionalTableName()}.*
4648  FROM qpl_questions
4649  LEFT JOIN {$this->getAdditionalTableName()}
4650  ON {$this->getAdditionalTableName()}.question_fi = qpl_questions.question_id
4651  WHERE qpl_questions.question_id = %s
4652  ";
4653  }
4654 
4655  public function setLastChange($lastChange)
4656  {
4657  $this->lastChange = $lastChange;
4658  }
4659 
4660  public function getLastChange()
4661  {
4662  return $this->lastChange;
4663  }
4664 
4675  protected function getCurrentSolutionResultSet($active_id, $pass, $authorized = true)
4676  {
4677  global $ilDB;
4678 
4679  if($this->getStep() !== NULL)
4680  {
4681  $query = "
4682  SELECT *
4683  FROM tst_solutions
4684  WHERE active_fi = %s
4685  AND question_fi = %s
4686  AND pass = %s
4687  AND step = %s
4688  AND authorized = %s
4689  ";
4690 
4691  return $ilDB->queryF($query, array('integer', 'integer', 'integer', 'integer', 'integer'),
4692  array($active_id, $this->getId(), $pass, $this->getStep(), (int)$authorized)
4693  );
4694  }
4695  else
4696  {
4697  $query = "
4698  SELECT *
4699  FROM tst_solutions
4700  WHERE active_fi = %s
4701  AND question_fi = %s
4702  AND pass = %s
4703  AND authorized = %s
4704  ";
4705 
4706  return $ilDB->queryF($query, array('integer', 'integer', 'integer', 'integer'),
4707  array($active_id, $this->getId(), $pass, (int)$authorized)
4708  );
4709  }
4710 
4711  }
4712 
4719  protected function removeSolutionRecordById($solutionId)
4720  {
4721  global $ilDB;
4722 
4723  return $ilDB->manipulateF("DELETE FROM tst_solutions WHERE solution_id = %s",
4724  array('integer'), array($solutionId)
4725  );
4726  }
4727 
4736  public function removeIntermediateSolution($active_id, $pass)
4737  {
4738  return $this->removeCurrentSolution($active_id, $pass, false);
4739  }
4740 
4750  public function removeCurrentSolution($active_id, $pass, $authorized = true, $ignoredSolutionIds = array())
4751  {
4752  global $ilDB;
4753 
4754  $not_in = '';
4755  if(count($ignoredSolutionIds) > 0)
4756  {
4757  $not_in = ' AND ' . $ilDB->in('solution_id', $ignoredSolutionIds, true, 'integer') . ' ';
4758  }
4759 
4760  if($this->getStep() !== NULL)
4761  {
4762  $query = "
4763  DELETE FROM tst_solutions
4764  WHERE active_fi = %s
4765  AND question_fi = %s
4766  AND pass = %s
4767  AND step = %s
4768  AND authorized = %s
4769  $not_in
4770  ";
4771 
4772  return $ilDB->manipulateF($query, array('integer', 'integer', 'integer', 'integer', 'integer'),
4773  array($active_id, $this->getId(), $pass, $this->getStep(), (int)$authorized)
4774  );
4775  }
4776  else
4777  {
4778  $query = "
4779  DELETE FROM tst_solutions
4780  WHERE active_fi = %s
4781  AND question_fi = %s
4782  AND pass = %s
4783  AND authorized = %s
4784  $not_in
4785  ";
4786 
4787  return $ilDB->manipulateF($query, array('integer', 'integer', 'integer', 'integer'),
4788  array($active_id, $this->getId(), $pass, (int)$authorized)
4789  );
4790  }
4791  }
4792 
4803  public function saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized = true)
4804  {
4805  global $ilDB;
4806 
4807  $next_id = $ilDB->nextId("tst_solutions");
4808 
4809  $fieldData = array(
4810  "solution_id" => array("integer", $next_id),
4811  "active_fi" => array("integer", $active_id),
4812  "question_fi" => array("integer", $this->getId()),
4813  "value1" => array("clob", $value1),
4814  "value2" => array("clob", $value2),
4815  "pass" => array("integer", $pass),
4816  "tstamp" => array("integer", time()),
4817  'authorized' => array('integer', (int)$authorized)
4818  );
4819 
4820  if( $this->getStep() !== null )
4821  {
4822  $fieldData['step'] = array("integer", $this->getStep());
4823  }
4824 
4825  return $ilDB->insert("tst_solutions", $fieldData);
4826  }
4827 
4838  public function updateCurrentSolution($solutionId, $value1, $value2, $authorized = true)
4839  {
4840  global $ilDB;
4841 
4842  $fieldData = array(
4843  "value1" => array("clob", $value1),
4844  "value2" => array("clob", $value2),
4845  "tstamp" => array("integer", time()),
4846  'authorized' => array('integer', (int)$authorized)
4847  );
4848 
4849  if( $this->getStep() !== null )
4850  {
4851  $fieldData['step'] = array("integer", $this->getStep());
4852  }
4853 
4854  return $ilDB->update("tst_solutions", $fieldData, array(
4855  'solution_id' => array('integer', $solutionId)
4856  ));
4857  }
4858 
4859  public function updateCurrentSolutionsAuthorization($activeId, $pass, $authorized)
4860  {
4861  global $ilDB;
4862 
4863  $fieldData = array(
4864  'tstamp' => array('integer', time()),
4865  'authorized' => array('integer', (int)$authorized)
4866  );
4867 
4868  $whereData = array(
4869  'question_fi' => array('integer', $this->getId()),
4870  'active_fi' => array('integer', $activeId),
4871  'pass' => array('integer', $pass)
4872  );
4873 
4874  return $ilDB->update('tst_solutions', $fieldData, $whereData);
4875  }
4876 
4877 
4881  public static function setResultGateway($resultGateway)
4882  {
4883  self::$resultGateway = $resultGateway;
4884  }
4885 
4889  public static function getResultGateway()
4890  {
4891  return self::$resultGateway;
4892  }
4893 
4897  public function setStep($step)
4898  {
4899  $this->step = $step;
4900  }
4901 
4905  public function getStep()
4906  {
4907  return $this->step;
4908  }
4909 
4915  public static function sumTimesInISO8601FormatH_i_s_Extended($time1, $time2)
4916  {
4919  return gmdate('H:i:s', $time);
4920  }
4921 
4926  public static function convertISO8601FormatH_i_s_ExtendedToSeconds($time)
4927  {
4928  $sec = 0;
4929  $time_array = explode(':',$time);
4930  if( sizeof($time_array) == 3)
4931  {
4932  $sec += $time_array[0] * 3600;
4933  $sec += $time_array[1] * 60;
4934  $sec += $time_array[2];
4935  }
4936  return $sec;
4937  }
4938 
4942  protected function getSelfAssessmentFormatter()
4943  {
4944  require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssSelfAssessmentQuestionFormatter.php';
4945  return new \ilAssSelfAssessmentQuestionFormatter();
4946  }
4947 
4948  public function toJSON()
4949  {
4950  return json_encode(array());
4951  }
4952 
4953  abstract public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null);
4954 
4955  // hey: prevPassSolutions - check for authorized solution
4956  public function authorizedSolutionExists($active_id, $pass)
4957  {
4958  $solutionAvailability = $this->lookupForExistingSolutions($active_id, $pass);
4959  return (bool)$solutionAvailability['authorized'];
4960  }
4961  public function authorizedOrIntermediateSolutionExists($active_id, $pass)
4962  {
4963  $solutionAvailability = $this->lookupForExistingSolutions($active_id, $pass);
4964  return (bool)$solutionAvailability['authorized'] || (bool)$solutionAvailability['intermediate'];
4965  }
4966  // hey.
4967 
4973  protected function lookupMaxStep($active_id, $pass)
4974  {
4976  global $ilDB;
4977 
4978  $res = $ilDB->queryF(
4979  "SELECT MAX(step) max_step FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
4980  array("integer", "integer", "integer"), array($active_id, $pass, $this->getId())
4981  );
4982 
4983  $row = $ilDB->fetchAssoc($res);
4984 
4985  $maxStep = $row['max_step'];
4986 
4987  return $maxStep;
4988  }
4989 
4990  // fau: testNav - new function lookupForExistingSolutions
4997  public function lookupForExistingSolutions($activeId, $pass)
4998  {
4999  global $ilDB;
5000 
5001  $return = array(
5002  'authorized' => false,
5003  'intermediate' => false
5004  );
5005 
5006  $query = "
5007  SELECT authorized, COUNT(*) cnt
5008  FROM tst_solutions
5009  WHERE active_fi = %s
5010  AND question_fi = %s
5011  AND pass = %s
5012  GROUP BY authorized
5013  ";
5014  $result = $ilDB->queryF($query, array('integer', 'integer', 'integer'), array($activeId, $this->getId(), $pass));
5015 
5016  while ($row = $ilDB->fetchAssoc($result))
5017  {
5018  if ($row['authorized']) {
5019  $return['authorized'] = $row['cnt'] > 0;
5020  }
5021  else
5022  {
5023  $return['intermediate'] = $row['cnt'] > 0;
5024  }
5025  }
5026  return $return;
5027  }
5028  // fau.
5029 
5030  public function removeExistingSolutions($activeId, $pass)
5031  {
5032  global $ilDB;
5033 
5034  $query = "
5035  DELETE FROM tst_solutions
5036  WHERE active_fi = %s
5037  AND question_fi = %s
5038  AND pass = %s
5039  ";
5040 
5041  return $ilDB->manipulateF($query, array('integer', 'integer', 'integer'),
5042  array($activeId, $this->getId(), $pass)
5043  );
5044  }
5045 
5046  public function resetUsersAnswer($activeId, $pass)
5047  {
5048  $this->removeExistingSolutions($activeId, $pass);
5049  $this->removeResultRecord($activeId, $pass);
5050 
5051  $this->_updateTestPassResults(
5052  $activeId, $pass, $this->areObligationsToBeConsidered(), $this->getProcessLocker(), $this->getTestId()
5053  );
5054  }
5055 
5056  public function removeResultRecord($activeId, $pass)
5057  {
5058  global $ilDB;
5059 
5060  $query = "
5061  DELETE FROM tst_test_result
5062  WHERE active_fi = %s
5063  AND question_fi = %s
5064  AND pass = %s
5065  ";
5066 
5067  return $ilDB->manipulateF($query, array('integer', 'integer', 'integer'),
5068  array($activeId, $this->getId(), $pass)
5069  );
5070  }
5071 
5072  public static function missingResultRecordExists($activeId, $pass, $questionIds)
5073  {
5074  global $ilDB;
5075 
5076  $IN_questionIds = $ilDB->in('question_fi', $questionIds, false, 'integer');
5077 
5078  $query = "
5079  SELECT COUNT(*) cnt
5080  FROM tst_test_result
5081  WHERE active_fi = %s
5082  AND pass = %s
5083  AND $IN_questionIds
5084  ";
5085 
5086  $row = $ilDB->fetchAssoc($ilDB->queryF(
5087  $query, array('integer', 'integer'), array($activeId, $pass)
5088  ));
5089 
5090  return $row['cnt'] < count($questionIds);
5091  }
5092 
5093  public static function lookupResultRecordExist($activeId, $questionId, $pass)
5094  {
5095  global $ilDB;
5096 
5097  $query = "
5098  SELECT COUNT(*) cnt
5099  FROM tst_test_result
5100  WHERE active_fi = %s
5101  AND question_fi = %s
5102  AND pass = %s
5103  ";
5104 
5105  $row = $ilDB->fetchAssoc($ilDB->queryF($query, array('integer', 'integer', 'integer'), array($activeId, $questionId, $pass)));
5106 
5107  return $row['cnt'] > 0;
5108  }
5109 
5114  {
5116  }
5117 
5122  {
5123  $this->obligationsToBeConsidered = $obligationsToBeConsidered;
5124  }
5125 
5126  public function validateSolutionSubmit()
5127  {
5128  return true;
5129  }
5130 
5131  public function updateTimestamp()
5132  {
5133  global $ilDB;
5134 
5135  $ilDB->manipulateF("UPDATE qpl_questions SET tstamp = %s WHERE question_id = %s",
5136  array('integer', 'integer'),
5137  array(time(), $this->getId())
5138  );
5139  }
5140 }
static _getUserIdFromActiveId($active_id)
isInUse($question_id="")
Checks whether the question is in use or not.
static isObligationPossible($questionId)
returns boolean wether it is possible to set this question type as obligatory or not considering the ...
static isCoreQuestionType($questionType)
static getPluginObject($a_ctype, $a_cname, $a_slot_id, $a_pname)
Get plugin object.
static makeDirParents($a_dir)
Create a new directory and all parent directories.
deletePageOfQuestion($question_id)
Deletes the page object of a question with a given ID.
afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
getId()
Gets the id of the assQuestion object.
getUserSolutionPreferingIntermediate($active_id, $pass=NULL)
_getCountSystem($active_id)
Gets the count system for the calculation of points.
static prepareFormOutput($a_str, $a_strip=false)
prepares string output for html forms public
saveToDb($original_id="")
Saves the question to the database.
static getListByQuestionId($questionId)
instantiates a question hint list for the passed question id
ILIAS Setting Class.
static isFileAvailable($file)
$export_image_path
(Web) Path to images
print $file
lookupForExistingSolutions($activeId, $pass)
Lookup if an authorized or intermediate solution exists.
getFlashPathWeb()
Returns the web image path for web accessable flash applications of a question.
static _instanciateQuestion($question_id)
Creates an instance of a question with a given question id.
static _getOriginalId($question_id)
Returns the original id of a question.
formatSAQuestion($a_q)
Format self assessment question.
setSuggestedSolution($solution_id="", $subquestion_index=0, $is_import=false)
Sets a suggested solution for the question.
fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
Receives parameters from a QTI parser and creates a valid ILIAS question object.
static getObjectClassNameByQuestionType($questionType)
Taxonomy node <-> item assignment.
migrateContentForLearningModule(ilAssSelfAssessmentMigrator $migrator)
static _includeClass($question_type, $gui=0)
Include the php class file for a given question type.
_lookupAuthor($obj_id)
Gets the authors name of the ilObjTest object.
_getTotalAnswers($a_q_id)
get number of answers for question id (static) note: do not use $this inside this method ...
static sumTimesInISO8601FormatH_i_s_Extended($time1, $time2)
$_POST['username']
Definition: cron.php:12
_questionExistsInPool($question_id)
Returns true if the question already exists in the database and is assigned to a question pool...
copySuggestedSolutionFiles($source_questionpool_id, $source_question_id)
static getNumExistingSolutionRecords($activeId, $pass, $questionId)
returns the number of existing solution records for the given test active / pass and given question i...
static prepareTextareaOutput($txt_output, $prepare_for_latex_output=FALSE, $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output where latex code may be in it If the text is HTML-free...
getSuggestedSolutionPath()
Returns the path for a suggested solution.
$_SESSION["AccountId"]
$result
static getUsageOfObject($a_obj_id, $a_include_titles=false)
Get usage of object.
static lookupResultRecordExist($activeId, $questionId, $pass)
_isWriteable($object_id, $user_id)
Returns true, if the question pool is writeable by a given user.
getQuestionType()
Returns the question type of the question.
static includeCoreClass($questionType, $withGuiClass)
syncSkillAssignments($srcParentId, $srcQuestionId, $trgParentId, $trgQuestionId)
static _getTotalRightAnswers($a_q_id)
get number of answers for question id (static) note: do not use $this inside this method ...
getPoints()
Returns the maximum available points for the question.
static originalQuestionExists($questionId)
copyPageOfQuestion($a_q_id)
questionTitleExists($questionpool_id, $title)
Returns TRUE if the question title exists in the database.
toXML($a_include_header=true, $a_include_binary=true, $a_shuffle=false, $test_output=false, $force_image_references=false)
Returns a QTI xml representation of the question.
_getManualScoringTypes()
Retrieve the manual scoring settings as type strings.
const ADDITIONAL_CONTENT_EDITING_MODE_PAGE_OBJECT
constant for additional content editing mode "pageobject"
$_GET["client_id"]
__set($key, $value)
Object setter.
__get($value)
Object getter.
Abstract basic class which is to be extended by the concrete assessment question type classes...
_getPass($active_id)
Retrieves the actual pass of a given user for a given test.
& _getSuggestedSolution($question_id, $subquestion_index=0)
Returns a suggested solution for a given subquestion index.
setDefaultNrOfTries($a_defaultnroftries)
Set Default Nr of Tries.
adjustReachedPointsByScoringOptions($points, $active_id, $pass=NULL)
Adjust the given reached points by checks for all special scoring options in the test container...
createPageObject()
create page object of question
deleteAnswers($question_id)
Deletes datasets from answers tables.
static & _instanciateQuestionGUI($question_id)
Creates an instance of a question gui with a given question id.
static _areAnswered($a_user_id, $a_question_ids)
Checks if an array of question ids is answered by an user or not.
prepareTextareaOutput($txt_output, $prepare_for_latex_output=FALSE, $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output in tests.
getSuggestedSolutionTitle($subquestion_index=0)
Returns the title of a suggested solution at a given subquestion_index.
getReachedPoints($active_id, $pass=NULL)
Returns the points, a learner has reached answering the question This is the fast way to get the poin...
_getResultPass($active_id)
Retrieves the pass number that should be counted for a given user.
setId($id=-1)
Sets the id of the assQuestion object.
copyXHTMLMediaObjectsOfQuestion($a_q_id)
& _getQuestionInfo($question_id)
Returns question information from the database.
static _getQuestionTypeName($type_tag)
Return the translation for a given question type tag.
getAdditionalTableName()
Returns the name of the additional question data table in the database.
duplicateSkillAssignments($srcParentId, $srcQuestionId, $trgParentId, $trgQuestionId)
static isForcePassResultUpdateEnabled()
static _getSuggestedSolutionCount($question_id)
Returns the number of suggested solutions associated with a question.
getImagePathWeb()
Returns the web image path for web accessable images of a question.
Question page object.
savePreviewData(ilAssQuestionPreviewSession $previewSession)
Reworks the allready saved working data if neccessary.
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
$target_id
Definition: goto.php:88
removeResultRecord($activeId, $pass)
_getWorkingTimeOfParticipantForPass($active_id, $pass)
Returns the complete working time in seconds for a test participant.
createRandomSolution($test_id, $user_id)
createNewQuestion($a_create_page=true)
Creates a new question without an owner when a new question is created This assures that an ID is giv...
static includePluginClass($questionType, $withGuiClass)
setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
Sets the estimated working time of a question from given hour, minute and second. ...
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
static _lookupTitle($a_id)
lookup object title
syncSuggestedSolutionFiles($original_id)
Syncs the files of a suggested solution if the question is synced.
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
assQuestion constructor
setNewOriginalId($newId)
beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
_getQuestionTitle($question_id)
Returns the question title of a question with a given id.
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true)
getAdditionalContentEditingMode()
getter for additional content editing mode for this question
_getQuestionText($a_q_id)
Returns question text.
getJavaPath()
Returns the image path for web accessable images of a question.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
deleteAdditionalTableData($question_id)
Deletes datasets from the additional question table in the database.
_getTitle($a_q_id)
Returns the title of a question.
_getQuestionCountAndPointsForPassOfParticipant($active_id, $pass)
isAnswered($active_id, $pass=null)
returns boolean wether the question is answered during test pass or not
setEstimatedWorkingTimeFromDurationString($durationString)
Sets the estimated working time of a question from a given datetime string.
getSelfAssessmentEditingMode()
Get Self-Assessment Editing Mode.
calculateResultsFromSolution($active_id, $pass=NULL, $obligationsEnabled=false)
Calculates the question results from a previously saved question solution.
setNrOfTries($a_nr_of_tries)
_enabledAssessmentLogging()
check wether assessment logging is enabled or not
_cleanupMediaObjectUsage($a_text, $a_usage_type, $a_usage_id)
synchronises appearances of media objects in $a_text with media object usage table ...
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
static lookupParentObjId($questionId)
ilDB $ilDB
const OUTPUT_JAVASCRIPT
isHTML($a_text)
Checks if a given string contains HTML or not.
loadFromDb($question_id)
Loads the question from the database.
persistPreviewState(ilAssQuestionPreviewSession $previewSession)
persists the preview state for current user and question
authorizedSolutionExists($active_id, $pass)
setShuffle($shuffle=true)
Sets the shuffle flag.
static _replaceMediaObjectImageSrc($a_text, $a_direction=0, $nic=IL_INST_ID)
replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
getObjId()
Get the object id of the container object.
getValidAdditionalContentEditingModes()
getter for valid additional content editing modes
getShuffle()
Gets the shuffle flag.
$arrData
Associative array to store properties.
static _getAllReferences($a_id)
get all reference ids of object
_removeUsage($a_mob_id, $a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
Remove usage of mob in another container.
isValidAdditionalContentEditingMode($additionalContentEditingMode)
returns the fact wether the passed additional content mode is valid or not
& getInstances()
Gets all instances of the question.
_updateObjectiveResult($a_user_id, $a_active_id, $a_question_id)
_getQuestionType($question_id)
Returns the question type of a question with a given id.
global $ilCtrl
Definition: ilias.php:18
getAdjustedReachedPoints($active_id, $pass=NULL, $authorizedSolution=true)
returns the reached points ...
setProcessLocker($processLocker)
isPreviewSolutionCorrect(ilAssQuestionPreviewSession $previewSession)
_questionExistsInTest($question_id, $test_id)
static convertISO8601FormatH_i_s_ExtendedToSeconds($time)
static isQuestionObligatory($question_id)
checks wether the question with given id is marked as obligatory or not
static deleteHintsByQuestionIds($questionIds)
Deletes all question hints relating to questions included in given question ids.
getSuggestedSolution($subquestion_index=0)
Returns a suggested solution for a given subquestion index.
authorizedOrIntermediateSolutionExists($active_id, $pass)
removeSolutionRecordById($solutionId)
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $previewSession)
duplicateQuestionHints($originalQuestionId, $duplicateQuestionId)
_logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
updateCurrentSolutionsAuthorization($activeId, $pass, $authorized)
supportsJavascriptOutput()
Returns true if the question type supports JavaScript output.
getJavaPathWeb()
Returns the web image path for web accessable java applets of a question.
_getObjectIDFromActiveID($active_id)
Returns the ILIAS test object id for a given active id.
getFlashPath()
Returns the image path for web accessable flash files of a question.
static isAllowedImageMimeType($mimeType)
getTestId()
Gets the test id of the assQuestion object.
setAuthor($author="")
Sets the authors name of the assQuestion object.
setObligationsToBeConsidered($obligationsToBeConsidered)
Assessment hint page object.
setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
Creates an Excel worksheet for the detailed cumulated results of this question.
getAuthor()
Gets the authors name of the assQuestion object.
getTotalAnswers()
get total number of answers
_saveUsage($a_mob_id, $a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
Save usage of mob within another container (e.g.
getQuestionTypeID()
Returns the question type of the question.
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
$data
static _updateQuestionCount($object_id)
Updates the number of available questions for a question pool in the database.
_getSolutionMaxPass($question_id, $active_id)
Returns the maximum pass a users question solution.
$mobs
static setForcePassResultUpdateEnabled($forcePassResultsUpdateEnabled)
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
removeCurrentSolution($active_id, $pass, $authorized=true, $ignoredSolutionIds=array())
const ILIAS_ABSOLUTE_PATH
static _exists($a_id)
checks wether a lm content object with specified id exists or not
static getImagePath($img, $module_path="", $mode="output", $offline=false)
get image path (for images located in a template directory)
_addLog($user_id, $object_id, $logtext, $question_id="", $original_id="", $test_only=FALSE, $test_ref_id=NULL)
Add an assessment log entry.
static moveUploadedFile($a_file, $a_name, $a_target, $a_raise_errors=true, $a_mode="move_uploaded")
move uploaded file
static _lookupObjId($a_id)
setOutputType($outputType=OUTPUT_HTML)
Sets the output type.
isComplete()
Returns true, if a question is complete for use.
static setResultGateway($resultGateway)
saveWorkingData($active_id, $pass=NULL, $authorized=true)
Saves the learners input of the question to the database.
static $forcePassResultsUpdateEnabled
getQuestion()
Gets the question string of the question object.
getComment()
Gets the comment string of the assQuestion object.
const IL_COMP_MODULE
static lookupOriginalParentObjId($originalQuestionId)
returns the parent object id for given original question id (should be a qpl id, but theoretically it...
redirection script todo: (a better solution should control the processing via a xml file) ...
static createDirectory($a_dir, $a_mod=0755)
create directory
_getMaximumPoints($question_id)
Returns the maximum points, a learner can reach answering the question.
_getInternalLinkHref($target="")
Class ilObjMediaObject.
$nr_of_tries
Number of tries.
persistWorkingState($active_id, $pass=NULL, $obligationsEnabled=false, $authorized=true)
persists the working state for current testactive and testpass
getSuggestedSolutionPathWeb()
Returns the web path for a suggested solution.
moveUploadedMediaFile($file, $name)
Move an uploaded media file to an public accessible temp dir to present it.
getDefaultNrOfTries()
Get Default Nr of Tries.
isClone($question_id="")
Checks whether the question is a clone of another question or not.
updateCurrentSolution($solutionId, $value1, $value2, $authorized=true)
static removeTrailingPathSeparators($path)
_getParticipantData($active_id)
Retrieves a participant name from active id.
cleanupMediaObjectUsage()
synchronises appearances of media objects in the question with media object usage table ...
deleteSuggestedSolutions()
Deletes all suggestes solutions in the database.
setPreventRteUsage($a_val)
Set prevent rte usage.
_needsManualScoring($question_id)
deleteRequestsByQuestionIds($questionIds)
Deletes all hint requests relating to a question included in given question ids.
_getLogLanguage()
retrieve the log language for assessment logging
fixSvgToPng($imageFilenameContainingString)
static _instantiateQuestion($question_id)
_isUsedInRandomTest($question_id="")
Checks whether the question is used in a random test or not.
setExternalId($external_id)
$filename
Definition: buildRTE.php:89
_getScoreCutting($active_id)
Determines if the score of a question should be cut at 0 points or the score of the whole test...
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
_getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
static duplicateListForQuestion($originalQuestionId, $duplicateQuestionId)
duplicates a hint list from given original question id to given duplicate question id and returns an ...
_getIdForImportId($a_import_id)
get current object id for import id (static)
isAdditionalContentEditingModePageObject()
isser for additional "pageobject" content editing mode
static $allowedCharsetsByMimeType
const ADDITIONAL_CONTENT_EDITING_MODE_DEFAULT
constant for additional content editing mode "default"
setPoints($a_points)
Sets the maximum available points for the question.
saveQuestionDataToDb($original_id="")
_resolveIntLinks($question_id)
static _getSuggestedSolutionOutput($question_id)
Returns the output of the suggested solution.
static getAllowedImageFileExtensions()
onDuplicate($originalParentId, $originalQuestionId, $duplicateParentId, $duplicateQuestionId)
Will be called when a question is duplicated (inside a question pool or for insertion in a test) ...
_questionExists($question_id)
Returns true if the question already exists in the database.
static _getInstanceByType($a_type)
Factory method for creating purifier instances.
static $imageSourceFixReplaceMap
getOwner()
Gets the creator/owner ID of the assQuestion object.
_getReachedPoints($active_id, $question_id, $pass=NULL)
Returns the points, a learner has reached answering the question.
static isAllowedImageFileExtension($mimeType, $fileExtension)
lmMigrateQuestionTypeGenericContent(ilAssSelfAssessmentMigrator $migrator)
global $ilUser
Definition: imgupload.php:15
resetUsersAnswer($activeId, $pass)
getEstimatedWorkingTime()
Gets the estimated working time of a question.
setQuestion($question="")
Sets the question string of the question object.
setTestId($id=-1)
Sets the test id of the assQuestion object.
buildImagePath($questionId, $parentObjectId)
global $ilDB
removeExistingSolutions($activeId, $pass)
setOriginalId($original_id)
static getResultGateway()
setLastChange($lastChange)
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass...
getAnswerTableName()
Returns the name of the answer table in the database.
getQuestionTypeFromDb($question_id)
get question type for question id
_setReachedPoints($active_id, $question_id, $points, $maxpoints, $pass, $manualscoring, $obligationsEnabled)
Sets the points, a learner has reached answering the question Additionally objective results are upda...
static buildExamId($active_id, $pass, $test_obj_id=null)
calculateReachedPoints($active_id, $pass=NULL, $authorizedSolution=true, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
removeIntermediateSolution($active_id, $pass)
getSolutionValues($active_id, $pass=NULL, $authorized=true)
Loads solutions of a given user from the database an returns it.
logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
static getGuiClassNameByQuestionType($questionType)
getTitle()
Gets the title string of the assQuestion object.
const OUTPUT_HTML
addQuestionChangeListener(ilQuestionChangeListener $listener)
static isHTML($a_text)
Checks if a given string contains HTML or not.
static fetchMimeTypeIdentifier($contentTypeString)
pcArrayShuffle($array)
Shuffles the values of a given array.
duplicateSuggestedSolutionFiles($parent_id, $question_id)
Duplicates the files of a suggested solution if the question is duplicated.
getOutputType()
Gets the output type.
onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $targetQuestionId)
Will be called when a question is copied (into another question pool)
getSuggestedSolutions()
Return the suggested solutions.
_resolveInternalLink($internal_link)
_updateTestResultCache($active_id, ilAssQuestionProcessLocker $processLocker=null)
Move this to a proper place.
setTitle($title="")
Sets the title string of the assQuestion object.
fixUnavailableSkinImageSources($html)
static _isWorkedThrough($active_id, $question_id, $pass=NULL)
Returns true if the question was worked through in the given pass Worked through means that the user ...
setObjId($obj_id=0)
Set the object id of the container object.
getActiveUserData($active_id)
Returns the user id and the test id for a given active id.
static delDir($a_dir, $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
setExportImagePath($a_path)
setComment($comment="")
Sets the comment string of the assQuestion object.
setShuffler(ilArrayElementShuffler $shuffler)
static $allowedFileExtensionsByMimeType
static getFeedbackClassNameByQuestionType($questionType)
$html
Definition: example_001.php:87
createNewImageFileName($image_filename, $unique=false)
static instantiateQuestionGUI($a_question_id)
Creates an instance of a question gui with a given question id.
keyInArray($searchkey, $array)
returns TRUE if the key occurs in an array
_isWriteable($question_id, $user_id)
Returns true if the question is writeable by a certain user.
addQTIMaterial(&$a_xml_writer, $a_material, $close_material_tag=TRUE, $add_mobs=TRUE)
Creates a QTI material tag from a plain text or xhtml text.
QTIMaterialToString($a_material)
Reads an QTI material tag an creates a text string.
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
setSelfAssessmentEditingMode($a_selfassessmenteditingmode)
Set Self-Assessment Editing Mode.
static missingResultRecordExists($activeId, $pass, $questionIds)
getPreventRteUsage()
Get prevent rte usage.
static getAllowedFileExtensionsForMimeType($mimeType)