ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
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
4include_once "./Modules/Test/classes/inc.AssessmentConstants.php";
5
20abstract 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
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
143
150
156 protected $original_id;
157
163 protected $page;
164
169
173 private $arrData;
174
179
184 protected $external_id = '';
185
190
195
202
208 public $feedbackOBJ = null;
209
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
264
265// fau: testNav - new variable $testQuestionConfig
270// fau.
271
273 'image/jpeg' => array('jpg', 'jpeg'), 'image/png' => array('png'), 'image/gif' => array('gif')
274 );
275
286 function __construct(
287 $title = "",
288 $comment = "",
289 $author = "",
290 $owner = -1,
291 $question = ""
292 )
293 {
294 global $ilias, $lng, $tpl, $ilDB;
295
296 $this->ilias = $ilias;
297 $this->lng = $lng;
298 $this->tpl = $tpl;
299 $this->db = $ilDB;
300
301 $this->original_id = null;
302 $this->title = $title;
303 $this->comment = $comment;
304 $this->page = null;
305 $this->author = $author;
306 $this->setQuestion($question);
307 if (!$this->author)
308 {
309 $this->author = $this->ilias->account->fullname;
310 }
311 $this->owner = $owner;
312 if ($this->owner <= 0)
313 {
314 $this->owner = $this->ilias->account->id;
315 }
316 $this->id = -1;
317 $this->test_id = -1;
318 $this->suggested_solutions = array();
319 $this->shuffle = 1;
320 $this->nr_of_tries = 0;
321 $this->setEstimatedWorkingTime(0,1,0);
322 $this->arrData = array();
323 $this->setExternalId('');
324
325 $this->questionActionCmd = 'handleQuestionAction';
326
327 $this->lastChange = null;
328
329 require_once 'Services/Randomization/classes/class.ilArrayElementOrderKeeper.php';
330 $this->shuffler = new ilArrayElementOrderKeeper();
331 }
332
333 protected static $forcePassResultsUpdateEnabled = false;
334
336 {
337 self::$forcePassResultsUpdateEnabled = $forcePassResultsUpdateEnabled;
338 }
339
340 public static function isForcePassResultUpdateEnabled()
341 {
343 }
344
345 public static function isAllowedImageMimeType($mimeType)
346 {
347 return (bool)count(self::getAllowedFileExtensionsForMimeType($mimeType));
348 }
349
350 public static function fetchMimeTypeIdentifier($contentTypeString)
351 {
352 return current(explode(';', $contentTypeString));
353 }
354
355 public static function getAllowedFileExtensionsForMimeType($mimeType)
356 {
357 foreach(self::$allowedFileExtensionsByMimeType as $allowedMimeType => $extensions)
358 {
359 $rexCharsets = implode('|', self::$allowedCharsetsByMimeType[$allowedMimeType]);
360 $rexMimeType = preg_quote($allowedMimeType, '/');
361
362 $rex = '/^'.$rexMimeType.'(;(\s)*charset=('.$rexCharsets.'))*$/';
363
364 if( !preg_match($rex, $mimeType) )
365 {
366 continue;
367 }
368
369 return $extensions;
370 }
371
372 return array();
373 }
374
375 public static function isAllowedImageFileExtension($mimeType, $fileExtension)
376 {
377 return in_array(
378 strtolower($fileExtension), self::getAllowedFileExtensionsForMimeType($mimeType)
379 );
380 }
381
382 // hey: prevPassSolutions - question action actracted (heavy use in fileupload refactoring)
383
387 protected function getQuestionAction()
388 {
389 if( !isset($_POST['cmd']) || !isset($_POST['cmd'][$this->questionActionCmd]) )
390 {
391 return '';
392 }
393
394 if( !is_array($_POST['cmd'][$this->questionActionCmd]) || !count($_POST['cmd'][$this->questionActionCmd]) )
395 {
396 return '';
397 }
398
399 return key($_POST['cmd'][$this->questionActionCmd]);
400 }
401
406 protected function isNonEmptyItemListPostSubmission($postSubmissionFieldname)
407 {
408 if( !isset($_POST[$postSubmissionFieldname]) )
409 {
410 return false;
411 }
412
413 if( !is_array($_POST[$postSubmissionFieldname]) )
414 {
415 return false;
416 }
417
418 if( !count($_POST[$postSubmissionFieldname]) )
419 {
420 return false;
421 }
422
423 return true;
424 }
425
431 protected function ensureCurrentTestPass($active_id, $pass)
432 {
433 if( is_integer($pass) && $pass >= 0 )
434 {
435 return $pass;
436 }
437
438 return $this->lookupCurrentTestPass($active_id, $pass);
439 }
440
446 protected function lookupCurrentTestPass($active_id, $pass)
447 {
448 require_once 'Modules/Test/classes/class.ilObjTest.php';
449 return ilObjTest::_getPass($active_id);
450 }
451
456 protected function lookupTestId($active_id)
457 {
458 $ilDB = isset($GLOBALS['DIC']) ? $GLOBALS['DIC']['ilDB'] : $GLOBALS['ilDB'];
459
460 $result = $ilDB->queryF("SELECT test_fi FROM tst_active WHERE active_id = %s",
461 array('integer'), array($active_id)
462 );
463
464 while($row = $ilDB->fetchAssoc($result))
465 {
466 return $row["test_fi"];
467 }
468
469 return null;
470 }
471 // hey.
472
477 protected function log($active_id, $langVar)
478 {
480 {
481 $message = $this->lng->txtlng('assessment', $langVar, ilObjAssessmentFolder::_getLogLanguage());
482 assQuestion::logAction($message, $active_id, $this->getId());
483 }
484 }
485
490 {
491 $extensions = array();
492
493 foreach (self::$allowedImageMaterialFileExtensionsByMimeType as $mimeType => $mimeExtensions)
494 {
495 $extensions = array_merge($extensions, $mimeExtensions);
496 }
497 return array_unique($extensions);
498 }
499
503 public function getShuffler()
504 {
505 return $this->shuffler;
506 }
507
512 {
513 $this->shuffler = $shuffler;
514 }
515
520 {
521 $this->processLocker = $processLocker;
522 }
523
527 public function getProcessLocker()
528 {
530 }
531
543 function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
544 {
545 include_once "./Modules/TestQuestionPool/classes/import/qti12/class." . $this->getQuestionType() . "Import.php";
546 $classname = $this->getQuestionType() . "Import";
547 $import = new $classname($this);
548 $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
549 }
550
557 function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
558 {
559 include_once "./Modules/TestQuestionPool/classes/export/qti12/class." . $this->getQuestionType() . "Export.php";
560 $classname = $this->getQuestionType() . "Export";
561 $export = new $classname($this);
562 return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
563 }
564
571 function isComplete()
572 {
573 return false;
574 }
575
583 function questionTitleExists($questionpool_id, $title)
584 {
585 global $ilDB;
586
587 $result = $ilDB->queryF("SELECT * FROM qpl_questions WHERE obj_fi = %s AND title = %s",
588 array('integer','text'),
589 array($questionpool_id, $title)
590 );
591 return ($result->numRows() > 0) ? TRUE : FALSE;
592 }
593
601 function setTitle($title = "")
602 {
603 $this->title = $title;
604 }
605
613 function setId($id = -1)
614 {
615 $this->id = $id;
616 }
617
625 function setTestId($id = -1)
626 {
627 $this->test_id = $id;
628 }
629
637 function setComment($comment = "")
638 {
639 $this->comment = $comment;
640 }
641
650 {
651 $this->outputType = $outputType;
652 }
653
654
662 function setShuffle($shuffle = true)
663 {
664 if ($shuffle)
665 {
666 $this->shuffle = 1;
667 }
668 else
669 {
670 $this->shuffle = 0;
671 }
672 }
673
684 function setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
685 {
686 $this->est_working_time = array("h" => (int)$hour, "m" => (int)$min, "s" => (int)$sec);
687 }
688
696 {
697 $this->est_working_time = array(
698 'h' => (int)substr($durationString, 0, 2),
699 'm' => (int)substr($durationString, 3, 2),
700 's' => (int)substr($durationString, 6, 2)
701 );
702 }
703
711 function keyInArray($searchkey, $array)
712 {
713 if ($searchkey)
714 {
715 foreach ($array as $key => $value)
716 {
717 if (strcmp($key, $searchkey)==0)
718 {
719 return true;
720 }
721 }
722 }
723 return false;
724 }
725
733 function setAuthor($author = "")
734 {
735 if (!$author)
736 {
737 $author = $this->ilias->account->fullname;
738 }
739 $this->author = $author;
740 }
741
749 function setOwner($owner = "")
750 {
751 $this->owner = $owner;
752 }
753
761 function getTitle()
762 {
763 return $this->title;
764 }
765
772 {
773 require_once 'Services/Utilities/classes/class.ilUtil.php';
774 return ilUtil::getASCIIFilename($this->getTitle());
775 }
776
784 function getId()
785 {
786 return $this->id;
787 }
788
796 function getShuffle()
797 {
798 return $this->shuffle;
799 }
800
808 function getTestId()
809 {
810 return $this->test_id;
811 }
812
820 function getComment()
821 {
822 return $this->comment;
823 }
824
832 function getOutputType()
833 {
834 return $this->outputType;
835 }
836
843 public function supportsJavascriptOutput()
844 {
845 return FALSE;
846 }
847
848 public function supportsNonJsOutput()
849 {
850 return true;
851 }
852
853 public function requiresJsSwitch()
854 {
855 return $this->supportsJavascriptOutput() && $this->supportsNonJsOutput();
856 }
857
866 {
867 if (!$this->est_working_time)
868 {
869 $this->est_working_time = array("h" => 0, "m" => 0, "s" => 0);
870 }
872 }
873
881 function getAuthor()
882 {
883 return $this->author;
884 }
885
893 function getOwner()
894 {
895 return $this->owner;
896 }
897
905 function getObjId()
906 {
907 return $this->obj_id;
908 }
909
917 function setObjId($obj_id = 0)
918 {
919 $this->obj_id = $obj_id;
920 }
921
926 {
927 $this->external_id = $external_id;
928 }
929
933 public function getExternalId()
934 {
935 if(!strlen($this->external_id))
936 {
937 if($this->getId() > 0)
938 {
939 return 'il_' . IL_INST_ID . '_qst_' . $this->getId();
940 }
941 else
942 {
943 return uniqid('', true);
944 }
945 }
946 else
947 {
948 return $this->external_id;
949 }
950 }
951
958 public static function _getMaximumPoints($question_id)
959 {
960 global $ilDB;
961
962 $points = 0;
963 $result = $ilDB->queryF("SELECT points FROM qpl_questions WHERE question_id = %s",
964 array('integer'),
965 array($question_id)
966 );
967 if ($result->numRows() == 1)
968 {
969 $row = $ilDB->fetchAssoc($result);
970 $points = $row["points"];
971 }
972 return $points;
973 }
974
981 public static function _getQuestionInfo($question_id)
982 {
983 global $ilDB;
984
985 $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",
986 array('integer'),
987 array($question_id)
988 );
989 if ($result->numRows())
990 {
991 return $ilDB->fetchAssoc($result);
992 }
993 else return array();
994 }
995
1002 public static function _getSuggestedSolutionCount($question_id)
1003 {
1004 global $ilDB;
1005
1006 $result = $ilDB->queryF("SELECT suggested_solution_id FROM qpl_sol_sug WHERE question_fi = %s",
1007 array('integer'),
1008 array($question_id)
1009 );
1010 return $result->numRows();
1011 }
1012
1019 public static function _getSuggestedSolutionOutput($question_id)
1020 {
1022 if (!is_object($question)) return "";
1023 return $question->getSuggestedSolutionOutput();
1024 }
1025
1027 {
1028 $output = array();
1029 foreach ($this->suggested_solutions as $solution)
1030 {
1031 switch ($solution["type"])
1032 {
1033 case "lm":
1034 case "st":
1035 case "pg":
1036 case "git":
1037 array_push($output, '<a href="' . assQuestion::_getInternalLinkHref($solution["internal_link"]) . '">' . $this->lng->txt("solution_hint") . '</a>');
1038 break;
1039 case "file":
1040 $possible_texts = array_values(array_filter(array(
1041 ilUtil::prepareFormOutput($solution['value']['filename']),
1042 ilUtil::prepareFormOutput($solution['value']['name']),
1043 $this->lng->txt('tst_show_solution_suggested')
1044 )));
1045
1046 require_once 'Services/WebAccessChecker/classes/class.ilWACSignedPath.php';
1048 array_push($output, '<a href="' . ilWACSignedPath::signFile($this->getSuggestedSolutionPathWeb() . $solution["value"]["name"]) . '">' . $possible_texts[0] . '</a>');
1049 break;
1050 case "text":
1051 $solutionValue = $solution["value"];
1052 $solutionValue = $this->fixSvgToPng($solutionValue);
1053 $solutionValue = $this->fixUnavailableSkinImageSources($solutionValue);
1054 $output[] = $this->prepareTextareaOutput($solutionValue, true);
1055 break;
1056 }
1057 }
1058 return join($output, "<br />");
1059 }
1060
1069 function &_getSuggestedSolution($question_id, $subquestion_index = 0)
1070 {
1071 global $ilDB;
1072
1073 $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s AND subquestion_index = %s",
1074 array('integer','integer'),
1075 array($question_id, $subquestion_index)
1076 );
1077 if ($result->numRows() == 1)
1078 {
1079 $row = $ilDB->fetchAssoc($result);
1080 return array(
1081 "internal_link" => $row["internal_link"],
1082 "import_id" => $row["import_id"]
1083 );
1084 }
1085 else
1086 {
1087 return array();
1088 }
1089 }
1090
1096 public function getSuggestedSolutions()
1097 {
1099 }
1100
1108 public static function _getReachedPoints($active_id, $question_id, $pass = NULL)
1109 {
1110 global $ilDB;
1111
1112 $points = 0;
1113 if (is_null($pass))
1114 {
1115 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
1116 $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
1117 }
1118 $result = $ilDB->queryF("SELECT * FROM tst_test_result WHERE active_fi = %s AND question_fi = %s AND pass = %s",
1119 array('integer','integer','integer'),
1120 array($active_id, $question_id, $pass)
1121 );
1122 if ($result->numRows() == 1)
1123 {
1124 $row = $ilDB->fetchAssoc($result);
1125 $points = $row["points"];
1126 }
1127 return $points;
1128 }
1129
1138 function getReachedPoints($active_id, $pass = NULL)
1139 {
1140 return round(self::_getReachedPoints($active_id, $this->getId(), $pass), 2);
1141 }
1142
1150 {
1151 return $this->points;
1152 }
1153
1165 final public function getAdjustedReachedPoints($active_id, $pass = NULL, $authorizedSolution = true)
1166 {
1167 if (is_null($pass))
1168 {
1169 include_once "./Modules/Test/classes/class.ilObjTest.php";
1170 $pass = ilObjTest::_getPass($active_id);
1171 }
1172
1173 // determine reached points for submitted solution
1174 $reached_points = $this->calculateReachedPoints($active_id, $pass, $authorizedSolution);
1175
1176
1177
1178 // deduct points for requested hints from reached points
1179 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
1180 $hintTracking = new ilAssQuestionHintTracking($this->getId(), $active_id, $pass);
1181 $requestsStatisticData = $hintTracking->getRequestStatisticDataByQuestionAndTestpass();
1182 $reached_points = $reached_points - $requestsStatisticData->getRequestsPoints();
1183
1184 // adjust reached points regarding to tests scoring options
1185 $reached_points = $this->adjustReachedPointsByScoringOptions($reached_points, $active_id, $pass);
1186
1187 return $reached_points;
1188 }
1189
1199 final public function calculateResultsFromSolution($active_id, $pass = NULL, $obligationsEnabled = false)
1200 {
1201 global $ilDB, $ilUser;
1202
1203 if( is_null($pass) )
1204 {
1205 include_once "./Modules/Test/classes/class.ilObjTest.php";
1206 $pass = ilObjTest::_getPass($active_id);
1207 }
1208
1209 // determine reached points for submitted solution
1210 $reached_points = $this->calculateReachedPoints($active_id, $pass);
1211
1212 // deduct points for requested hints from reached points
1213 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
1214 $questionHintTracking = new ilAssQuestionHintTracking($this->getId(), $active_id, $pass);
1215 $requestsStatisticData = $questionHintTracking->getRequestStatisticDataByQuestionAndTestpass();
1216 $reached_points = $reached_points - $requestsStatisticData->getRequestsPoints();
1217
1218 // adjust reached points regarding to tests scoring options
1219 $reached_points = $this->adjustReachedPointsByScoringOptions($reached_points, $active_id, $pass);
1220
1221 if( $obligationsEnabled && ilObjTest::isQuestionObligatory($this->getId()) )
1222 {
1223 $isAnswered = $this->isAnswered($active_id, $pass);
1224 }
1225 else
1226 {
1227 $isAnswered = true;
1228 }
1229
1230 if( is_null($reached_points) ) $reached_points = 0;
1231
1232// fau: testNav - check for existing authorized solution to know if a result record should be written
1233 $existingSolutions = $this->lookupForExistingSolutions($active_id, $pass);
1234
1235 $this->getProcessLocker()->executeUserQuestionResultUpdateOperation(function() use($ilDB, $active_id, $pass, $reached_points, $requestsStatisticData, $isAnswered, $existingSolutions) {
1236
1237 $query = "
1238 DELETE FROM tst_test_result
1239
1240 WHERE active_fi = %s
1241 AND question_fi = %s
1242 AND pass = %s
1243 ";
1244
1245 $types = array('integer', 'integer', 'integer');
1246 $values = array($active_id, $this->getId(), $pass);
1247
1248 if( $this->getStep() !== NULL )
1249 {
1250 $query .= "
1251 AND step = %s
1252 ";
1253
1254 $types[] = 'integer';
1255 $values[] = $this->getStep();
1256 }
1257 $ilDB->manipulateF($query, $types, $values);
1258
1259 if ($existingSolutions['authorized'])
1260 {
1261 $next_id = $ilDB->nextId("tst_test_result");
1262 $fieldData = array(
1263 'test_result_id' => array('integer', $next_id),
1264 'active_fi' => array('integer', $active_id),
1265 'question_fi' => array('integer', $this->getId()),
1266 'pass' => array('integer', $pass),
1267 'points' => array('float', $reached_points),
1268 'tstamp' => array('integer', time()),
1269 'hint_count' => array('integer', $requestsStatisticData->getRequestsCount()),
1270 'hint_points' => array('float', $requestsStatisticData->getRequestsPoints()),
1271 'answered' => array('integer', $isAnswered)
1272 );
1273
1274 if( $this->getStep() !== NULL )
1275 {
1276 $fieldData['step'] = array('integer', $this->getStep());
1277 }
1278
1279 $ilDB->insert('tst_test_result', $fieldData);
1280 }
1281
1282 });
1283// fau.
1284
1285 include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1286
1288 {
1290 sprintf(
1291 $this->lng->txtlng(
1292 "assessment", "log_user_answered_question", ilObjAssessmentFolder::_getLogLanguage()
1293 ),
1294 $reached_points
1295 ),
1296 $active_id,
1297 $this->getId()
1298 );
1299 }
1300
1301 // update test pass results
1302 self::_updateTestPassResults($active_id, $pass, $obligationsEnabled, $this->getProcessLocker());
1303
1304 // Update objective status
1305 include_once 'Modules/Course/classes/class.ilCourseObjectiveResult.php';
1306 ilCourseObjectiveResult::_updateObjectiveResult($ilUser->getId(),$active_id,$this->getId());
1307 }
1308
1317 final public function persistWorkingState($active_id, $pass = NULL, $obligationsEnabled = false, $authorized = true)
1318 {
1319 if( $pass === null )
1320 {
1321 require_once 'Modules/Test/classes/class.ilObjTest.php';
1322 $pass = ilObjTest::_getPass($active_id);
1323 }
1324
1325 if( !$this->validateSolutionSubmit() )
1326 {
1327 return false;
1328 }
1329
1330 $saveStatus = false;
1331
1332 $this->getProcessLocker()->executePersistWorkingStateLockOperation(function() use ($active_id, $pass, $authorized, $obligationsEnabled, &$saveStatus) {
1333
1334 $saveStatus = $this->saveWorkingData($active_id, $pass, $authorized);
1335
1336 if($authorized)
1337 {
1338// fau: testNav - remove an intermediate solution if the authorized solution is saved
1339// the intermediate solution would set the displayed question status as "editing ..."
1340 $this->removeIntermediateSolution($active_id, $pass);
1341// fau.
1342 $this->calculateResultsFromSolution($active_id, $pass, $obligationsEnabled);
1343 }
1344
1345 $this->reworkWorkingData($active_id, $pass, $obligationsEnabled, $authorized);
1346
1347 });
1348
1349 return $saveStatus;
1350 }
1351
1355 final public function persistPreviewState(ilAssQuestionPreviewSession $previewSession)
1356 {
1357 $this->savePreviewData($previewSession);
1358 return $this->validateSolutionSubmit();
1359 }
1360
1361 public function validateSolutionSubmit()
1362 {
1363 return true;
1364 }
1365
1375 abstract public function saveWorkingData($active_id, $pass = NULL, $authorized = true);
1376
1384 abstract protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized);
1385
1386 protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
1387 {
1388 $previewSession->setParticipantsSolution($this->getSolutionSubmit());
1389 }
1390
1392 public static function _updateTestResultCache($active_id, ilAssQuestionProcessLocker $processLocker = null)
1393 {
1394 global $ilDB;
1395
1396 include_once "./Modules/Test/classes/class.ilObjTest.php";
1397 include_once "./Modules/Test/classes/class.assMarkSchema.php";
1398
1399 $pass = ilObjTest::_getResultPass($active_id);
1400
1401 $query = "
1402 SELECT tst_pass_result.*
1403 FROM tst_pass_result
1404 WHERE active_fi = %s
1405 AND pass = %s
1406 ";
1407
1408 $result = $ilDB->queryF(
1409 $query, array('integer','integer'), array($active_id, $pass)
1410 );
1411
1412 $row = $ilDB->fetchAssoc($result);
1413
1414 $max = $row['maxpoints'];
1415 $reached = $row['points'];
1416
1417 $obligationsAnswered = (int)$row['obligations_answered'];
1418
1419 include_once "./Modules/Test/classes/class.assMarkSchema.php";
1420
1421 $percentage = (!$max) ? 0 : ($reached / $max) * 100.0;
1422
1423 $mark = ASS_MarkSchema::_getMatchingMarkFromActiveId($active_id, $percentage);
1424
1425 $isPassed = ( $mark["passed"] ? 1 : 0 );
1426 $isFailed = ( !$mark["passed"] ? 1 : 0 );
1427
1428 $userTestResultUpdateCallback = function() use ($ilDB, $active_id, $pass, $max, $reached, $isFailed, $isPassed, $obligationsAnswered, $row, $mark) {
1429
1430 $query = "
1431 DELETE FROM tst_result_cache
1432 WHERE active_fi = %s
1433 ";
1434 $ilDB->manipulateF(
1435 $query, array('integer'), array($active_id)
1436 );
1437
1438 $ilDB->insert('tst_result_cache', array(
1439 'active_fi'=> array('integer', $active_id),
1440 'pass'=> array('integer', strlen($pass) ? $pass : 0),
1441 'max_points'=> array('float', strlen($max) ? $max : 0),
1442 'reached_points'=> array('float', strlen($reached) ? $reached : 0),
1443 'mark_short'=> array('text', strlen($mark["short_name"]) ? $mark["short_name"] : " "),
1444 'mark_official'=> array('text', strlen($mark["official_name"]) ? $mark["official_name"] : " "),
1445 'passed'=> array('integer', $isPassed),
1446 'failed'=> array('integer', $isFailed),
1447 'tstamp'=> array('integer', time()),
1448 'hint_count'=> array('integer', $row['hint_count']),
1449 'hint_points'=> array('float', $row['hint_points']),
1450 'obligations_answered' => array('integer', $obligationsAnswered)
1451 ));
1452
1453 };
1454
1455 if(is_object($processLocker))
1456 {
1457 $processLocker->executeUserTestResultUpdateLockOperation($userTestResultUpdateCallback);
1458 }
1459 else
1460 {
1461 $userTestResultUpdateCallback();
1462 }
1463 }
1464
1466 public static function _updateTestPassResults($active_id, $pass, $obligationsEnabled = false, ilAssQuestionProcessLocker $processLocker = null, $test_obj_id = null)
1467 {
1468 global $ilDB;
1469
1470 include_once "./Modules/Test/classes/class.ilObjTest.php";
1471
1472 if( self::getResultGateway() !== null )
1473 {
1474 $data = self::getResultGateway()->getQuestionCountAndPointsForPassOfParticipant($active_id, $pass);
1475 $time = self::getResultGateway()->getWorkingTimeOfParticipantForPass($active_id, $pass);
1476 }
1477 else
1478 {
1481 }
1482
1483
1484 // update test pass results
1485
1486 $result = $ilDB->queryF("
1487 SELECT SUM(points) reachedpoints,
1488 SUM(hint_count) hint_count,
1489 SUM(hint_points) hint_points,
1490 COUNT(DISTINCT(question_fi)) answeredquestions
1491 FROM tst_test_result
1492 WHERE active_fi = %s
1493 AND pass = %s
1494 ",
1495 array('integer','integer'),
1496 array($active_id, $pass)
1497 );
1498
1499 if ($result->numRows() > 0)
1500 {
1501 if( $obligationsEnabled )
1502 {
1503 $query = '
1504 SELECT answered answ
1505 FROM tst_test_question
1506 INNER JOIN tst_active
1507 ON active_id = %s
1508 AND tst_test_question.test_fi = tst_active.test_fi
1509 LEFT JOIN tst_test_result
1510 ON tst_test_result.active_fi = %s
1511 AND tst_test_result.pass = %s
1512 AND tst_test_question.question_fi = tst_test_result.question_fi
1513 WHERE obligatory = 1';
1514
1515 $result_obligatory = $ilDB->queryF(
1516 $query, array('integer','integer','integer'), array($active_id, $active_id, $pass)
1517 );
1518
1519 $obligations_answered = 1;
1520
1521 while($row_obligatory = $ilDB->fetchAssoc($result_obligatory))
1522 {
1523 if(!(int)$row_obligatory['answ'])
1524 {
1525 $obligations_answered = 0;
1526 break;
1527 }
1528 }
1529 }
1530 else
1531 {
1532 $obligations_answered = 1;
1533 }
1534
1535 $row = $ilDB->fetchAssoc($result);
1536
1537 if( $row['hint_count'] === null ) $row['hint_count'] = 0;
1538 if( $row['hint_points'] === null ) $row['hint_points'] = 0;
1539
1540 $exam_identifier = ilObjTest::buildExamId( $active_id, $pass, $test_obj_id);
1541
1542 $updatePassResultCallback = function() use ($ilDB, $data, $active_id, $pass, $row, $time, $obligations_answered, $exam_identifier) {
1543
1545 $ilDB->replace('tst_pass_result',
1546 array(
1547 'active_fi' => array('integer', $active_id),
1548 'pass' => array('integer', strlen($pass) ? $pass : 0)),
1549 array(
1550 'points' => array('float', $row['reachedpoints'] ? $row['reachedpoints'] : 0),
1551 'maxpoints' => array('float', $data['points']),
1552 'questioncount' => array('integer', $data['count']),
1553 'answeredquestions' => array('integer', $row['answeredquestions']),
1554 'workingtime' => array('integer', $time),
1555 'tstamp' => array('integer', time()),
1556 'hint_count' => array('integer', $row['hint_count']),
1557 'hint_points' => array('float', $row['hint_points']),
1558 'obligations_answered' => array('integer', $obligations_answered),
1559 'exam_id' => array('text', $exam_identifier)
1560 )
1561 );
1562
1563 };
1564
1565 if(is_object($processLocker))
1566 {
1567 $processLocker->executeUserPassResultUpdateLockOperation($updatePassResultCallback);
1568 }
1569 else
1570 {
1571 $updatePassResultCallback();
1572 }
1573 }
1574
1576
1577 return array(
1578 'active_fi' => $active_id,
1579 'pass' => $pass,
1580 'points' => ($row["reachedpoints"]) ? $row["reachedpoints"] : 0,
1581 'maxpoints' => $data["points"],
1582 'questioncount' => $data["count"],
1583 'answeredquestions' => $row["answeredquestions"],
1584 'workingtime' => $time,
1585 'tstamp' => time(),
1586 'hint_count' => $row['hint_count'],
1587 'hint_points' => $row['hint_points'],
1588 'obligations_answered' => $obligations_answered,
1589 'exam_id' => $exam_identifier
1590 );
1591 }
1592
1600 public static function logAction($logtext = "", $active_id = "", $question_id = "")
1601 {
1602 $original_id = "";
1603 if( strlen($question_id) )
1604 {
1605 $original_id = self::_getOriginalId($question_id);
1606 }
1607
1608 require_once 'Modules/Test/classes/class.ilObjAssessmentFolder.php';
1609 require_once 'Modules/Test/classes/class.ilObjTest.php';
1610
1612 $GLOBALS['DIC']['ilUser']->getId(),
1614 $logtext,
1615 $question_id,
1617 );
1618 }
1619
1628 {
1629 $mediatempdir = CLIENT_WEB_DIR . "/assessment/temp";
1630 if (!@is_dir($mediatempdir)) ilUtil::createDirectory($mediatempdir);
1631 $temp_name = tempnam($mediatempdir, $name . "_____");
1632 $temp_name = str_replace("\\", "/", $temp_name);
1633 @unlink($temp_name);
1634 if (!ilUtil::moveUploadedFile($file, $name, $temp_name))
1635 {
1636 return FALSE;
1637 }
1638 else
1639 {
1640 return $temp_name;
1641 }
1642 }
1643
1650 return CLIENT_WEB_DIR . "/assessment/$this->obj_id/$this->id/solution/";
1651 }
1652
1659 function getJavaPath() {
1660 return CLIENT_WEB_DIR . "/assessment/$this->obj_id/$this->id/java/";
1661 }
1662
1669 function getImagePath($question_id = null, $object_id = null)
1670 {
1671 if( $question_id === null)
1672 {
1673 $question_id = $this->id;
1674 }
1675
1676 if( $object_id === null)
1677 {
1678 $object_id = $this->obj_id;
1679 }
1680
1681 return $this->buildImagePath($question_id, $object_id);
1682 }
1683
1684 public function buildImagePath($questionId, $parentObjectId)
1685 {
1686 return CLIENT_WEB_DIR . "/assessment/{$parentObjectId}/{$questionId}/images/";
1687 }
1688
1695 function getFlashPath()
1696 {
1697 return CLIENT_WEB_DIR . "/assessment/$this->obj_id/$this->id/flash/";
1698 }
1699
1707 {
1708 include_once "./Services/Utilities/classes/class.ilUtil.php";
1709 $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/java/";
1710 return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
1711 }
1712
1719 {
1720 include_once "./Services/Utilities/classes/class.ilUtil.php";
1721 $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/solution/";
1722 return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
1723 }
1724
1734 {
1735 if(!$this->export_image_path)
1736 {
1737 include_once "./Services/Utilities/classes/class.ilUtil.php";
1738 $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/images/";
1739 return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
1740 }
1741 else
1742 {
1744 }
1745 }
1746
1754 {
1755 include_once "./Services/Utilities/classes/class.ilUtil.php";
1756 $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/$this->obj_id/$this->id/flash/";
1757 return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
1758 }
1759
1760 // hey: prevPassSolutions - accept and prefer intermediate only from current pass
1761 public function getTestOutputSolutions($activeId, $pass)
1762 {
1763 // hey: refactored identifiers
1764 if( $this->getTestPresentationConfig()->isSolutionInitiallyPrefilled() )
1765 // hey.
1766 {
1767 return $this->getSolutionValues($activeId, $pass, true);
1768 }
1769
1770 return $this->getUserSolutionPreferingIntermediate($activeId, $pass);
1771 }
1772 // hey.
1773
1774 public function getUserSolutionPreferingIntermediate($active_id, $pass = NULL)
1775 {
1776 $solution = $this->getSolutionValues($active_id, $pass, false);
1777
1778 if( !count($solution) )
1779 {
1780 $solution = $this->getSolutionValues($active_id, $pass, true);
1781 }
1782
1783 return $solution;
1784 }
1785
1789 public function getSolutionValues($active_id, $pass = NULL, $authorized = true)
1790 {
1791 global $ilDB;
1792
1793 if (is_null($pass))
1794 {
1795 $pass = $this->getSolutionMaxPass($active_id);
1796 }
1797
1798 if( $this->getStep() !== NULL )
1799 {
1800 $query = "
1801 SELECT *
1802 FROM tst_solutions
1803 WHERE active_fi = %s
1804 AND question_fi = %s
1805 AND pass = %s
1806 AND step = %s
1807 AND authorized = %s
1808 ORDER BY solution_id";
1809
1810 $result = $ilDB->queryF($query, array('integer', 'integer', 'integer', 'integer', 'integer'),
1811 array($active_id, $this->getId(), $pass, $this->getStep(), (int)$authorized)
1812 );
1813 }
1814 else
1815 {
1816 $query = "
1817 SELECT *
1818 FROM tst_solutions
1819 WHERE active_fi = %s
1820 AND question_fi = %s
1821 AND pass = %s
1822 AND authorized = %s
1823 ORDER BY solution_id
1824 ";
1825
1826 $result = $ilDB->queryF($query, array('integer', 'integer', 'integer', 'integer'),
1827 array($active_id, $this->getId(), $pass, (int)$authorized)
1828 );
1829 }
1830
1831 $values = array();
1832
1833 while( $row = $ilDB->fetchAssoc($result) )
1834 {
1835 $values[] = $row;
1836 }
1837
1838 return $values;
1839 }
1840
1847 public function isInUse($question_id = "")
1848 {
1849 global $ilDB;
1850
1851 if ($question_id < 1) $question_id = $this->getId();
1852 $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",
1853 array('integer'),
1854 array($question_id)
1855 );
1856 $row = $ilDB->fetchAssoc($result);
1857 $count = $row["question_count"];
1858
1859 $result = $ilDB->queryF("
1860 SELECT tst_active.test_fi
1861 FROM qpl_questions
1862 INNER JOIN tst_test_rnd_qst ON tst_test_rnd_qst.question_fi = qpl_questions.question_id
1863 INNER JOIN tst_active ON tst_active.active_id = tst_test_rnd_qst.active_fi
1864 WHERE qpl_questions.original_id = %s
1865 GROUP BY tst_active.test_fi",
1866 array('integer'),
1867 array($question_id)
1868 );
1869 $count += $result->numRows();
1870
1871 return $count;
1872 }
1873
1880 function isClone($question_id = "")
1881 {
1882 global $ilDB;
1883
1884 if ($question_id < 1) $question_id = $this->id;
1885 $result = $ilDB->queryF("SELECT original_id FROM qpl_questions WHERE question_id = %s",
1886 array('integer'),
1887 array($question_id)
1888 );
1889 $row = $ilDB->fetchAssoc($result);
1890 return ($row["original_id"] > 0) ? TRUE : FALSE;
1891 }
1892
1899 function pcArrayShuffle($array)
1900 {
1901 $keys = array_keys($array);
1902 shuffle($keys);
1903 $result = array();
1904 foreach ($keys as $key)
1905 {
1906 $result[$key] = $array[$key];
1907 }
1908 return $result;
1909 }
1910
1916 public static function getQuestionTypeFromDb($question_id)
1917 {
1918 global $ilDB;
1919
1920 $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",
1921 array('integer'),
1922 array($question_id)
1923 );
1924 $data = $ilDB->fetchAssoc($result);
1925 return $data["type_tag"];
1926 }
1927
1935 {
1936 return "";
1937 }
1938
1946 {
1947 return "";
1948 }
1949
1956 function deleteAnswers($question_id)
1957 {
1958 global $ilDB;
1959 $answer_table_name = $this->getAnswerTableName();
1960
1961 if( !is_array($answer_table_name) )
1962 {
1963 $answer_table_name = array($answer_table_name);
1964 }
1965
1966 foreach ($answer_table_name as $table)
1967 {
1968 if (strlen($table))
1969 {
1970 $affectedRows = $ilDB->manipulateF("DELETE FROM $table WHERE question_fi = %s",
1971 array('integer'),
1972 array($question_id)
1973 );
1974 }
1975 }
1976 }
1977
1984 function deleteAdditionalTableData($question_id)
1985 {
1986 global $ilDB;
1987
1988 $additional_table_name = $this->getAdditionalTableName();
1989
1990 if( !is_array($additional_table_name) )
1991 {
1992 $additional_table_name = array($additional_table_name);
1993 }
1994
1995 foreach ($additional_table_name as $table)
1996 {
1997 if (strlen($table))
1998 {
1999 $affectedRows = $ilDB->manipulateF("DELETE FROM $table WHERE question_fi = %s",
2000 array('integer'),
2001 array($question_id)
2002 );
2003 }
2004 }
2005 }
2006
2013 protected function deletePageOfQuestion($question_id)
2014 {
2015 include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
2016 $page = new ilAssQuestionPage($question_id);
2017 $page->delete();
2018 return true;
2019 }
2020
2027 public function delete($question_id)
2028 {
2029 global $ilDB, $ilLog;
2030
2031 if ($question_id < 1) return true; // nothing to do
2032
2033 $result = $ilDB->queryF("SELECT obj_fi FROM qpl_questions WHERE question_id = %s",
2034 array('integer'),
2035 array($question_id)
2036 );
2037 if ($result->numRows() == 1)
2038 {
2039 $row = $ilDB->fetchAssoc($result);
2040 $obj_id = $row["obj_fi"];
2041 }
2042 else
2043 {
2044 return true; // nothing to do
2045 }
2046 try
2047 {
2048 $this->deletePageOfQuestion($question_id);
2049 }
2050 catch (Exception $e)
2051 {
2052 $ilLog->write("EXCEPTION: Could not delete page of question $question_id: $e");
2053 return false;
2054 }
2055
2056 $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_questions WHERE question_id = %s",
2057 array('integer'),
2058 array($question_id)
2059 );
2060 if ($affectedRows == 0) return false;
2061
2062 try
2063 {
2064 $this->deleteAdditionalTableData($question_id);
2065 $this->deleteAnswers($question_id);
2066 $this->feedbackOBJ->deleteGenericFeedbacks($question_id, $this->isAdditionalContentEditingModePageObject());
2067 $this->feedbackOBJ->deleteSpecificAnswerFeedbacks($question_id, $this->isAdditionalContentEditingModePageObject());
2068 }
2069 catch (Exception $e)
2070 {
2071 $ilLog->write("EXCEPTION: Could not delete additional table data of question $question_id: $e");
2072 return false;
2073 }
2074
2075 try
2076 {
2077 // delete the question in the tst_test_question table (list of test questions)
2078 $affectedRows = $ilDB->manipulateF("DELETE FROM tst_test_question WHERE question_fi = %s",
2079 array('integer'),
2080 array($question_id)
2081 );
2082 }
2083 catch (Exception $e)
2084 {
2085 $ilLog->write("EXCEPTION: Could not delete delete question $question_id from a test: $e");
2086 return false;
2087 }
2088
2089 try
2090 {
2091 // delete suggested solutions contained in the question
2092 $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s",
2093 array('integer'),
2094 array($question_id)
2095 );
2096 }
2097 catch (Exception $e)
2098 {
2099 $ilLog->write("EXCEPTION: Could not delete suggested solutions of question $question_id: $e");
2100 return false;
2101 }
2102
2103 try
2104 {
2105 $directory = CLIENT_WEB_DIR . "/assessment/" . $obj_id . "/$question_id";
2106 if (preg_match("/\d+/", $obj_id) and preg_match("/\d+/", $question_id) and is_dir($directory))
2107 {
2108 include_once "./Services/Utilities/classes/class.ilUtil.php";
2109 ilUtil::delDir($directory);
2110 }
2111 }
2112 catch (Exception $e)
2113 {
2114 $ilLog->write("EXCEPTION: Could not delete question file directory $directory of question $question_id: $e");
2115 return false;
2116 }
2117
2118 try
2119 {
2120 include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
2121 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $question_id);
2122 // remaining usages are not in text anymore -> delete them
2123 // and media objects (note: delete method of ilObjMediaObject
2124 // checks whether object is used in another context; if yes,
2125 // the object is not deleted!)
2126 foreach($mobs as $mob)
2127 {
2128 ilObjMediaObject::_removeUsage($mob, "qpl:html", $question_id);
2129 if (ilObjMediaObject::_exists($mob))
2130 {
2131 $mob_obj = new ilObjMediaObject($mob);
2132 $mob_obj->delete();
2133 }
2134 }
2135 }
2136 catch (Exception $e)
2137 {
2138 $ilLog->write("EXCEPTION: Error deleting the media objects of question $question_id: $e");
2139 return false;
2140 }
2141
2142 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintTracking.php';
2143 ilAssQuestionHintTracking::deleteRequestsByQuestionIds(array($question_id));
2144
2145 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintList.php';
2147
2148 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionSkillAssignmentList.php';
2149 $assignmentList = new ilAssQuestionSkillAssignmentList($ilDB);
2150 $assignmentList->setParentObjId($obj_id);
2151 $assignmentList->setQuestionIdFilter($question_id);
2152 $assignmentList->loadFromDb();
2153 foreach($assignmentList->getAssignmentsByQuestionId($question_id) as $assignment)
2154 {
2155 /* @var ilAssQuestionSkillAssignment $assignment */
2156 $assignment->deleteFromDb();
2157 }
2158
2160
2161 try
2162 {
2163 // update question count of question pool
2164 include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
2166 }
2167 catch (Exception $e)
2168 {
2169 $ilLog->write("EXCEPTION: Error updating the question pool question count of question pool " . $this->getObjId() . " when deleting question $question_id: $e");
2170 return false;
2171 }
2172
2173 $this->notifyQuestionDeleted($this);
2174
2175 return true;
2176 }
2177
2178 private function deleteTaxonomyAssignments()
2179 {
2180 require_once 'Services/Taxonomy/classes/class.ilObjTaxonomy.php';
2181 require_once 'Services/Taxonomy/classes/class.ilTaxNodeAssignment.php';
2182 $taxIds = ilObjTaxonomy::getUsageOfObject($this->getObjId());
2183
2184 foreach($taxIds as $taxId)
2185 {
2186 $taxNodeAssignment = new ilTaxNodeAssignment('qpl', $this->getObjId(), 'quest', $taxId);
2187 $taxNodeAssignment->deleteAssignmentsOfItem($this->getId());
2188 }
2189 }
2190
2195 {
2196 return $this->_getTotalAnswers($this->id);
2197 }
2198
2205 function _getTotalAnswers($a_q_id)
2206 {
2207 global $ilDB;
2208
2209 // get all question references to the question id
2210 $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id = %s OR question_id = %s",
2211 array('integer','integer'),
2212 array($a_q_id, $a_q_id)
2213 );
2214 if ($result->numRows() == 0)
2215 {
2216 return 0;
2217 }
2218 $found_id = array();
2219 while ($row = $ilDB->fetchAssoc($result))
2220 {
2221 array_push($found_id, $row["question_id"]);
2222 }
2223
2224 $result = $ilDB->query("SELECT * FROM tst_test_result WHERE " . $ilDB->in('question_fi', $found_id, false, 'integer'));
2225
2226 return $result->numRows();
2227 }
2228
2229
2236 public static function _getTotalRightAnswers($a_q_id)
2237 {
2238 global $ilDB;
2239 $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id = %s OR question_id = %s",
2240 array('integer','integer'),
2241 array($a_q_id, $a_q_id)
2242 );
2243 if ($result->numRows() == 0)
2244 {
2245 return 0;
2246 }
2247 $found_id = array();
2248 while ($row = $ilDB->fetchAssoc($result))
2249 {
2250 array_push($found_id, $row["question_id"]);
2251 }
2252 $result = $ilDB->query("SELECT * FROM tst_test_result WHERE " . $ilDB->in('question_fi', $found_id, false, 'integer'));
2253 $answers = array();
2254 while ($row = $ilDB->fetchAssoc($result))
2255 {
2256 $reached = $row["points"];
2257 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
2258 $max = assQuestion::_getMaximumPoints($row["question_fi"]);
2259 array_push($answers, array("reached" => $reached, "max" => $max));
2260 }
2261 $max = 0.0;
2262 $reached = 0.0;
2263 foreach ($answers as $key => $value)
2264 {
2265 $max += $value["max"];
2266 $reached += $value["reached"];
2267 }
2268 if ($max > 0)
2269 {
2270 return $reached / $max;
2271 }
2272 else
2273 {
2274 return 0;
2275 }
2276 }
2277
2283 static function _getTitle($a_q_id)
2284 {
2285 global $ilDB;
2286 $result = $ilDB->queryF("SELECT title FROM qpl_questions WHERE question_id = %s",
2287 array('integer'),
2288 array($a_q_id)
2289 );
2290 if ($result->numRows() == 1)
2291 {
2292 $row = $ilDB->fetchAssoc($result);
2293 return $row["title"];
2294 }
2295 else
2296 {
2297 return "";
2298 }
2299 }
2300
2306 static function _getQuestionText($a_q_id)
2307 {
2308 global $ilDB;
2309 $result = $ilDB->queryF("SELECT question_text FROM qpl_questions WHERE question_id = %s",
2310 array('integer'),
2311 array($a_q_id)
2312 );
2313 if ($result->numRows() == 1)
2314 {
2315 $row = $ilDB->fetchAssoc($result);
2316 return $row["question_text"];
2317 }
2318 else
2319 {
2320 return "";
2321 }
2322 }
2323
2324 public static function isFileAvailable($file)
2325 {
2326 if( !file_exists($file) )
2327 {
2328 return false;
2329 }
2330
2331 if( !is_file($file) )
2332 {
2333 return false;
2334 }
2335
2336 if( !is_readable($file) )
2337 {
2338 return false;
2339 }
2340
2341 return true;
2342 }
2343
2345 {
2346 include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
2347 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $a_q_id);
2348 foreach ($mobs as $mob)
2349 {
2350 ilObjMediaObject::_saveUsage($mob, "qpl:html", $this->getId());
2351 }
2352 }
2353
2355 {
2356 include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
2357 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
2358 foreach ($mobs as $mob)
2359 {
2360 ilObjMediaObject::_saveUsage($mob, "qpl:html", $this->original_id);
2361 }
2362 }
2363
2368 {
2369 $qpl_id = $this->getObjId();
2370
2371 include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
2372 $this->page = new ilAssQuestionPage(0);
2373 $this->page->setId($this->getId());
2374 $this->page->setParentId($qpl_id);
2375 $this->page->setXMLContent("<PageObject><PageContent>".
2376 "<Question QRef=\"il__qst_".$this->getId()."\"/>".
2377 "</PageContent></PageObject>");
2378 $this->page->create();
2379 }
2380
2381 function copyPageOfQuestion($a_q_id)
2382 {
2383 if ($a_q_id > 0)
2384 {
2385 include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
2386 $page = new ilAssQuestionPage($a_q_id);
2387
2388 $xml = str_replace("il__qst_".$a_q_id, "il__qst_".$this->id, $page->getXMLContent());
2389 $this->page->setXMLContent($xml);
2390 $this->page->updateFromXML();
2391 }
2392 }
2393
2395 {
2396 include_once "./Modules/TestQuestionPool/classes/class.ilAssQuestionPage.php";
2397 $page = new ilAssQuestionPage($this->id);
2398 return $page->getXMLContent();
2399 }
2400
2406 public static function _getQuestionType($question_id)
2407 {
2408 global $ilDB;
2409
2410 if ($question_id < 1) return "";
2411 $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",
2412 array('integer'),
2413 array($question_id)
2414 );
2415 if ($result->numRows() == 1)
2416 {
2417 $data = $ilDB->fetchAssoc($result);
2418 return $data["type_tag"];
2419 }
2420 else
2421 {
2422 return "";
2423 }
2424 }
2425
2433 public static function _getQuestionTitle($question_id)
2434 {
2435 global $ilDB;
2436
2437 if ($question_id < 1) return "";
2438
2439 $result = $ilDB->queryF("SELECT title FROM qpl_questions WHERE qpl_questions.question_id = %s",
2440 array('integer'),
2441 array($question_id)
2442 );
2443 if ($result->numRows() == 1)
2444 {
2445 $data = $ilDB->fetchAssoc($result);
2446 return $data["title"];
2447 }
2448 else
2449 {
2450 return "";
2451 }
2452 }
2453
2455 {
2456 $this->original_id = $original_id;
2457 }
2458
2459 function getOriginalId()
2460 {
2461 return $this->original_id;
2462 }
2463
2464 protected static $imageSourceFixReplaceMap = array(
2465 'ok.svg' => 'ok.png', 'not_ok.svg' => 'not_ok.png',
2466 'checkbox_checked.svg' => 'checkbox_checked.png',
2467 'checkbox_unchecked.svg' => 'checkbox_unchecked.png',
2468 'radiobutton_checked.svg' => 'radiobutton_checked.png',
2469 'radiobutton_unchecked.svg' => 'radiobutton_unchecked.png'
2470 );
2471
2472 public function fixSvgToPng($imageFilenameContainingString)
2473 {
2474 $needles = array_keys(self::$imageSourceFixReplaceMap);
2475 $replacements = array_values(self::$imageSourceFixReplaceMap);
2476 return str_replace($needles, $replacements, $imageFilenameContainingString);
2477 }
2478
2479
2481 {
2482 $matches = null;
2483 if( preg_match_all('/src="(.*?)"/m', $html, $matches) )
2484 {
2485 $sources = $matches[1];
2486
2487 $needleReplacementMap = array();
2488
2489 foreach($sources as $src)
2490 {
2491 $file = ilUtil::removeTrailingPathSeparators( ILIAS_ABSOLUTE_PATH ) . DIRECTORY_SEPARATOR . $src;
2492
2493 if( file_exists($file) )
2494 {
2495 continue;
2496 }
2497
2498 $levels = explode(DIRECTORY_SEPARATOR, $src);
2499 if( count($levels) < 5 || $levels[0] != 'Customizing' || $levels[2] != 'skin' )
2500 {
2501 continue;
2502 }
2503
2504 $component = '';
2505
2506 if( $levels[4] == 'Modules' || $levels[4] == 'Services' )
2507 {
2508 $component = $levels[4] . DIRECTORY_SEPARATOR . $levels[5];
2509 }
2510
2511 $needleReplacementMap[$src] = ilUtil::getImagePath(basename($src), $component);
2512 }
2513
2514 if( count($needleReplacementMap) )
2515 {
2516 $html = str_replace(array_keys($needleReplacementMap), array_values($needleReplacementMap), $html);
2517 }
2518 }
2519
2520 return $html;
2521 }
2522
2529 function loadFromDb($question_id)
2530 {
2531 global $ilDB;
2532
2533 $result = $ilDB->queryF(
2534 "SELECT external_id FROM qpl_questions WHERE question_id = %s",
2535 array("integer"),
2536 array($question_id)
2537 );
2538 if($result->numRows() == 1)
2539 {
2540 $data = $ilDB->fetchAssoc($result);
2541 $this->external_id = $data['external_id'];
2542 }
2543
2544 $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s",
2545 array('integer'),
2546 array($this->getId())
2547 );
2548 $this->suggested_solutions = array();
2549 if ($result->numRows())
2550 {
2551 include_once("./Services/RTE/classes/class.ilRTE.php");
2552 while ($row = $ilDB->fetchAssoc($result))
2553 {
2554 $value = (is_array(unserialize($row["value"]))) ? unserialize($row["value"]) : ilRTE::_replaceMediaObjectImageSrc($row["value"], 1);
2555 $this->suggested_solutions[$row["subquestion_index"]] = array(
2556 "type" => $row["type"],
2557 "value" => $value,
2558 "internal_link" => $row["internal_link"],
2559 "import_id" => $row["import_id"]
2560 );
2561 }
2562 }
2563 }
2564
2571 public function createNewQuestion($a_create_page = true)
2572 {
2573 global $ilDB, $ilUser;
2574
2575 $complete = "0";
2576 $estw_time = $this->getEstimatedWorkingTime();
2577 $estw_time = sprintf("%02d:%02d:%02d", $estw_time['h'], $estw_time['m'], $estw_time['s']);
2578 $obj_id = ($this->getObjId() <= 0) ? (ilObject::_lookupObjId((strlen($_GET["ref_id"])) ? $_GET["ref_id"] : $_POST["sel_qpl"])) : $this->getObjId();
2579 if ($obj_id > 0)
2580 {
2581 if($a_create_page)
2582 {
2583 $tstamp = 0;
2584 }
2585 else
2586 {
2587 // question pool must not try to purge
2588 $tstamp = time();
2589 }
2590
2591 $next_id = $ilDB->nextId('qpl_questions');
2592 $affectedRows = $ilDB->insert("qpl_questions", array(
2593 "question_id" => array("integer", $next_id),
2594 "question_type_fi" => array("integer", $this->getQuestionTypeID()),
2595 "obj_fi" => array("integer", $obj_id),
2596 "title" => array("text", NULL),
2597 "description" => array("text", NULL),
2598 "author" => array("text", $this->getAuthor()),
2599 "owner" => array("integer", $ilUser->getId()),
2600 "question_text" => array("clob", NULL),
2601 "points" => array("float", 0),
2602 "nr_of_tries" => array("integer", $this->getDefaultNrOfTries()), // #10771
2603 "working_time" => array("text", $estw_time),
2604 "complete" => array("text", $complete),
2605 "created" => array("integer", time()),
2606 "original_id" => array("integer", NULL),
2607 "tstamp" => array("integer", $tstamp),
2608 "external_id" => array("text", $this->getExternalId()),
2609 'add_cont_edit_mode' => array('text', $this->getAdditionalContentEditingMode())
2610 ));
2611 $this->setId($next_id);
2612
2613 if($a_create_page)
2614 {
2615 // create page object of question
2616 $this->createPageObject();
2617 }
2618 }
2619
2620 $this->notifyQuestionCreated();
2621
2622 return $this->getId();
2623 }
2624
2626 {
2627 global $ilDB;
2628
2629 $estw_time = $this->getEstimatedWorkingTime();
2630 $estw_time = sprintf("%02d:%02d:%02d", $estw_time['h'], $estw_time['m'], $estw_time['s']);
2631
2632 // cleanup RTE images which are not inserted into the question text
2633 include_once("./Services/RTE/classes/class.ilRTE.php");
2634 if ($this->getId() == -1)
2635 {
2636 // Neuen Datensatz schreiben
2637 $next_id = $ilDB->nextId('qpl_questions');
2638 $affectedRows = $ilDB->insert("qpl_questions", array(
2639 "question_id" => array("integer", $next_id),
2640 "question_type_fi" => array("integer", $this->getQuestionTypeID()),
2641 "obj_fi" => array("integer", $this->getObjId()),
2642 "title" => array("text", $this->getTitle()),
2643 "description" => array("text", $this->getComment()),
2644 "author" => array("text", $this->getAuthor()),
2645 "owner" => array("integer", $this->getOwner()),
2646 "question_text" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getQuestion(), 0)),
2647 "points" => array("float", $this->getMaximumPoints()),
2648 "working_time" => array("text", $estw_time),
2649 "nr_of_tries" => array("integer", $this->getNrOfTries()),
2650 "created" => array("integer", time()),
2651 "original_id" => array("integer", ($original_id) ? $original_id : NULL),
2652 "tstamp" => array("integer", time()),
2653 "external_id" => array("text", $this->getExternalId()),
2654 'add_cont_edit_mode' => array('text', $this->getAdditionalContentEditingMode())
2655 ));
2656 $this->setId($next_id);
2657 // create page object of question
2658 $this->createPageObject();
2659 }
2660 else
2661 {
2662 // Vorhandenen Datensatz aktualisieren
2663 $affectedRows = $ilDB->update("qpl_questions", array(
2664 "obj_fi" => array("integer", $this->getObjId()),
2665 "title" => array("text", $this->getTitle()),
2666 "description" => array("text", $this->getComment()),
2667 "author" => array("text", $this->getAuthor()),
2668 "question_text" => array("clob", ilRTE::_replaceMediaObjectImageSrc($this->getQuestion(), 0)),
2669 "points" => array("float", $this->getMaximumPoints()),
2670 "nr_of_tries" => array("integer", $this->getNrOfTries()),
2671 "working_time" => array("text", $estw_time),
2672 "tstamp" => array("integer", time()),
2673 'complete' => array('integer', $this->isComplete()),
2674 "external_id" => array("text", $this->getExternalId())
2675 ), array(
2676 "question_id" => array("integer", $this->getId())
2677 ));
2678 }
2679 }
2680
2687 function saveToDb($original_id = "")
2688 {
2689 global $ilDB;
2690
2691 $this->updateSuggestedSolutions();
2692
2693 // remove unused media objects from ILIAS
2694 $this->cleanupMediaObjectUsage();
2695
2696 $complete = "0";
2697 if ($this->isComplete())
2698 {
2699 $complete = "1";
2700 }
2701
2702 // update the question time stamp and completion status
2703 $affectedRows = $ilDB->manipulateF("UPDATE qpl_questions SET tstamp = %s, owner = %s, complete = %s WHERE question_id = %s",
2704 array('integer','integer', 'integer','text'),
2705 array(time(), ($this->getOwner() <= 0) ? $this->ilias->account->id : $this->getOwner(), $complete, $this->getId())
2706 );
2707
2708 // update question count of question pool
2709 include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
2711
2712 $this->notifyQuestionEdited($this);
2713 }
2714
2718 public function setNewOriginalId($newId) {
2719 self::saveOriginalId($this->getId(), $newId);
2720 }
2721
2722 public static function saveOriginalId($questionId, $originalId)
2723 {
2724 $query = "UPDATE qpl_questions SET tstamp = %s, original_id = %s WHERE question_id = %s";
2725
2726 $GLOBALS['DIC']['ilDB']->manipulateF(
2727 $query, array('integer','integer', 'text'), array(time(), $originalId, $questionId)
2728 );
2729 }
2730
2731 public static function resetOriginalId($questionId)
2732 {
2733 $query = "UPDATE qpl_questions SET tstamp = %s, original_id = NULL WHERE question_id = %s";
2734
2735 $GLOBALS['DIC']['ilDB']->manipulateF(
2736 $query, array('integer', 'text'), array(time(), $questionId)
2737 );
2738 }
2739
2743 protected function onDuplicate($originalParentId, $originalQuestionId, $duplicateParentId, $duplicateQuestionId)
2744 {
2745 $this->duplicateSuggestedSolutionFiles($originalParentId, $originalQuestionId);
2746
2747 // duplicate question feeback
2748 $this->feedbackOBJ->duplicateFeedback($originalQuestionId, $duplicateQuestionId);
2749
2750 // duplicate question hints
2751 $this->duplicateQuestionHints($originalQuestionId, $duplicateQuestionId);
2752
2753 // duplicate skill assignments
2754 $this->duplicateSkillAssignments($originalParentId, $originalQuestionId, $duplicateParentId, $duplicateQuestionId);
2755 }
2756
2757 protected function beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
2758 {
2759
2760 }
2761
2762 protected function afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
2763 {
2764 // sync question feeback
2765 $this->feedbackOBJ->syncFeedback($origQuestionId, $dupQuestionId);
2766 }
2767
2771 protected function onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $targetQuestionId)
2772 {
2773 $this->copySuggestedSolutionFiles($sourceParentId, $sourceQuestionId);
2774
2775 // duplicate question feeback
2776 $this->feedbackOBJ->duplicateFeedback($sourceQuestionId, $targetQuestionId);
2777
2778 // duplicate question hints
2779 $this->duplicateQuestionHints($sourceQuestionId, $targetQuestionId);
2780
2781 // duplicate skill assignments
2782 $this->duplicateSkillAssignments($sourceParentId, $sourceQuestionId, $targetParentId, $targetQuestionId);
2783 }
2784
2789 {
2790 global $ilDB;
2791 // delete the links in the qpl_sol_sug table
2792 $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s",
2793 array('integer'),
2794 array($this->getId())
2795 );
2796 // delete the links in the int_link table
2797 include_once "./Services/Link/classes/class.ilInternalLink.php";
2799 $this->suggested_solutions = array();
2801 }
2802
2810 function getSuggestedSolution($subquestion_index = 0)
2811 {
2812 if (array_key_exists($subquestion_index, $this->suggested_solutions))
2813 {
2814 return $this->suggested_solutions[$subquestion_index];
2815 }
2816 else
2817 {
2818 return array();
2819 }
2820 }
2821
2830 function getSuggestedSolutionTitle($subquestion_index = 0)
2831 {
2832 if (array_key_exists($subquestion_index, $this->suggested_solutions))
2833 {
2834 $title = $this->suggested_solutions[$subquestion_index]["internal_link"];
2835 // TO DO: resolve internal link an get link type and title
2836 }
2837 else
2838 {
2839 $title = "";
2840 }
2841 return $title;
2842 }
2843
2853 function setSuggestedSolution($solution_id = "", $subquestion_index = 0, $is_import = false)
2854 {
2855 if (strcmp($solution_id, "") != 0)
2856 {
2857 $import_id = "";
2858 if ($is_import)
2859 {
2860 $import_id = $solution_id;
2861 $solution_id = $this->_resolveInternalLink($import_id);
2862 }
2863 $this->suggested_solutions[$subquestion_index] = array(
2864 "internal_link" => $solution_id,
2865 "import_id" => $import_id
2866 );
2867 }
2868 }
2869
2873 protected function duplicateSuggestedSolutionFiles($parent_id, $question_id)
2874 {
2875 global $ilLog;
2876
2877 foreach ($this->suggested_solutions as $index => $solution)
2878 {
2879 if (strcmp($solution["type"], "file") == 0)
2880 {
2881 $filepath = $this->getSuggestedSolutionPath();
2882 $filepath_original = str_replace(
2883 "/{$this->obj_id}/{$this->id}/solution",
2884 "/$parent_id/$question_id/solution",
2885 $filepath
2886 );
2887 if (!file_exists($filepath))
2888 {
2889 ilUtil::makeDirParents($filepath);
2890 }
2891 $filename = $solution["value"]["name"];
2892 if (strlen($filename))
2893 {
2894 if (!copy($filepath_original . $filename, $filepath . $filename))
2895 {
2896 $ilLog->write("File could not be duplicated!!!!", $ilLog->ERROR);
2897 $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
2898 }
2899 }
2900 }
2901 }
2902 }
2903
2908 {
2909 global $ilLog;
2910
2911 $filepath = $this->getSuggestedSolutionPath();
2912 $filepath_original = str_replace("/$this->id/solution", "/$original_id/solution", $filepath);
2913 ilUtil::delDir($filepath_original);
2914 foreach ($this->suggested_solutions as $index => $solution)
2915 {
2916 if (strcmp($solution["type"], "file") == 0)
2917 {
2918 if (!file_exists($filepath_original))
2919 {
2920 ilUtil::makeDirParents($filepath_original);
2921 }
2922 $filename = $solution["value"]["name"];
2923 if (strlen($filename))
2924 {
2925 if (!@copy($filepath . $filename, $filepath_original . $filename))
2926 {
2927 $ilLog->write("File could not be duplicated!!!!", $ilLog->ERROR);
2928 $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
2929 }
2930 }
2931 }
2932 }
2933 }
2934
2935 protected function copySuggestedSolutionFiles($source_questionpool_id, $source_question_id)
2936 {
2937 global $ilLog;
2938
2939 foreach ($this->suggested_solutions as $index => $solution)
2940 {
2941 if (strcmp($solution["type"], "file") == 0)
2942 {
2943 $filepath = $this->getSuggestedSolutionPath();
2944 $filepath_original = str_replace("/$this->obj_id/$this->id/solution", "/$source_questionpool_id/$source_question_id/solution", $filepath);
2945 if (!file_exists($filepath))
2946 {
2947 ilUtil::makeDirParents($filepath);
2948 }
2949 $filename = $solution["value"]["name"];
2950 if (strlen($filename))
2951 {
2952 if (!copy($filepath_original . $filename, $filepath . $filename))
2953 {
2954 $ilLog->write("File could not be copied!!!!", $ilLog->ERROR);
2955 $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
2956 }
2957 }
2958 }
2959 }
2960 }
2961
2965 public function updateSuggestedSolutions($original_id = "")
2966 {
2967 global $ilDB;
2968
2969 $id = (strlen($original_id) && is_numeric($original_id)) ? $original_id : $this->getId();
2970 include_once "./Services/Link/classes/class.ilInternalLink.php";
2971 $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s",
2972 array('integer'),
2973 array($id)
2974 );
2976 include_once("./Services/RTE/classes/class.ilRTE.php");
2977 foreach ($this->suggested_solutions as $index => $solution)
2978 {
2979 $next_id = $ilDB->nextId('qpl_sol_sug');
2981 $ilDB->insert('qpl_sol_sug', array(
2982 'suggested_solution_id' => array( 'integer', $next_id ),
2983 'question_fi' => array( 'integer', $id ),
2984 'type' => array( 'text', $solution['type'] ),
2985 'value' => array( 'clob', ilRTE::_replaceMediaObjectImageSrc( (is_array( $solution['value'] ) ) ? serialize( $solution[ 'value' ] ) : $solution['value'], 0 ) ),
2986 'internal_link' => array( 'text', $solution['internal_link'] ),
2987 'import_id' => array( 'text', null ),
2988 'subquestion_index' => array( 'integer', $index ),
2989 'tstamp' => array( 'integer', time() ),
2990 )
2991 );
2992 if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
2993 {
2994 ilInternalLink::_saveLink("qst", $id, $matches[2], $matches[3], $matches[1]);
2995 }
2996 }
2997 if (strlen($original_id) && is_numeric($original_id)) $this->syncSuggestedSolutionFiles($id);
2998 $this->cleanupMediaObjectUsage();
2999 }
3000
3010 function saveSuggestedSolution($type, $solution_id = "", $subquestion_index = 0, $value = "")
3011 {
3012 global $ilDB;
3013
3014 $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_sol_sug WHERE question_fi = %s AND subquestion_index = %s",
3015 array("integer", "integer"),
3016 array(
3017 $this->getId(),
3018 $subquestion_index
3019 )
3020 );
3021
3022 $next_id = $ilDB->nextId('qpl_sol_sug');
3023 include_once("./Services/RTE/classes/class.ilRTE.php");
3025 $affectedRows = $ilDB->insert('qpl_sol_sug', array(
3026 'suggested_solution_id' => array( 'integer', $next_id ),
3027 'question_fi' => array( 'integer', $this->getId() ),
3028 'type' => array( 'text', $type ),
3029 'value' => array( 'clob', ilRTE::_replaceMediaObjectImageSrc( (is_array( $value ) ) ? serialize( $value ) : $value, 0 ) ),
3030 'internal_link' => array( 'text', $solution_id ),
3031 'import_id' => array( 'text', null ),
3032 'subquestion_index' => array( 'integer', $subquestion_index ),
3033 'tstamp' => array( 'integer', time() ),
3034 )
3035 );
3036 if ($affectedRows == 1)
3037 {
3038 $this->suggested_solutions[$subquestion_index] = array(
3039 "type" => $type,
3040 "value" => $value,
3041 "internal_link" => $solution_id,
3042 "import_id" => ""
3043 );
3044 }
3045 $this->cleanupMediaObjectUsage();
3046 }
3047
3048 function _resolveInternalLink($internal_link)
3049 {
3050 if (preg_match("/il_(\d+)_(\w+)_(\d+)/", $internal_link, $matches))
3051 {
3052 include_once "./Services/Link/classes/class.ilInternalLink.php";
3053 include_once "./Modules/LearningModule/classes/class.ilLMObject.php";
3054 include_once "./Modules/Glossary/classes/class.ilGlossaryTerm.php";
3055 switch ($matches[2])
3056 {
3057 case "lm":
3058 $resolved_link = ilLMObject::_getIdForImportId($internal_link);
3059 break;
3060 case "pg":
3061 $resolved_link = ilInternalLink::_getIdForImportId("PageObject", $internal_link);
3062 break;
3063 case "st":
3064 $resolved_link = ilInternalLink::_getIdForImportId("StructureObject", $internal_link);
3065 break;
3066 case "git":
3067 $resolved_link = ilInternalLink::_getIdForImportId("GlossaryItem", $internal_link);
3068 break;
3069 case "mob":
3070 $resolved_link = ilInternalLink::_getIdForImportId("MediaObject", $internal_link);
3071 break;
3072 }
3073 if (strcmp($resolved_link, "") == 0)
3074 {
3075 $resolved_link = $internal_link;
3076 }
3077 }
3078 else
3079 {
3080 $resolved_link = $internal_link;
3081 }
3082 return $resolved_link;
3083 }
3084
3085 function _resolveIntLinks($question_id)
3086 {
3087 global $ilDB;
3088 $resolvedlinks = 0;
3089 $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s",
3090 array('integer'),
3091 array($question_id)
3092 );
3093 if ($result->numRows())
3094 {
3095 while ($row = $ilDB->fetchAssoc($result))
3096 {
3097 $internal_link = $row["internal_link"];
3098 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
3099 $resolved_link = assQuestion::_resolveInternalLink($internal_link);
3100 if (strcmp($internal_link, $resolved_link) != 0)
3101 {
3102 // internal link was resolved successfully
3103 $affectedRows = $ilDB->manipulateF("UPDATE qpl_sol_sug SET internal_link = %s WHERE suggested_solution_id = %s",
3104 array('text','integer'),
3105 array($resolved_link, $row["suggested_solution_id"])
3106 );
3107 $resolvedlinks++;
3108 }
3109 }
3110 }
3111 if ($resolvedlinks)
3112 {
3113 // there are resolved links -> reenter theses links to the database
3114
3115 // delete all internal links from the database
3116 include_once "./Services/Link/classes/class.ilInternalLink.php";
3117 ilInternalLink::_deleteAllLinksOfSource("qst", $question_id);
3118
3119 $result = $ilDB->queryF("SELECT * FROM qpl_sol_sug WHERE question_fi = %s",
3120 array('integer'),
3121 array($question_id)
3122 );
3123 if ($result->numRows())
3124 {
3125 while ($row = $ilDB->fetchAssoc($result))
3126 {
3127 if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $row["internal_link"], $matches))
3128 {
3129 ilInternalLink::_saveLink("qst", $question_id, $matches[2], $matches[3], $matches[1]);
3130 }
3131 }
3132 }
3133 }
3134 }
3135
3136 public static function _getInternalLinkHref($target = "")
3137 {
3138 global $ilDB;
3139 $linktypes = array(
3140 "lm" => "LearningModule",
3141 "pg" => "PageObject",
3142 "st" => "StructureObject",
3143 "git" => "GlossaryItem",
3144 "mob" => "MediaObject"
3145 );
3146 $href = "";
3147 if (preg_match("/il__(\w+)_(\d+)/", $target, $matches))
3148 {
3149 $type = $matches[1];
3150 $target_id = $matches[2];
3151 include_once "./Services/Utilities/classes/class.ilUtil.php";
3152 switch($linktypes[$matches[1]])
3153 {
3154 case "LearningModule":
3155 $href = "./goto.php?target=" . $type . "_" . $target_id;
3156 break;
3157 case "PageObject":
3158 case "StructureObject":
3159 $href = "./goto.php?target=" . $type . "_" . $target_id;
3160 break;
3161 case "GlossaryItem":
3162 $href = "./goto.php?target=" . $type . "_" . $target_id;
3163 break;
3164 case "MediaObject":
3165 $href = "./ilias.php?baseClass=ilLMPresentationGUI&obj_type=" . $linktypes[$type] . "&cmd=media&ref_id=".$_GET["ref_id"]."&mob_id=".$target_id;
3166 break;
3167 }
3168 }
3169 return $href;
3170 }
3171
3179 public static function _getOriginalId($question_id)
3180 {
3181 global $ilDB;
3182 $result = $ilDB->queryF("SELECT * FROM qpl_questions WHERE question_id = %s",
3183 array('integer'),
3184 array($question_id)
3185 );
3186 if ($result->numRows() > 0)
3187 {
3188 $row = $ilDB->fetchAssoc($result);
3189 if ($row["original_id"] > 0)
3190 {
3191 return $row["original_id"];
3192 }
3193 else
3194 {
3195 return $row["question_id"];
3196 }
3197 }
3198 else
3199 {
3200 return "";
3201 }
3202 }
3203
3204 public static function originalQuestionExists($questionId)
3205 {
3206 global $ilDB;
3207
3208 $query = "
3209 SELECT COUNT(dupl.question_id) cnt
3210 FROM qpl_questions dupl
3211 INNER JOIN qpl_questions orig
3212 ON orig.question_id = dupl.original_id
3213 WHERE dupl.question_id = %s
3214 ";
3215
3216 $res = $ilDB->queryF($query, array('integer'), array($questionId));
3217 $row = $ilDB->fetchAssoc($res);
3218
3219 return $row['cnt'] > 0;
3220 }
3221
3223 {
3224 global $ilDB;
3225
3226 if( !$this->getOriginalId() )
3227 {
3228 return;
3229 }
3230
3231 $originalObjId = self::lookupOriginalParentObjId($this->getOriginalId());
3232
3233 if ( !$originalObjId )
3234 {
3235 return;
3236 }
3237
3238 $id = $this->getId();
3239 $objId = $this->getObjId();
3240 $original = $this->getOriginalId();
3241
3242 $this->beforeSyncWithOriginal($original, $id, $originalObjId, $objId);
3243
3244 $this->setId($original);
3245 $this->setOriginalId(NULL);
3246 $this->setObjId($originalObjId);
3247
3248 $this->saveToDb();
3249
3250 $this->deletePageOfQuestion($original);
3251 $this->createPageObject();
3252 $this->copyPageOfQuestion($id);
3253
3254 $this->setId($id);
3255 $this->setOriginalId($original);
3256 $this->setObjId($objId);
3257
3258 $this->updateSuggestedSolutions($original);
3260
3261 $this->afterSyncWithOriginal($original, $id, $originalObjId, $objId);
3262 $this->syncHints();
3263 }
3264
3265 function createRandomSolution($test_id, $user_id)
3266 {
3267 }
3268
3276 function _questionExists($question_id)
3277 {
3278 global $ilDB;
3279
3280 if ($question_id < 1)
3281 {
3282 return false;
3283 }
3284
3285 $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE question_id = %s",
3286 array('integer'),
3287 array($question_id)
3288 );
3289 if ($result->numRows() == 1)
3290 {
3291 return true;
3292 }
3293 else
3294 {
3295 return false;
3296 }
3297 }
3298
3306 function _questionExistsInPool($question_id)
3307 {
3308 global $ilDB;
3309
3310 if ($question_id < 1)
3311 {
3312 return false;
3313 }
3314
3315 $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'",
3316 array('integer'),
3317 array($question_id)
3318 );
3319 if ($result->numRows() == 1)
3320 {
3321 return true;
3322 }
3323 else
3324 {
3325 return false;
3326 }
3327 }
3328
3336 public static function _instanciateQuestion($question_id)
3337 {
3338 return self::_instantiateQuestion($question_id);
3339 }
3340
3345 public static function _instantiateQuestion($question_id)
3346 {
3347 global $ilCtrl, $ilDB, $lng;
3348
3349 if (strcmp($question_id, "") != 0)
3350 {
3351 $question_type = assQuestion::_getQuestionType($question_id);
3352 if (!strlen($question_type)) return null;
3353 assQuestion::_includeClass($question_type);
3354 $objectClassname = self::getObjectClassNameByQuestionType($question_type);
3355 $question = new $objectClassname();
3356 $question->loadFromDb($question_id);
3357
3358 $feedbackObjectClassname = self::getFeedbackClassNameByQuestionType($question_type);
3359 $question->feedbackOBJ = new $feedbackObjectClassname($question, $ilCtrl, $ilDB, $lng);
3360
3361 return $question;
3362 }
3363 }
3364
3371 function getPoints()
3372 {
3373 if (strcmp($this->points, "") == 0)
3374 {
3375 return 0;
3376 }
3377 else
3378 {
3379 return $this->points;
3380 }
3381 }
3382
3383
3390 function setPoints($a_points)
3391 {
3392 $this->points = $a_points;
3393 }
3394
3401 function getSolutionMaxPass($active_id)
3402 {
3403 return self::_getSolutionMaxPass($this->getId(), $active_id);
3404 }
3405
3412 public static function _getSolutionMaxPass($question_id, $active_id)
3413 {
3414/* include_once "./Modules/Test/classes/class.ilObjTest.php";
3415 $pass = ilObjTest::_getPass($active_id);
3416 return $pass;*/
3417
3418 // the following code was the old solution which added the non answered
3419 // questions of a pass from the answered questions of the previous pass
3420 // with the above solution, only the answered questions of the last pass are counted
3421 global $ilDB;
3422
3423 $result = $ilDB->queryF("SELECT MAX(pass) maxpass FROM tst_test_result WHERE active_fi = %s AND question_fi = %s",
3424 array('integer','integer'),
3425 array($active_id, $question_id)
3426 );
3427 if ($result->numRows() == 1)
3428 {
3429 $row = $ilDB->fetchAssoc($result);
3430 return $row["maxpass"];
3431 }
3432 else
3433 {
3434 return 0;
3435 }
3436 }
3437
3446 public static function _isWriteable($question_id, $user_id)
3447 {
3448 global $ilDB;
3449
3450 if (($question_id < 1) || ($user_id < 1))
3451 {
3452 return false;
3453 }
3454
3455 $result = $ilDB->queryF("SELECT obj_fi FROM qpl_questions WHERE question_id = %s",
3456 array('integer'),
3457 array($question_id)
3458 );
3459 if ($result->numRows() == 1)
3460 {
3461 $row = $ilDB->fetchAssoc($result);
3462 $qpl_object_id = $row["obj_fi"];
3463 include_once "./Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php";
3464 return ilObjQuestionPool::_isWriteable($qpl_object_id, $user_id);
3465 }
3466 else
3467 {
3468 return false;
3469 }
3470 }
3471
3478 public static function _isUsedInRandomTest($question_id = "")
3479 {
3480 global $ilDB;
3481
3482 if ($question_id < 1) return 0;
3483 $result = $ilDB->queryF("SELECT test_random_question_id FROM tst_test_rnd_qst WHERE question_fi = %s",
3484 array('integer'),
3485 array($question_id)
3486 );
3487 return $result->numRows();
3488 }
3489
3501 abstract public function calculateReachedPoints($active_id, $pass = NULL, $authorizedSolution = true, $returndetails = FALSE);
3502
3503 public function deductHintPointsFromReachedPoints(ilAssQuestionPreviewSession $previewSession, $reachedPoints)
3504 {
3505 global $DIC;
3506
3507 $hintTracking = new ilAssQuestionPreviewHintTracking($DIC->database(), $previewSession);
3508 $requestsStatisticData = $hintTracking->getRequestStatisticData();
3509 $reachedPoints = $reachedPoints - $requestsStatisticData->getRequestsPoints();
3510
3511 return $reachedPoints;
3512 }
3513
3515 {
3516 $reachedPoints = $this->calculateReachedPointsForSolution($previewSession->getParticipantsSolution());
3517 $reachedPoints = $this->deductHintPointsFromReachedPoints($previewSession, $reachedPoints);
3518
3519 return $this->ensureNonNegativePoints($reachedPoints);
3520 }
3521
3523 {
3524 return $points > 0 ? $points : 0;
3525 }
3526
3528 {
3529 $reachedPoints = $this->calculateReachedPointsFromPreviewSession($previewSession);
3530
3531 if( $reachedPoints < $this->getMaximumPoints() )
3532 {
3533 return false;
3534 }
3535
3536 return true;
3537 }
3538
3539
3550 final public function adjustReachedPointsByScoringOptions($points, $active_id, $pass = NULL)
3551 {
3552 include_once "./Modules/Test/classes/class.ilObjTest.php";
3553 $count_system = ilObjTest::_getCountSystem($active_id);
3554 if ($count_system == 1)
3555 {
3556 if (abs($this->getMaximumPoints() - $points) > 0.0000000001)
3557 {
3558 $points = 0;
3559 }
3560 }
3561 $score_cutting = ilObjTest::_getScoreCutting($active_id);
3562 if ($score_cutting == 0)
3563 {
3564 if ($points < 0)
3565 {
3566 $points = 0;
3567 }
3568 }
3569 return $points;
3570 }
3571
3580 public static function _isWorkedThrough($active_id, $question_id, $pass = NULL)
3581 {
3582 return self::lookupResultRecordExist($active_id, $question_id, $pass);
3583
3584 // oldschool "workedthru"
3585
3586 global $ilDB;
3587
3588 $points = 0;
3589 if (is_null($pass))
3590 {
3591 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
3592 $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
3593 }
3594 $result = $ilDB->queryF("SELECT solution_id FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
3595 array('integer','integer','integer'),
3596 array($active_id, $question_id, $pass)
3597 );
3598 if ($result->numRows())
3599 {
3600 return TRUE;
3601 }
3602 else
3603 {
3604 return FALSE;
3605 }
3606 }
3607
3615 public static function _areAnswered($a_user_id,$a_question_ids)
3616 {
3617 global $ilDB;
3618
3619 $res = $ilDB->queryF("SELECT DISTINCT(question_fi) FROM tst_test_result JOIN tst_active ".
3620 "ON (active_id = active_fi) ".
3621 "WHERE " . $ilDB->in('question_fi', $a_question_ids, false, 'integer') .
3622 " AND user_fi = %s",
3623 array('integer'),
3624 array($a_user_id)
3625 );
3626 return ($res->numRows() == count($a_question_ids)) ? true : false;
3627 }
3628
3637 function isHTML($a_text)
3638 {
3639 return ilUtil::isHTML($a_text);
3640 }
3641
3648 function prepareTextareaOutput($txt_output, $prepare_for_latex_output = FALSE, $omitNl2BrWhenTextArea = false)
3649 {
3650 include_once "./Services/Utilities/classes/class.ilUtil.php";
3651 return ilUtil::prepareTextareaOutput($txt_output, $prepare_for_latex_output, $omitNl2BrWhenTextArea);
3652 }
3653
3661 function QTIMaterialToString($a_material)
3662 {
3663 $result = "";
3664 for ($i = 0; $i < $a_material->getMaterialCount(); $i++)
3665 {
3666 $material = $a_material->getMaterial($i);
3667 if (strcmp($material["type"], "mattext") == 0)
3668 {
3669 $result .= $material["material"]->getContent();
3670 }
3671 if (strcmp($material["type"], "matimage") == 0)
3672 {
3673 $matimage = $material["material"];
3674 if (preg_match("/(il_([0-9]+)_mob_([0-9]+))/", $matimage->getLabel(), $matches))
3675 {
3676 // import an mediaobject which was inserted using tiny mce
3677 if (!is_array($_SESSION["import_mob_xhtml"])) $_SESSION["import_mob_xhtml"] = array();
3678 array_push($_SESSION["import_mob_xhtml"], array("mob" => $matimage->getLabel(), "uri" => $matimage->getUri()));
3679 }
3680 }
3681 }
3682 return $result;
3683 }
3684
3693 function addQTIMaterial(&$a_xml_writer, $a_material, $close_material_tag = TRUE, $add_mobs = TRUE)
3694 {
3695 include_once "./Services/RTE/classes/class.ilRTE.php";
3696 include_once("./Services/MediaObjects/classes/class.ilObjMediaObject.php");
3697
3698 $a_xml_writer->xmlStartTag("material");
3699 $attrs = array(
3700 "texttype" => "text/plain"
3701 );
3702 if ($this->isHTML($a_material))
3703 {
3704 $attrs["texttype"] = "text/xhtml";
3705 }
3706 $a_xml_writer->xmlElement("mattext", $attrs, ilRTE::_replaceMediaObjectImageSrc($a_material, 0));
3707 if ($add_mobs)
3708 {
3709 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
3710 foreach ($mobs as $mob)
3711 {
3712 $moblabel = "il_" . IL_INST_ID . "_mob_" . $mob;
3713 if (strpos($a_material, "mm_$mob") !== FALSE)
3714 {
3715 if (ilObjMediaObject::_exists($mob))
3716 {
3717 $mob_obj = new ilObjMediaObject($mob);
3718 $imgattrs = array(
3719 "label" => $moblabel,
3720 "uri" => "objects/" . "il_" . IL_INST_ID . "_mob_" . $mob . "/" . $mob_obj->getTitle()
3721 );
3722 }
3723 $a_xml_writer->xmlElement("matimage", $imgattrs, NULL);
3724 }
3725 }
3726 }
3727 if ($close_material_tag) $a_xml_writer->xmlEndTag("material");
3728 }
3729
3730 function buildHashedImageFilename($plain_image_filename, $unique = false)
3731 {
3732 $extension = "";
3733
3734 if (preg_match("/.*\.(png|jpg|gif|jpeg)$/i", $plain_image_filename, $matches))
3735 {
3736 $extension = "." . $matches[1];
3737 }
3738
3739 if($unique)
3740 {
3741 $plain_image_filename = uniqid($plain_image_filename.microtime(true));
3742 }
3743
3744 $hashed_filename = md5($plain_image_filename) . $extension;
3745
3746 return $hashed_filename;
3747 }
3748
3759 public static function _setReachedPoints($active_id, $question_id, $points, $maxpoints, $pass, $manualscoring, $obligationsEnabled)
3760 {
3761 global $ilDB;
3762
3763 if ($points <= $maxpoints)
3764 {
3765 if (is_null($pass))
3766 {
3767 $pass = assQuestion::_getSolutionMaxPass($question_id, $active_id);
3768 }
3769
3770 // retrieve the already given points
3771 $old_points = 0;
3772 $result = $ilDB->queryF("SELECT points FROM tst_test_result WHERE active_fi = %s AND question_fi = %s AND pass = %s",
3773 array('integer','integer','integer'),
3774 array($active_id, $question_id, $pass)
3775 );
3776 $manual = ($manualscoring) ? 1 : 0;
3777 $rowsnum = $result->numRows();
3778 if($rowsnum)
3779 {
3780 $row = $ilDB->fetchAssoc($result);
3781 $old_points = $row["points"];
3782 if($old_points != $points)
3783 {
3784 $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",
3785 array('float', 'integer', 'integer', 'integer', 'integer', 'integer'),
3786 array($points, $manual, time(), $active_id, $question_id, $pass)
3787 );
3788 }
3789 }
3790 else
3791 {
3792 $next_id = $ilDB->nextId('tst_test_result');
3793 $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)",
3794 array('integer', 'integer','integer', 'float', 'integer', 'integer','integer'),
3795 array($next_id, $active_id, $question_id, $points, $pass, $manual, time())
3796 );
3797 }
3798
3799 if(self::isForcePassResultUpdateEnabled() || $old_points != $points || !$rowsnum)
3800 {
3801 assQuestion::_updateTestPassResults($active_id, $pass, $obligationsEnabled);
3802 // finally update objective result
3803 include_once "./Modules/Test/classes/class.ilObjTest.php";
3804 include_once './Modules/Course/classes/class.ilCourseObjectiveResult.php';
3806
3807 include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
3809 {
3810 global $lng, $ilUser;
3811 include_once "./Modules/Test/classes/class.ilObjTestAccess.php";
3812 $username = ilObjTestAccess::_getParticipantData($active_id);
3813 assQuestion::logAction(sprintf($lng->txtlng("assessment", "log_answer_changed_points", ilObjAssessmentFolder::_getLogLanguage()), $username, $old_points, $points, $ilUser->getFullname() . " (" . $ilUser->getLogin() . ")"), $active_id, $question_id);
3814 }
3815 }
3816
3817 return TRUE;
3818 }
3819 else
3820 {
3821 return FALSE;
3822 }
3823 }
3824
3832 function getQuestion()
3833 {
3834 return $this->question;
3835 }
3836
3844 function setQuestion($question = "")
3845 {
3846 $this->question = $question;
3847 }
3848
3854 abstract public function getQuestionType();
3855
3865 {
3866 global $ilDB;
3867
3868 $result = $ilDB->queryF("SELECT question_type_id FROM qpl_qst_type WHERE type_tag = %s",
3869 array('text'),
3870 array($this->getQuestionType())
3871 );
3872 if ($result->numRows() == 1)
3873 {
3874 $row = $ilDB->fetchAssoc($result);
3875 return $row["question_type_id"];
3876 }
3877 return 0;
3878 }
3879
3880 public function syncHints()
3881 {
3882 global $ilDB;
3883
3884 // delete hints of the original
3885 $ilDB->manipulateF("DELETE FROM qpl_hints WHERE qht_question_fi = %s",
3886 array('integer'),
3887 array($this->original_id)
3888 );
3889
3890 // get hints of the actual question
3891 $result = $ilDB->queryF("SELECT * FROM qpl_hints WHERE qht_question_fi = %s",
3892 array('integer'),
3893 array($this->getId())
3894 );
3895
3896 // save hints to the original
3897 if ($result->numRows())
3898 {
3899 while ($row = $ilDB->fetchAssoc($result))
3900 {
3901 $next_id = $ilDB->nextId('qpl_hints');
3903 $ilDB->insert('qpl_hints', array(
3904 'qht_hint_id' => array('integer', $next_id),
3905 'qht_question_fi' => array('integer', $this->original_id),
3906 'qht_hint_index' => array('integer', $row["qht_hint_index"]),
3907 'qht_hint_points' => array('integer', $row["qht_hint_points"]),
3908 'qht_hint_text' => array('text', $row["qht_hint_text"]),
3909 )
3910 );
3911 }
3912 }
3913 }
3914
3919 protected function getRTETextWithMediaObjects()
3920 {
3921 // must be called in parent classes. add additional RTE text in the parent
3922 // classes and call this method to add the standard RTE text
3923 $collected = $this->getQuestion();
3924 $collected .= $this->feedbackOBJ->getGenericFeedbackContent($this->getId(), false);
3925 $collected .= $this->feedbackOBJ->getGenericFeedbackContent($this->getId(), true);
3926 $collected .= $this->feedbackOBJ->getAllSpecificAnswerFeedbackContents($this->getId());
3927
3928 foreach ($this->suggested_solutions as $solution_array)
3929 {
3930 $collected .= $solution_array["value"];
3931 }
3932
3933 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintList.php';
3934 $questionHintList = ilAssQuestionHintList::getListByQuestionId($this->getId());
3935 foreach($questionHintList as $questionHint)
3936 {
3937 /* @var $questionHint ilAssQuestionHint */
3938 $collected .= $questionHint->getText();
3939 }
3940
3941 return $collected;
3942 }
3943
3949 {
3950 $combinedtext = $this->getRTETextWithMediaObjects();
3951 include_once("./Services/RTE/classes/class.ilRTE.php");
3952 ilRTE::_cleanupMediaObjectUsage($combinedtext, "qpl:html", $this->getId());
3953 }
3954
3960 function &getInstances()
3961 {
3962 global $ilDB;
3963
3964 $result = $ilDB->queryF("SELECT question_id FROM qpl_questions WHERE original_id = %s",
3965 array("integer"),
3966 array($this->getId())
3967 );
3968 $instances = array();
3969 $ids = array();
3970 while ($row = $ilDB->fetchAssoc($result))
3971 {
3972 array_push($ids, $row["question_id"]);
3973 }
3974 foreach ($ids as $question_id)
3975 {
3976 // check non random tests
3977 $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",
3978 array("integer"),
3979 array($question_id)
3980 );
3981 while ($row = $ilDB->fetchAssoc($result))
3982 {
3983 $instances[$row['obj_fi']] = ilObject::_lookupTitle($row['obj_fi']);
3984 }
3985 // check random tests
3986 $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",
3987 array("integer"),
3988 array($question_id)
3989 );
3990 while ($row = $ilDB->fetchAssoc($result))
3991 {
3992 $instances[$row['obj_fi']] = ilObject::_lookupTitle($row['obj_fi']);
3993 }
3994 }
3995 include_once "./Modules/Test/classes/class.ilObjTest.php";
3996 foreach ($instances as $key => $value)
3997 {
3998 $instances[$key] = array("obj_id" => $key, "title" => $value, "author" => ilObjTest::_lookupAuthor($key), "refs" => ilObject::_getAllReferences($key));
3999 }
4000 return $instances;
4001 }
4002
4003 public static function _needsManualScoring($question_id)
4004 {
4005 include_once "./Modules/Test/classes/class.ilObjAssessmentFolder.php";
4007 $questiontype = assQuestion::_getQuestionType($question_id);
4008 if (in_array($questiontype, $scoring))
4009 {
4010 return TRUE;
4011 }
4012 else
4013 {
4014 return FALSE;
4015 }
4016 }
4017
4025 function getActiveUserData($active_id)
4026 {
4027 global $ilDB;
4028 $result = $ilDB->queryF("SELECT * FROM tst_active WHERE active_id = %s",
4029 array('integer'),
4030 array($active_id)
4031 );
4032 if ($result->numRows())
4033 {
4034 $row = $ilDB->fetchAssoc($result);
4035 return array("user_id" => $row["user_fi"], "test_id" => $row["test_fi"]);
4036 }
4037 else
4038 {
4039 return array();
4040 }
4041 }
4042
4050 static function _includeClass($question_type, $gui = 0)
4051 {
4052 if( self::isCoreQuestionType($question_type) )
4053 {
4054 self::includeCoreClass($question_type, $gui);
4055 }
4056 else
4057 {
4058 self::includePluginClass($question_type, $gui);
4059 }
4060 }
4061
4062 public static function getGuiClassNameByQuestionType($questionType)
4063 {
4064 return $questionType.'GUI';
4065 }
4066
4067 public static function getObjectClassNameByQuestionType($questionType)
4068 {
4069 return $questionType;
4070 }
4071
4072 public static function getFeedbackClassNameByQuestionType($questionType)
4073 {
4074 return str_replace('ass', 'ilAss', $questionType).'Feedback';
4075 }
4076
4077 public static function isCoreQuestionType($questionType)
4078 {
4079 $guiClassName = self::getGuiClassNameByQuestionType($questionType);
4080 return file_exists("Modules/TestQuestionPool/classes/class.{$guiClassName}.php");
4081 }
4082
4083 public static function includeCoreClass($questionType, $withGuiClass)
4084 {
4085 if( $withGuiClass )
4086 {
4087 $guiClassName = self::getGuiClassNameByQuestionType($questionType);
4088 require_once "Modules/TestQuestionPool/classes/class.{$guiClassName}.php";
4089
4090 // object class is included by gui classes constructor
4091 }
4092 else
4093 {
4094 $objectClassName = self::getObjectClassNameByQuestionType($questionType);
4095 require_once "Modules/TestQuestionPool/classes/class.{$objectClassName}.php";
4096 }
4097
4098 $feedbackClassName = self::getFeedbackClassNameByQuestionType($questionType);
4099 require_once "Modules/TestQuestionPool/classes/feedback/class.{$feedbackClassName}.php";
4100 }
4101
4102 public static function includePluginClass($questionType, $withGuiClass)
4103 {
4104 global $ilPluginAdmin;
4105
4106 $classes = array(
4107 self::getObjectClassNameByQuestionType($questionType),
4108 self::getFeedbackClassNameByQuestionType($questionType)
4109 );
4110
4111 if( $withGuiClass )
4112 {
4113 $classes[] = self::getGuiClassNameByQuestionType($questionType);
4114 }
4115
4116 $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
4117 foreach ($pl_names as $pl_name)
4118 {
4119 $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
4120 if (strcmp($pl->getQuestionType(), $questionType) == 0)
4121 {
4122 foreach($classes as $class)
4123 {
4124 $pl->includeClass("class.{$class}.php");
4125 }
4126
4127 break;
4128 }
4129 }
4130 }
4131
4138 static function _getQuestionTypeName($type_tag)
4139 {
4140 if (file_exists("./Modules/TestQuestionPool/classes/class.".$type_tag.".php"))
4141 {
4142 global $lng;
4143 return $lng->txt($type_tag);
4144 }
4145 else
4146 {
4147 global $ilPluginAdmin;
4148 $pl_names = $ilPluginAdmin->getActivePluginsForSlot(IL_COMP_MODULE, "TestQuestionPool", "qst");
4149 foreach ($pl_names as $pl_name)
4150 {
4151 $pl = ilPlugin::getPluginObject(IL_COMP_MODULE, "TestQuestionPool", "qst", $pl_name);
4152 if (strcmp($pl->getQuestionType(), $type_tag) == 0)
4153 {
4154 return $pl->getQuestionTypeTranslation();
4155 }
4156 }
4157 }
4158 return "";
4159 }
4160
4170 public static function &_instanciateQuestionGUI($question_id)
4171 {
4172 return self::instantiateQuestionGUI($question_id);
4173 }
4174
4182 public static function instantiateQuestionGUI($a_question_id)
4183 {
4184 global $ilCtrl, $ilDB, $lng, $ilUser;
4185
4186 if (strcmp($a_question_id, "") != 0)
4187 {
4188 $question_type = assQuestion::_getQuestionType($a_question_id);
4189
4190 assQuestion::_includeClass($question_type, 1);
4191
4192 $question_type_gui = self::getGuiClassNameByQuestionType($question_type);
4193 $question_gui = new $question_type_gui();
4194 $question_gui->object->loadFromDb($a_question_id);
4195
4196 $feedbackObjectClassname = self::getFeedbackClassNameByQuestionType($question_type);
4197 $question_gui->object->feedbackOBJ = new $feedbackObjectClassname($question_gui->object, $ilCtrl, $ilDB, $lng);
4198
4199 $assSettings = new ilSetting('assessment');
4200 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionProcessLockerFactory.php';
4201 $processLockerFactory = new ilAssQuestionProcessLockerFactory($assSettings, $ilDB);
4202 $processLockerFactory->setQuestionId($question_gui->object->getId());
4203 $processLockerFactory->setUserId($ilUser->getId());
4204 include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
4205 $processLockerFactory->setAssessmentLogEnabled(ilObjAssessmentFolder::_enabledAssessmentLogging());
4206 $question_gui->object->setProcessLocker($processLockerFactory->getLocker());
4207 }
4208 else
4209 {
4210 global $ilLog;
4211 $ilLog->write('Instantiate question called without question id. (instantiateQuestionGUI@assQuestion)', $ilLog->WARNING);
4212 return null;
4213 }
4214 return $question_gui;
4215 }
4216
4227 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
4228 {
4229 $worksheet->setFormattedExcelTitle($worksheet->getColumnCoord(0) . $startrow, $this->lng->txt($this->getQuestionType()));
4230 $worksheet->setFormattedExcelTitle($worksheet->getColumnCoord(1) . $startrow, $this->getTitle());
4231
4232 return $startrow;
4233 }
4234
4238 public function __get($value)
4239 {
4240 switch ($value)
4241 {
4242 case "id":
4243 return $this->getId();
4244 break;
4245 case "title":
4246 return $this->getTitle();
4247 break;
4248 case "comment":
4249 return $this->getComment();
4250 break;
4251 case "owner":
4252 return $this->getOwner();
4253 break;
4254 case "author":
4255 return $this->getAuthor();
4256 break;
4257 case "question":
4258 return $this->getQuestion();
4259 break;
4260 case "points":
4261 return $this->getPoints();
4262 break;
4263 case "est_working_time":
4264 return $this->getEstimatedWorkingTime();
4265 break;
4266 case "shuffle":
4267 return $this->getShuffle();
4268 break;
4269 case "test_id":
4270 return $this->getTestId();
4271 break;
4272 case "obj_id":
4273 return $this->getObjId();
4274 break;
4275 case "ilias":
4276 return $this->ilias;
4277 break;
4278 case "tpl":
4279 return $this->tpl;
4280 break;
4281 case "page":
4282 return $this->page;
4283 break;
4284 case "outputType":
4285 return $this->getOutputType();
4286 break;
4287 case "suggested_solutions":
4288 return $this->getSuggestedSolutions();
4289 break;
4290 case "original_id":
4291 return $this->getOriginalId();
4292 break;
4293 default:
4294 if (array_key_exists($value, $this->arrData))
4295 {
4296 return $this->arrData[$value];
4297 }
4298 else
4299 {
4300 return null;
4301 }
4302 break;
4303 }
4304 }
4305
4309 public function __set($key, $value)
4310 {
4311 switch ($key)
4312 {
4313 case "id":
4314 $this->setId($value);
4315 break;
4316 case "title":
4317 $this->setTitle($value);
4318 break;
4319 case "comment":
4320 $this->setComment($value);
4321 break;
4322 case "owner":
4323 $this->setOwner($value);
4324 break;
4325 case "author":
4326 $this->setAuthor($value);
4327 break;
4328 case "question":
4329 $this->setQuestion($value);
4330 break;
4331 case "points":
4332 $this->setPoints($value);
4333 break;
4334 case "est_working_time":
4335 if (is_array($value))
4336 {
4337 $this->setEstimatedWorkingTime($value["h"], $value["m"], $value["s"]);
4338 }
4339 break;
4340 case "shuffle":
4341 $this->setShuffle($value);
4342 break;
4343 case "test_id":
4344 $this->setTestId($value);
4345 break;
4346 case "obj_id":
4347 $this->setObjId($value);
4348 break;
4349 case "outputType":
4350 $this->setOutputType($value);
4351 break;
4352 case "original_id":
4353 $this->setOriginalId($value);
4354 break;
4355 case "page":
4356 $this->page =& $value;
4357 break;
4358 default:
4359 $this->arrData[$key] = $value;
4360 break;
4361 }
4362 }
4363
4364 public function getNrOfTries()
4365 {
4366 return (int)$this->nr_of_tries;
4367 }
4368
4369 public function setNrOfTries($a_nr_of_tries)
4370 {
4371 $this->nr_of_tries = $a_nr_of_tries;
4372 }
4373
4374 public function setExportImagePath($a_path)
4375 {
4376 $this->export_image_path = (string)$a_path;
4377 }
4378
4379 public static function _questionExistsInTest($question_id, $test_id)
4380 {
4381 global $ilDB;
4382
4383 if ($question_id < 1)
4384 {
4385 return false;
4386 }
4387
4388 $result = $ilDB->queryF("SELECT question_fi FROM tst_test_question WHERE question_fi = %s AND test_fi = %s",
4389 array('integer', 'integer'),
4390 array($question_id, $test_id)
4391 );
4392 if ($result->numRows() == 1)
4393 {
4394 return true;
4395 }
4396 else
4397 {
4398 return false;
4399 }
4400 }
4401
4408 public function formatSAQuestion($a_q)
4409 {
4410 return $this->getSelfAssessmentFormatter()->format($a_q);
4411 }
4412
4416 protected function getSelfAssessmentFormatter()
4417 {
4418 require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssSelfAssessmentQuestionFormatter.php';
4419 return new \ilAssSelfAssessmentQuestionFormatter();
4420 }
4421
4422 // scorm2004-start ???
4423
4429 function setPreventRteUsage($a_val)
4430 {
4431 $this->prevent_rte_usage = $a_val;
4432 }
4433
4440 {
4442 }
4443
4448 {
4449 $this->lmMigrateQuestionTypeGenericContent($migrator);
4450 $this->lmMigrateQuestionTypeSpecificContent($migrator);
4451 $this->saveToDb();
4452
4453 $this->feedbackOBJ->migrateContentForLearningModule($migrator, $this->getId());
4454 }
4455
4460 {
4461 $this->setQuestion( $migrator->migrateToLmContent( $this->getQuestion() ) );
4462 }
4463
4468 {
4469 // overwrite if any question type specific content except feedback needs to be migrated
4470 }
4471
4477 function setSelfAssessmentEditingMode($a_selfassessmenteditingmode)
4478 {
4479 $this->selfassessmenteditingmode = $a_selfassessmenteditingmode;
4480 }
4481
4488 {
4490 }
4491
4497 function setDefaultNrOfTries($a_defaultnroftries)
4498 {
4499 $this->defaultnroftries = $a_defaultnroftries;
4500 }
4501
4508 {
4509 return (int)$this->defaultnroftries;
4510 }
4511
4512 // scorm2004-end ???
4513
4519 public static function lookupParentObjId($questionId)
4520 {
4521 global $ilDB;
4522
4523 $query = "SELECT obj_fi FROM qpl_questions WHERE question_id = %s";
4524
4525 $res = $ilDB->queryF($query, array('integer'), array((int)$questionId));
4526 $row = $ilDB->fetchAssoc($res);
4527
4528 return $row['obj_fi'];
4529 }
4530
4541 public static function lookupOriginalParentObjId($originalQuestionId)
4542 {
4543 return self::lookupParentObjId($originalQuestionId);
4544 }
4545
4546 protected function duplicateQuestionHints($originalQuestionId, $duplicateQuestionId)
4547 {
4548 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionHintList.php';
4549 $hintIds = ilAssQuestionHintList::duplicateListForQuestion($originalQuestionId, $duplicateQuestionId);
4550
4552 {
4553 require_once 'Modules/TestQuestionPool/classes/class.ilAssHintPage.php';
4554
4555 foreach($hintIds as $originalHintId => $duplicateHintId)
4556 {
4557 $originalPageObject = new ilAssHintPage($originalHintId);
4558 $originalXML = $originalPageObject->getXMLContent();
4559
4560 $duplicatePageObject = new ilAssHintPage();
4561 $duplicatePageObject->setId($duplicateHintId);
4562 $duplicatePageObject->setParentId($this->getId());
4563 $duplicatePageObject->setXMLContent($originalXML);
4564 $duplicatePageObject->createFromXML();
4565 }
4566 }
4567 }
4568
4569 protected function duplicateSkillAssignments($srcParentId, $srcQuestionId, $trgParentId, $trgQuestionId)
4570 {
4571 global $ilDB;
4572
4573 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionSkillAssignmentList.php';
4574 $assignmentList = new ilAssQuestionSkillAssignmentList($ilDB);
4575 $assignmentList->setParentObjId($srcParentId);
4576 $assignmentList->setQuestionIdFilter($srcQuestionId);
4577 $assignmentList->loadFromDb();
4578
4579 foreach($assignmentList->getAssignmentsByQuestionId($srcQuestionId) as $assignment)
4580 {
4581 /* @var ilAssQuestionSkillAssignment $assignment */
4582
4583 $assignment->setParentObjId($trgParentId);
4584 $assignment->setQuestionId($trgQuestionId);
4585 $assignment->saveToDb();
4586 }
4587 }
4588
4589 public function syncSkillAssignments($srcParentId, $srcQuestionId, $trgParentId, $trgQuestionId)
4590 {
4591 global $ilDB;
4592
4593 require_once 'Modules/TestQuestionPool/classes/class.ilAssQuestionSkillAssignmentList.php';
4594 $assignmentList = new ilAssQuestionSkillAssignmentList($ilDB);
4595 $assignmentList->setParentObjId($trgParentId);
4596 $assignmentList->setQuestionIdFilter($trgQuestionId);
4597 $assignmentList->loadFromDb();
4598
4599 foreach($assignmentList->getAssignmentsByQuestionId($trgQuestionId) as $assignment)
4600 {
4601 /* @var ilAssQuestionSkillAssignment $assignment */
4602
4603 $assignment->deleteFromDb();
4604 }
4605
4606 $this->duplicateSkillAssignments($srcParentId, $srcQuestionId, $trgParentId, $trgQuestionId);
4607 }
4608
4621 public function isAnswered($active_id, $pass = null)
4622 {
4623 return true;
4624 }
4625
4638 public static function isObligationPossible($questionId)
4639 {
4640 return false;
4641 }
4642
4643 public function isAutosaveable()
4644 {
4645 return TRUE;
4646 }
4647
4660 protected static function getNumExistingSolutionRecords($activeId, $pass, $questionId)
4661 {
4662 global $ilDB;
4663
4664 $query = "
4665 SELECT count(active_fi) cnt
4666
4667 FROM tst_solutions
4668
4669 WHERE active_fi = %s
4670 AND question_fi = %s
4671 AND pass = %s
4672 ";
4673
4674 $res = $ilDB->queryF(
4675 $query, array('integer','integer','integer'),
4676 array($activeId, $questionId, $pass)
4677 );
4678
4679 $row = $ilDB->fetchAssoc($res);
4680
4681 return (int)$row['cnt'];
4682 }
4683
4691 {
4693 }
4694
4702 {
4704 {
4705 require_once 'Modules/TestQuestionPool/exceptions/class.ilTestQuestionPoolException.php';
4706 throw new ilTestQuestionPoolException('invalid additional content editing mode given: '.$additinalContentEditingMode);
4707 }
4708
4709 $this->additinalContentEditingMode = $additinalContentEditingMode;
4710 }
4711
4719 {
4721 }
4722
4730 public function isValidAdditionalContentEditingMode($additionalContentEditingMode)
4731 {
4732 if( in_array($additionalContentEditingMode, $this->getValidAdditionalContentEditingModes()) )
4733 {
4734 return true;
4735 }
4736
4737 return false;
4738 }
4739
4747 {
4748 return array(
4749 self::ADDITIONAL_CONTENT_EDITING_MODE_DEFAULT,
4750 self::ADDITIONAL_CONTENT_EDITING_MODE_PAGE_OBJECT
4751 );
4752 }
4753
4758 {
4759 $this->questionChangeListeners[] = $listener;
4760 }
4761
4766 {
4768 }
4769
4770 private function notifyQuestionCreated()
4771 {
4772 foreach($this->getQuestionChangeListeners() as $listener)
4773 {
4774 $listener->notifyQuestionCreated($this);
4775 }
4776 }
4777
4778 private function notifyQuestionEdited()
4779 {
4780 foreach($this->getQuestionChangeListeners() as $listener)
4781 {
4782 $listener->notifyQuestionEdited($this);
4783 }
4784 }
4785
4786 private function notifyQuestionDeleted()
4787 {
4788 foreach($this->getQuestionChangeListeners() as $listener)
4789 {
4790 $listener->notifyQuestionDeleted($this);
4791 }
4792 }
4793
4798 {
4799 require_once 'Services/Html/classes/class.ilHtmlPurifierFactory.php';
4800 return ilHtmlPurifierFactory::_getInstanceByType('qpl_usersolution');
4801 }
4802
4807 {
4808 require_once 'Services/Html/classes/class.ilHtmlPurifierFactory.php';
4809 return ilHtmlPurifierFactory::_getInstanceByType('qpl_usersolution');
4810 }
4811
4812 protected function buildQuestionDataQuery()
4813 {
4814 return "
4815 SELECT qpl_questions.*,
4816 {$this->getAdditionalTableName()}.*
4817 FROM qpl_questions
4818 LEFT JOIN {$this->getAdditionalTableName()}
4819 ON {$this->getAdditionalTableName()}.question_fi = qpl_questions.question_id
4820 WHERE qpl_questions.question_id = %s
4821 ";
4822 }
4823
4825 {
4826 $this->lastChange = $lastChange;
4827 }
4828
4829 public function getLastChange()
4830 {
4831 return $this->lastChange;
4832 }
4833
4844 protected function getCurrentSolutionResultSet($active_id, $pass, $authorized = true)
4845 {
4846 global $ilDB;
4847
4848 if($this->getStep() !== NULL)
4849 {
4850 $query = "
4851 SELECT *
4852 FROM tst_solutions
4853 WHERE active_fi = %s
4854 AND question_fi = %s
4855 AND pass = %s
4856 AND step = %s
4857 AND authorized = %s
4858 ";
4859
4860 return $ilDB->queryF($query, array('integer', 'integer', 'integer', 'integer', 'integer'),
4861 array($active_id, $this->getId(), $pass, $this->getStep(), (int)$authorized)
4862 );
4863 }
4864 else
4865 {
4866 $query = "
4867 SELECT *
4868 FROM tst_solutions
4869 WHERE active_fi = %s
4870 AND question_fi = %s
4871 AND pass = %s
4872 AND authorized = %s
4873 ";
4874
4875 return $ilDB->queryF($query, array('integer', 'integer', 'integer', 'integer'),
4876 array($active_id, $this->getId(), $pass, (int)$authorized)
4877 );
4878 }
4879
4880 }
4881
4888 protected function removeSolutionRecordById($solutionId)
4889 {
4890 global $ilDB;
4891
4892 return $ilDB->manipulateF("DELETE FROM tst_solutions WHERE solution_id = %s",
4893 array('integer'), array($solutionId)
4894 );
4895 }
4896
4897 // hey: prevPassSolutions - selected file reuse, copy solution records
4904 protected function getSolutionRecordById($solutionId)
4905 {
4906 $ilDB = isset($GLOBALS['DIC']) ? $GLOBALS['DIC']['ilDB'] : $GLOBALS['ilDB'];
4907
4908 $res = $ilDB->queryF("SELECT * FROM tst_solutions WHERE solution_id = %s",
4909 array('integer'), array($solutionId)
4910 );
4911
4912 while($row = $ilDB->fetchAssoc($res))
4913 {
4914 return $row;
4915 }
4916 }
4917 // hey.
4918
4927 public function removeIntermediateSolution($active_id, $pass)
4928 {
4929 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function() use ($active_id, $pass) {
4930 $this->removeCurrentSolution($active_id, $pass, false);
4931 });
4932 }
4933
4942 public function removeCurrentSolution($active_id, $pass, $authorized = true)
4943 {
4944 global $ilDB;
4945
4946 if($this->getStep() !== NULL)
4947 {
4948 $query = "
4949 DELETE FROM tst_solutions
4950 WHERE active_fi = %s
4951 AND question_fi = %s
4952 AND pass = %s
4953 AND step = %s
4954 AND authorized = %s
4955 ";
4956
4957 return $ilDB->manipulateF($query, array('integer', 'integer', 'integer', 'integer', 'integer'),
4958 array($active_id, $this->getId(), $pass, $this->getStep(), (int)$authorized)
4959 );
4960 }
4961 else
4962 {
4963 $query = "
4964 DELETE FROM tst_solutions
4965 WHERE active_fi = %s
4966 AND question_fi = %s
4967 AND pass = %s
4968 AND authorized = %s
4969 ";
4970
4971 return $ilDB->manipulateF($query, array('integer', 'integer', 'integer', 'integer'),
4972 array($active_id, $this->getId(), $pass, (int)$authorized)
4973 );
4974 }
4975 }
4976
4977// fau: testNav - add timestamp as parameter to saveCurrentSolution
4989 public function saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized = true, $tstamp = null)
4990 {
4991 global $ilDB;
4992
4993 $next_id = $ilDB->nextId("tst_solutions");
4994
4995 $fieldData = array(
4996 "solution_id" => array("integer", $next_id),
4997 "active_fi" => array("integer", $active_id),
4998 "question_fi" => array("integer", $this->getId()),
4999 "value1" => array("clob", $value1),
5000 "value2" => array("clob", $value2),
5001 "pass" => array("integer", $pass),
5002 "tstamp" => array("integer", isset($tstamp) ? $tstamp : time()),
5003 'authorized' => array('integer', (int)$authorized)
5004 );
5005
5006 if( $this->getStep() !== null )
5007 {
5008 $fieldData['step'] = array("integer", $this->getStep());
5009 }
5010
5011 return $ilDB->insert("tst_solutions", $fieldData);
5012 }
5013// fau.
5014
5025 public function updateCurrentSolution($solutionId, $value1, $value2, $authorized = true)
5026 {
5027 global $ilDB;
5028
5029 $fieldData = array(
5030 "value1" => array("clob", $value1),
5031 "value2" => array("clob", $value2),
5032 "tstamp" => array("integer", time()),
5033 'authorized' => array('integer', (int)$authorized)
5034 );
5035
5036 if( $this->getStep() !== null )
5037 {
5038 $fieldData['step'] = array("integer", $this->getStep());
5039 }
5040
5041 return $ilDB->update("tst_solutions", $fieldData, array(
5042 'solution_id' => array('integer', $solutionId)
5043 ));
5044 }
5045
5046// fau: testNav - added parameter to keep the timestamp (default: false)
5047 public function updateCurrentSolutionsAuthorization($activeId, $pass, $authorized, $keepTime = false)
5048 {
5049 global $ilDB;
5050
5051 $fieldData = array(
5052 'authorized' => array('integer', (int)$authorized)
5053 );
5054
5055 if (!$keepTime)
5056 {
5057 $fieldData['tstamp'] = array('integer', time());
5058 }
5059
5060 $whereData = array(
5061 'question_fi' => array('integer', $this->getId()),
5062 'active_fi' => array('integer', $activeId),
5063 'pass' => array('integer', $pass)
5064 );
5065
5066 if( $this->getStep() !== NULL )
5067 {
5068 $whereData['step'] = array("integer", $this->getStep());
5069 }
5070
5071 return $ilDB->update('tst_solutions', $fieldData, $whereData);
5072 }
5073 // fau.
5074
5075 // hey: prevPassSolutions - motivation slowly decreases on imagemap
5077 protected static function getKeyValuesImplosionSeparator()
5078 {
5080 }
5081 public static function implodeKeyValues($keyValues)
5082 {
5083 return implode(self::getKeyValuesImplosionSeparator(), $keyValues);
5084 }
5085 public static function explodeKeyValues($keyValues)
5086 {
5087 return explode(self::getKeyValuesImplosionSeparator(), $keyValues);
5088 }
5089
5090 protected function deleteDummySolutionRecord($activeId, $passIndex)
5091 {
5092 foreach( $this->getSolutionValues($activeId, $passIndex, false) as $solutionRec )
5093 {
5094 if( 0 == strlen($solutionRec['value1']) && 0 == strlen($solutionRec['value2']) )
5095 {
5096 $this->removeSolutionRecordById($solutionRec['solution_id']);
5097 }
5098 }
5099 }
5100
5101 protected function deleteSolutionRecordByValues($activeId, $passIndex, $authorized, $matchValues)
5102 {
5103 $ilDB = isset($GLOBALS['DIC']) ? $GLOBALS['DIC']['ilDB'] : $GLOBALS['ilDB'];
5104
5105 $types = array("integer", "integer", "integer", "integer");
5106 $values = array($activeId, $this->getId(), $passIndex, (int)$authorized);
5107 $valuesCondition = array();
5108
5109 foreach($matchValues as $valueField => $value)
5110 {
5111 switch($valueField)
5112 {
5113 case 'value1':
5114 case 'value2':
5115 $valuesCondition[] = "{$valueField} = %s";
5116 $types[] = 'text';
5117 $values[] = $value;
5118 break;
5119
5120 default:
5121 require_once 'Modules/TestQuestionPool/exceptions/class.ilTestQuestionPoolException.php';
5122 throw new ilTestQuestionPoolException('invalid value field given: '.$valueField);
5123 }
5124 }
5125
5126 $valuesCondition = implode(' AND ', $valuesCondition);
5127
5128 $query = "
5129 DELETE FROM tst_solutions
5130 WHERE active_fi = %s
5131 AND question_fi = %s
5132 AND pass = %s
5133 AND authorized = %s
5134 AND $valuesCondition
5135 ";
5136
5137 if( $this->getStep() !== NULL )
5138 {
5139 $query .= " AND step = %s ";
5140 $types[] = 'integer';
5141 $values[] = $this->getStep();
5142 }
5143
5144 $ilDB->manipulateF($query, $types, $values);
5145 }
5146
5147 protected function duplicateIntermediateSolutionAuthorized($activeId, $passIndex)
5148 {
5149 foreach($this->getSolutionValues($activeId, $passIndex, false) as $rec)
5150 {
5151 $this->saveCurrentSolution($activeId, $passIndex, $rec['value1'], $rec['value2'], true, $rec['tstamp']);
5152 }
5153 }
5154
5155 protected function forceExistingIntermediateSolution($activeId, $passIndex, $considerDummyRecordCreation)
5156 {
5157 $intermediateSolution = $this->getSolutionValues($activeId, $passIndex, false);
5158
5159 if( !count($intermediateSolution) )
5160 {
5161 // make the authorized solution intermediate (keeping timestamps)
5162 // this keeps the solution_ids in synch with eventually selected in $_POST['deletefiles']
5163 $this->updateCurrentSolutionsAuthorization($activeId, $passIndex, false, true);
5164
5165 // create a backup as authorized solution again (keeping timestamps)
5166 $this->duplicateIntermediateSolutionAuthorized($activeId, $passIndex);
5167
5168 if( $considerDummyRecordCreation )
5169 {
5170 // create an additional dummy record to indicate the existence of an intermediate solution
5171 // even if all entries are deleted from the intermediate solution later
5172 $this->saveCurrentSolution($activeId, $passIndex, null, null, false, null);
5173 }
5174 }
5175 }
5176 // hey.
5177
5181 public static function setResultGateway($resultGateway)
5182 {
5183 self::$resultGateway = $resultGateway;
5184 }
5185
5189 public static function getResultGateway()
5190 {
5191 return self::$resultGateway;
5192 }
5193
5197 public function setStep($step)
5198 {
5199 $this->step = $step;
5200 }
5201
5205 public function getStep()
5206 {
5207 return $this->step;
5208 }
5209
5215 public static function sumTimesInISO8601FormatH_i_s_Extended($time1, $time2)
5216 {
5219 return gmdate('H:i:s', $time);
5220 }
5221
5227 {
5228 $sec = 0;
5229 $time_array = explode(':',$time);
5230 if( sizeof($time_array) == 3)
5231 {
5232 $sec += $time_array[0] * 3600;
5233 $sec += $time_array[1] * 60;
5234 $sec += $time_array[2];
5235 }
5236 return $sec;
5237 }
5238
5239 public function toJSON()
5240 {
5241 return json_encode(array());
5242 }
5243
5244 abstract public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null);
5245
5246 // hey: prevPassSolutions - check for authorized solution
5247 public function intermediateSolutionExists($active_id, $pass)
5248 {
5249 $solutionAvailability = $this->lookupForExistingSolutions($active_id, $pass);
5250 return (bool)$solutionAvailability['intermediate'];
5251 }
5252 public function authorizedSolutionExists($active_id, $pass)
5253 {
5254 $solutionAvailability = $this->lookupForExistingSolutions($active_id, $pass);
5255 return (bool)$solutionAvailability['authorized'];
5256 }
5258 {
5259 $solutionAvailability = $this->lookupForExistingSolutions($active_id, $pass);
5260 return (bool)$solutionAvailability['authorized'] || (bool)$solutionAvailability['intermediate'];
5261 }
5262 // hey.
5263
5269 protected function lookupMaxStep($active_id, $pass)
5270 {
5272 global $ilDB;
5273
5274 $res = $ilDB->queryF(
5275 "SELECT MAX(step) max_step FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
5276 array("integer", "integer", "integer"), array($active_id, $pass, $this->getId())
5277 );
5278
5279 $row = $ilDB->fetchAssoc($res);
5280
5281 $maxStep = $row['max_step'];
5282
5283 return $maxStep;
5284 }
5285
5286// fau: testNav - new function lookupForExistingSolutions
5293 public function lookupForExistingSolutions($activeId, $pass)
5294 {
5296 global $ilDB;
5297
5298 $return = array(
5299 'authorized' => false,
5300 'intermediate' => false
5301 );
5302
5303 $query = "
5304 SELECT authorized, COUNT(*) cnt
5305 FROM tst_solutions
5306 WHERE active_fi = %s
5307 AND question_fi = %s
5308 AND pass = %s
5309 ";
5310
5311 if( $this->getStep() !== NULL )
5312 {
5313 $query .= " AND step = " . $ilDB->quote((int)$this->getStep(), 'integer') . " ";
5314 }
5315
5316 $query .= "
5317 GROUP BY authorized
5318 ";
5319
5320 $result = $ilDB->queryF($query, array('integer', 'integer', 'integer'), array($activeId, $this->getId(), $pass));
5321
5322 while ($row = $ilDB->fetchAssoc($result))
5323 {
5324 if ($row['authorized']) {
5325 $return['authorized'] = $row['cnt'] > 0;
5326 }
5327 else
5328 {
5329 $return['intermediate'] = $row['cnt'] > 0;
5330 }
5331 }
5332 return $return;
5333 }
5334// fau.
5335
5336 public function removeExistingSolutions($activeId, $pass)
5337 {
5338 global $ilDB;
5339
5340 $query = "
5341 DELETE FROM tst_solutions
5342 WHERE active_fi = %s
5343 AND question_fi = %s
5344 AND pass = %s
5345 ";
5346
5347 if( $this->getStep() !== NULL )
5348 {
5349 $query .= " AND step = " . $ilDB->quote((int)$this->getStep(), 'integer') . " ";
5350 }
5351
5352 return $ilDB->manipulateF($query, array('integer', 'integer', 'integer'),
5353 array($activeId, $this->getId(), $pass)
5354 );
5355 }
5356
5357 public function resetUsersAnswer($activeId, $pass)
5358 {
5359 $this->removeExistingSolutions($activeId, $pass);
5360 $this->removeResultRecord($activeId, $pass);
5361
5362 $this->log($activeId, "log_user_solution_willingly_deleted");
5363
5364 self::_updateTestPassResults(
5365 $activeId, $pass, $this->areObligationsToBeConsidered(), $this->getProcessLocker(), $this->getTestId()
5366 );
5367 }
5368
5369 public function removeResultRecord($activeId, $pass)
5370 {
5371 global $ilDB;
5372
5373 $query = "
5374 DELETE FROM tst_test_result
5375 WHERE active_fi = %s
5376 AND question_fi = %s
5377 AND pass = %s
5378 ";
5379
5380 if( $this->getStep() !== NULL )
5381 {
5382 $query .= " AND step = " . $ilDB->quote((int)$this->getStep(), 'integer') . " ";
5383 }
5384
5385 return $ilDB->manipulateF($query, array('integer', 'integer', 'integer'),
5386 array($activeId, $this->getId(), $pass)
5387 );
5388 }
5389
5390 public static function missingResultRecordExists($activeId, $pass, $questionIds)
5391 {
5392 global $ilDB;
5393
5394 $IN_questionIds = $ilDB->in('question_fi', $questionIds, false, 'integer');
5395
5396 $query = "
5397 SELECT COUNT(*) cnt
5398 FROM tst_test_result
5399 WHERE active_fi = %s
5400 AND pass = %s
5401 AND $IN_questionIds
5402 ";
5403
5404 $row = $ilDB->fetchAssoc($ilDB->queryF(
5405 $query, array('integer', 'integer'), array($activeId, $pass)
5406 ));
5407
5408 return $row['cnt'] < count($questionIds);
5409 }
5410
5411 public static function getQuestionsMissingResultRecord($activeId, $pass, $questionIds)
5412 {
5413 global $ilDB;
5414
5415 $IN_questionIds = $ilDB->in('question_fi', $questionIds, false, 'integer');
5416
5417 $query = "
5418 SELECT question_fi
5419 FROM tst_test_result
5420 WHERE active_fi = %s
5421 AND pass = %s
5422 AND $IN_questionIds
5423 ";
5424
5425 $res = $ilDB->queryF(
5426 $query, array('integer', 'integer'), array($activeId, $pass)
5427 );
5428
5429 $questionsHavingResultRecord = array();
5430
5431 while($row = $ilDB->fetchAssoc($res))
5432 {
5433 $questionsHavingResultRecord[] = $row['question_fi'];
5434 }
5435
5436 $questionsMissingResultRecordt = array_diff(
5437 $questionIds, $questionsHavingResultRecord
5438 );
5439
5440 return $questionsMissingResultRecordt;
5441 }
5442
5443 public static function lookupResultRecordExist($activeId, $questionId, $pass)
5444 {
5445 global $ilDB;
5446
5447 $query = "
5448 SELECT COUNT(*) cnt
5449 FROM tst_test_result
5450 WHERE active_fi = %s
5451 AND question_fi = %s
5452 AND pass = %s
5453 ";
5454
5455 $row = $ilDB->fetchAssoc($ilDB->queryF($query, array('integer', 'integer', 'integer'), array($activeId, $questionId, $pass)));
5456
5457 return $row['cnt'] > 0;
5458 }
5459
5464 public function fetchValuePairsFromIndexedValues(array $indexedValues)
5465 {
5466 $valuePairs = array();
5467
5468 foreach($indexedValues as $value1 => $value2)
5469 {
5470 $valuePairs[] = array('value1' => $value1, 'value2' => $value2);
5471 }
5472
5473 return $valuePairs;
5474 }
5475
5480 public function fetchIndexedValuesFromValuePairs(array $valuePairs)
5481 {
5482 $indexedValues = array();
5483
5484 foreach($valuePairs as $valuePair)
5485 {
5486 $indexedValues[ $valuePair['value1'] ] = $valuePair['value2'];
5487 }
5488
5489 return $indexedValues;
5490 }
5491
5496 {
5498 }
5499
5504 {
5505 $this->obligationsToBeConsidered = $obligationsToBeConsidered;
5506 }
5507
5508 public function updateTimestamp()
5509 {
5510 global $ilDB;
5511
5512 $ilDB->manipulateF("UPDATE qpl_questions SET tstamp = %s WHERE question_id = %s",
5513 array('integer', 'integer'),
5514 array(time(), $this->getId())
5515 );
5516 }
5517
5518// fau: testNav - new function getTestQuestionConfig()
5519 // hey: prevPassSolutions - get caching independent from configuration (config once)
5520 // renamed: getTestPresentationConfig() -> does the caching
5521 // completed: extracted instance building
5522 // avoids configuring cached instances on every access
5523 // allows a stable reconfigure of the instance from outside
5528
5534 {
5535 if( $this->testQuestionConfigInstance === null )
5536 {
5537 $this->testQuestionConfigInstance = $this->buildTestPresentationConfig();
5538 }
5539
5541 }
5542
5551 protected function buildTestPresentationConfig()
5552 {
5553 include_once('Modules/TestQuestionPool/classes/class.ilTestQuestionConfig.php');
5554 return new ilTestQuestionConfig();
5555 }
5556 // hey.
5557// fau.
5558}
sprintf('%.4f', $callTime)
$worksheet
$result
$_GET["client_id"]
$_POST["username"]
$_SESSION["AccountId"]
An exception for terminatinating execution or to throw for unit testing.
const IL_COMP_MODULE
Abstract basic class which is to be extended by the concrete assessment question type classes.
getTotalAnswers()
get total number of answers
$export_image_path
(Web) Path to images
moveUploadedMediaFile($file, $name)
Move an uploaded media file to an public accessible temp dir to present it.
isNonEmptyItemListPostSubmission($postSubmissionFieldname)
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass.
static includePluginClass($questionType, $withGuiClass)
static getFeedbackClassNameByQuestionType($questionType)
setPreventRteUsage($a_val)
Set prevent rte usage.
removeResultRecord($activeId, $pass)
static _getOriginalId($question_id)
Returns the original id of a question.
const KEY_VALUES_IMPLOSION_SEPARATOR
setProcessLocker($processLocker)
formatSAQuestion($a_q)
Format self assessment question.
setShuffle($shuffle=true)
Sets the shuffle flag.
setId($id=-1)
Sets the id of the assQuestion object.
keyInArray($searchkey, $array)
returns TRUE if the key occurs in an array
setOriginalId($original_id)
static _instantiateQuestion($question_id)
getQuestionTypeID()
Returns the question type of the question.
setObjId($obj_id=0)
Set the object id of the container object.
static sumTimesInISO8601FormatH_i_s_Extended($time1, $time2)
static instantiateQuestionGUI($a_question_id)
Creates an instance of a question gui with a given question id.
static isAllowedImageFileExtension($mimeType, $fileExtension)
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
Creates an Excel worksheet for the detailed cumulated results of this question.
copySuggestedSolutionFiles($source_questionpool_id, $source_question_id)
static originalQuestionExists($questionId)
beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
static isObligationPossible($questionId)
returns boolean wether it is possible to set this question type as obligatory or not considering the ...
static setForcePassResultUpdateEnabled($forcePassResultsUpdateEnabled)
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
setSuggestedSolution($solution_id="", $subquestion_index=0, $is_import=false)
Sets a suggested solution for the question.
static _getMaximumPoints($question_id)
Returns the maximum points, a learner can reach answering the question.
static _getReachedPoints($active_id, $question_id, $pass=NULL)
Returns the points, a learner has reached answering the question.
saveQuestionDataToDb($original_id="")
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.
QTIMaterialToString($a_material)
Reads an QTI material tag an creates a text string.
syncSuggestedSolutionFiles($original_id)
Syncs the files of a suggested solution if the question is synced.
static _getSolutionMaxPass($question_id, $active_id)
Returns the maximum pass a users question solution.
isClone($question_id="")
Checks whether the question is a clone of another question or not.
static fetchMimeTypeIdentifier($contentTypeString)
fixUnavailableSkinImageSources($html)
adjustReachedPointsByScoringOptions($points, $active_id, $pass=NULL)
Adjust the given reached points by checks for all special scoring options in the test container.
deletePageOfQuestion($question_id)
Deletes the page object of a question with a given ID.
lmMigrateQuestionTypeGenericContent(ilAssSelfAssessmentMigrator $migrator)
duplicateSkillAssignments($srcParentId, $srcQuestionId, $trgParentId, $trgQuestionId)
savePreviewData(ilAssQuestionPreviewSession $previewSession)
deleteSolutionRecordByValues($activeId, $passIndex, $authorized, $matchValues)
isComplete()
Returns true, if a question is complete for use.
const ADDITIONAL_CONTENT_EDITING_MODE_DEFAULT
constant for additional content editing mode "default"
log($active_id, $langVar)
static _getSuggestedSolutionCount($question_id)
Returns the number of suggested solutions associated with a question.
isInUse($question_id="")
Checks whether the question is in use or not.
static resetOriginalId($questionId)
static _areAnswered($a_user_id, $a_question_ids)
Checks if an array of question ids is answered by an user or not.
isPreviewSolutionCorrect(ilAssQuestionPreviewSession $previewSession)
cleanupMediaObjectUsage()
synchronises appearances of media objects in the question with media object usage table
deleteDummySolutionRecord($activeId, $passIndex)
getUserSolutionPreferingIntermediate($active_id, $pass=NULL)
getId()
Gets the id of the assQuestion object.
static saveOriginalId($questionId, $originalId)
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true, $tstamp=null)
fetchValuePairsFromIndexedValues(array $indexedValues)
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
getObjId()
Get the object id of the container object.
questionTitleExists($questionpool_id, $title)
Returns TRUE if the question title exists in the database.
supportsJavascriptOutput()
Returns true if the question type supports JavaScript output.
persistWorkingState($active_id, $pass=NULL, $obligationsEnabled=false, $authorized=true)
persists the working state for current testactive and testpass
static _getQuestionTypeName($type_tag)
Return the translation for a given question type tag.
getSuggestedSolutionPath()
Returns the path for a suggested solution.
removeExistingSolutions($activeId, $pass)
setTitle($title="")
Sets the title string of the assQuestion object.
static _getTitle($a_q_id)
Returns the title of a question.
lookupTestId($active_id)
getOwner()
Gets the creator/owner ID of the assQuestion object.
getAdditionalContentEditingMode()
getter for additional content editing mode for this question
static _isUsedInRandomTest($question_id="")
Checks whether the question is used in a random test or not.
getSolutionRecordById($solutionId)
isHTML($a_text)
Checks if a given string contains HTML or not.
buildTestPresentationConfig()
build basic test question configuration instance
setObligationsToBeConsidered($obligationsToBeConsidered)
persistPreviewState(ilAssQuestionPreviewSession $previewSession)
persists the preview state for current user and question
static $forcePassResultsUpdateEnabled
addQuestionChangeListener(ilQuestionChangeListener $listener)
calculateReachedPoints($active_id, $pass=NULL, $authorizedSolution=true, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $targetQuestionId)
Will be called when a question is copied (into another question pool)
duplicateIntermediateSolutionAuthorized($activeId, $passIndex)
authorizedOrIntermediateSolutionExists($active_id, $pass)
prepareTextareaOutput($txt_output, $prepare_for_latex_output=FALSE, $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output in tests.
resetUsersAnswer($activeId, $pass)
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
getJavaPath()
Returns the image path for web accessable images of a question.
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...
migrateContentForLearningModule(ilAssSelfAssessmentMigrator $migrator)
getAdditionalTableName()
Returns the name of the additional question data table in the database.
setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
Sets the estimated working time of a question from given hour, minute and second.
fetchIndexedValuesFromValuePairs(array $valuePairs)
getSuggestedSolutionTitle($subquestion_index=0)
Returns the title of a suggested solution at a given subquestion_index.
static getObjectClassNameByQuestionType($questionType)
deductHintPointsFromReachedPoints(ilAssQuestionPreviewSession $previewSession, $reachedPoints)
static $allowedImageMaterialFileExtensionsByMimeType
setExportImagePath($a_path)
isValidAdditionalContentEditingMode($additionalContentEditingMode)
returns the fact wether the passed additional content mode is valid or not
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.
setEstimatedWorkingTimeFromDurationString($durationString)
Sets the estimated working time of a question from a given datetime string.
getSuggestedSolutionPathWeb()
Returns the web path for a suggested solution.
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $previewSession)
setNewOriginalId($newId)
buildImagePath($questionId, $parentObjectId)
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.
static explodeKeyValues($keyValues)
getFlashPath()
Returns the image path for web accessable flash files of a question.
__get($value)
Object getter.
static logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
removeCurrentSolution($active_id, $pass, $authorized=true)
getSuggestedSolution($subquestion_index=0)
Returns a suggested solution for a given subquestion index.
buildHashedImageFilename($plain_image_filename, $unique=false)
updateCurrentSolution($solutionId, $value1, $value2, $authorized=true)
removeSolutionRecordById($solutionId)
static getNumExistingSolutionRecords($activeId, $pass, $questionId)
returns the number of existing solution records for the given test active / pass and given question i...
static implodeKeyValues($keyValues)
static convertISO8601FormatH_i_s_ExtendedToSeconds($time)
duplicateSuggestedSolutionFiles($parent_id, $question_id)
Duplicates the files of a suggested solution if the question is duplicated.
setAuthor($author="")
Sets the authors name of the assQuestion object.
$arrData
Associative array to store properties.
static isFileAvailable($file)
getQuestionType()
Returns the question type of the question.
deleteSuggestedSolutions()
Deletes all suggestes solutions in the database.
getPoints()
Returns the maximum available points for the question.
getOutputType()
Gets the output type.
ensureCurrentTestPass($active_id, $pass)
static isCoreQuestionType($questionType)
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
getActiveUserData($active_id)
Returns the user id and the test id for a given active id.
static _updateTestResultCache($active_id, ilAssQuestionProcessLocker $processLocker=null)
@TODO Move this to a proper place.
setExternalId($external_id)
getValidAdditionalContentEditingModes()
getter for valid additional content editing modes
static _getSuggestedSolutionOutput($question_id)
Returns the output of the suggested solution.
static _needsManualScoring($question_id)
intermediateSolutionExists($active_id, $pass)
_getTotalAnswers($a_q_id)
get number of answers for question id (static) note: do not use $this inside this method
static _questionExistsInTest($question_id, $test_id)
authorizedSolutionExists($active_id, $pass)
getJavaPathWeb()
Returns the web image path for web accessable java applets of a question.
onDuplicate($originalParentId, $originalQuestionId, $duplicateParentId, $duplicateQuestionId)
Will be called when a question is duplicated (inside a question pool or for insertion in a test)
getTestId()
Gets the test id of the assQuestion object.
setShuffler(ilArrayElementShuffler $shuffler)
__set($key, $value)
Object setter.
getTestOutputSolutions($activeId, $pass)
calculateResultsFromSolution($active_id, $pass=NULL, $obligationsEnabled=false)
Calculates the question results from a previously saved question solution.
removeIntermediateSolution($active_id, $pass)
_resolveIntLinks($question_id)
saveWorkingData($active_id, $pass=NULL, $authorized=true)
Saves the learners input of the question to the database.
static _getInternalLinkHref($target="")
getSelfAssessmentEditingMode()
Get Self-Assessment Editing Mode.
static $allowedCharsetsByMimeType
getTitleFilenameCompliant()
returns the object title prepared to be used as a filename
copyPageOfQuestion($a_q_id)
static isForcePassResultUpdateEnabled()
static lookupOriginalParentObjId($originalQuestionId)
returns the parent object id for given original question id (should be a qpl id, but theoretically it...
_resolveInternalLink($internal_link)
getTestPresentationConfig()
Get the test question configuration (initialised once)
static & _instanciateQuestionGUI($question_id)
Creates an instance of a question gui with a given question id.
getSolutionValues($active_id, $pass=NULL, $authorized=true)
Loads solutions of a given user from the database an returns it.
_questionExistsInPool($question_id)
Returns true if the question already exists in the database and is assigned to a question pool.
setOutputType($outputType=OUTPUT_HTML)
Sets the output type.
static lookupParentObjId($questionId)
@global ilDBInterface $ilDB
forceExistingIntermediateSolution($activeId, $passIndex, $considerDummyRecordCreation)
static _getTotalRightAnswers($a_q_id)
get number of answers for question id (static) note: do not use $this inside this method
static _getQuestionText($a_q_id)
Returns question text.
static $imageSourceFixReplaceMap
static getGuiClassNameByQuestionType($questionType)
getDefaultNrOfTries()
Get Default Nr of Tries.
static _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...
setSelfAssessmentEditingMode($a_selfassessmenteditingmode)
Set Self-Assessment Editing Mode.
static $allowedFileExtensionsByMimeType
static setResultGateway($resultGateway)
getShuffle()
Gets the shuffle flag.
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
getFlashPathWeb()
Returns the web image path for web accessable flash applications of a question.
static includeCoreClass($questionType, $withGuiClass)
createPageObject()
create page object of question
static missingResultRecordExists($activeId, $pass, $questionIds)
static getKeyValuesImplosionSeparator()
copyXHTMLMediaObjectsOfQuestion($a_q_id)
const ADDITIONAL_CONTENT_EDITING_MODE_PAGE_OBJECT
constant for additional content editing mode "pageobject"
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 ...
getSuggestedSolutions()
Return the suggested solutions.
& _getSuggestedSolution($question_id, $subquestion_index=0)
Returns a suggested solution for a given subquestion index.
getAnswerTableName()
Returns the name of the answer table in the database.
fixSvgToPng($imageFilenameContainingString)
syncSkillAssignments($srcParentId, $srcQuestionId, $trgParentId, $trgQuestionId)
updateCurrentSolutionsAuthorization($activeId, $pass, $authorized, $keepTime=false)
static getQuestionsMissingResultRecord($activeId, $pass, $questionIds)
deleteAdditionalTableData($question_id)
Deletes datasets from the additional question table in the database.
static _includeClass($question_type, $gui=0)
Include the php class file for a given question type.
getTitle()
Gets the title string of the assQuestion object.
static _getQuestionTitle($question_id)
Returns the question title of a question with a given id.
setPoints($a_points)
Sets the maximum available points for the question.
afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
isAdditionalContentEditingModePageObject()
isser for additional "pageobject" content editing mode
duplicateQuestionHints($originalQuestionId, $duplicateQuestionId)
setComment($comment="")
Sets the comment string of the assQuestion object.
static _getQuestionInfo($question_id)
Returns question information from the database.
getComment()
Gets the comment string of the assQuestion object.
setTestId($id=-1)
Sets the test id of the assQuestion object.
getAdjustedReachedPoints($active_id, $pass=NULL, $authorizedSolution=true)
returns the reached points ...
getAuthor()
Gets the authors name of the assQuestion object.
static getQuestionTypeFromDb($question_id)
get question type for question id
setNrOfTries($a_nr_of_tries)
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
assQuestion constructor
getReachedPoints($active_id, $pass=NULL)
Returns the points, a learner has reached answering the question This is the fast way to get the poin...
$nr_of_tries
Number of tries.
static _getQuestionType($question_id)
Returns the question type of a question with a given id.
getQuestion()
Gets the question string of the question object.
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
static isAllowedImageMimeType($mimeType)
isAnswered($active_id, $pass=null)
returns boolean wether the question is answered during test pass or not
saveToDb($original_id="")
Saves the question to the database.
static _instanciateQuestion($question_id)
Creates an instance of a question with a given question id.
static _isWriteable($question_id, $user_id)
Returns true if the question is writeable by a certain user.
reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
Reworks the allready saved working data if neccessary.
_questionExists($question_id)
Returns true if the question already exists in the database.
pcArrayShuffle($array)
Shuffles the values of a given array.
setQuestion($question="")
Sets the question string of the question object.
getImagePathWeb()
Returns the web image path for web accessable images of a question.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
loadFromDb($question_id)
Loads the question from the database.
static getAllowedFileExtensionsForMimeType($mimeType)
static lookupResultRecordExist($activeId, $questionId, $pass)
setLastChange($lastChange)
static getResultGateway()
& getInstances()
Gets all instances of the question.
static getAllowedImageMaterialFileExtensions()
lookupCurrentTestPass($active_id, $pass)
createRandomSolution($test_id, $user_id)
getEstimatedWorkingTime()
Gets the estimated working time of a question.
getPreventRteUsage()
Get prevent rte usage.
deleteAnswers($question_id)
Deletes datasets from answers tables.
setDefaultNrOfTries($a_defaultnroftries)
Set Default Nr of Tries.
ensureNonNegativePoints($points)
Assessment hint page object.
static deleteHintsByQuestionIds($questionIds)
Deletes all question hints relating to questions included in given question ids.
static duplicateListForQuestion($originalQuestionId, $duplicateQuestionId)
duplicates a hint list from given original question id to given duplicate question id and returns an ...
static getListByQuestionId($questionId)
instantiates a question hint list for the passed question id
Question page object.
static _updateObjectiveResult($a_user_id, $a_active_id, $a_question_id)
static _getInstanceByType($a_type)
Factory method for creating purifier instances.
static _getIdForImportId($a_import_id)
get current object id for import id (static)
static _addLog($user_id, $object_id, $logtext, $question_id="", $original_id="", $test_only=FALSE, $test_ref_id=NULL)
Add an assessment log entry.
static _getLogLanguage()
retrieve the log language for assessment logging
static _getManualScoringTypes()
Retrieve the manual scoring settings as type strings.
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
Class ilObjMediaObject.
static _saveUsage($a_mob_id, $a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
Save usage of mob within another container (e.g.
static _getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
static _removeUsage($a_mob_id, $a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
Remove usage of mob in another container.
static _exists($a_id, $a_reference=false, $a_type=NULL)
checks wether a lm content object with specified id exists or not
static _isWriteable($object_id, $user_id)
Returns true, if the question pool is writeable by a given user.
static _updateQuestionCount($object_id)
Updates the number of available questions for a question pool in the database.
static getUsageOfObject($a_obj_id, $a_include_titles=false)
Get usage of object.
static _getParticipantData($active_id)
Retrieves a participant name from active id.
static _getResultPass($active_id)
Retrieves the pass number that should be counted for a given user.
static _getObjectIDFromActiveID($active_id)
Returns the ILIAS test object id for a given active id.
static _getCountSystem($active_id)
Gets the count system for the calculation of points.
static _lookupAuthor($obj_id)
Gets the authors name of the ilObjTest object.
static _getQuestionCountAndPointsForPassOfParticipant($active_id, $pass)
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static buildExamId($active_id, $pass, $test_obj_id=null)
static _getWorkingTimeOfParticipantForPass($active_id, $pass)
Returns the complete working time in seconds for a test participant.
static _getUserIdFromActiveId($active_id)
static _getScoreCutting($active_id)
Determines if the score of a question should be cut at 0 points or the score of the whole test.
static isQuestionObligatory($question_id)
checks wether the question with given id is marked as obligatory or not
static _lookupObjId($a_id)
static _lookupTitle($a_id)
lookup object title
static _getAllReferences($a_id)
get all reference ids of object
static getPluginObject($a_ctype, $a_cname, $a_slot_id, $a_pname)
Get plugin object.
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...
static _cleanupMediaObjectUsage($a_text, $a_usage_type, $a_usage_id)
Synchronises appearances of media objects in $a_text with media object usage table.
ILIAS Setting Class.
Taxonomy node <-> item assignment.
Test Question configuration.
static moveUploadedFile($a_file, $a_name, $a_target, $a_raise_errors=true, $a_mode="move_uploaded")
move uploaded file
static delDir($a_dir, $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
static isHTML($a_text)
Checks if a given string contains HTML or not.
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,...
static getASCIIFilename($a_filename)
convert utf8 to ascii filename
static makeDirParents($a_dir)
Create a new directory and all parent directories.
static getImagePath($img, $module_path="", $mode="output", $offline=false)
get image path (for images located in a template directory)
static removeTrailingPathSeparators($path)
static prepareFormOutput($a_str, $a_strip=false)
prepares string output for html forms @access public
static createDirectory($a_dir, $a_mod=0755)
create directory
static signFile($path_to_file)
static setTokenMaxLifetimeInSeconds($token_max_lifetime_in_seconds)
comment()
Definition: comment.php:2
$html
Definition: example_001.php:87
if(!is_dir( $entity_dir)) exit("Fatal Error ([A-Za-z0-9]+)\s+" &#(? foreach( $entity_files as $file) $output
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
$target_id
Definition: goto.php:51
global $ilCtrl
Definition: ilias.php:18
const OUTPUT_HTML
const OUTPUT_JAVASCRIPT
redirection script todo: (a better solution should control the processing via a xml file)
if(!file_exists("$old.txt")) if( $old===$new) if(file_exists("$new.txt")) $file
global $ilDB
global $DIC
$mobs
$ilUser
Definition: imgupload.php:18