ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.assMultipleChoice.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3 
4 require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
5 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
6 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
7 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
8 require_once './Modules/TestQuestionPool/interfaces/interface.iQuestionCondition.php';
9 require_once './Modules/TestQuestionPool/classes/class.ilUserQuestionResult.php';
10 require_once 'Modules/TestQuestionPool/interfaces/interface.ilAssSpecificFeedbackOptionLabelProvider.php';
11 
28 {
36  public $answers;
37 
46  public $output_type;
47 
48  public $isSingleline;
49  public $lastChange;
51 
53  protected $thumb_size;
54 
58  protected $selectionLimit;
59 
63  public function setIsSingleline($isSingleline)
64  {
65  $this->isSingleline = $isSingleline;
66  }
67 
71  public function getIsSingleline()
72  {
73  return $this->isSingleline;
74  }
75 
79  public function setLastChange($lastChange)
80  {
81  $this->lastChange = $lastChange;
82  }
83 
87  public function getLastChange()
88  {
89  return $this->lastChange;
90  }
91 
106  public function __construct(
107  $title = "",
108  $comment = "",
109  $author = "",
110  $owner = -1,
111  $question = "",
113  ) {
114  parent::__construct($title, $comment, $author, $owner, $question);
115  $this->output_type = $output_type;
116  $this->thumb_size = 150;
117  $this->answers = array();
118  $this->shuffle = 1;
119  $this->selectionLimit = null;
120  $this->feedback_setting = 0;
121  }
122 
126  public function getSelectionLimit()
127  {
128  return $this->selectionLimit;
129  }
130 
135  {
136  $this->selectionLimit = $selectionLimit;
137  }
138 
145  public function isComplete()
146  {
147  if (strlen($this->title) and ($this->author) and ($this->question) and (count($this->answers)) and ($this->getMaximumPoints() > 0)) {
148  return true;
149  } else {
150  return false;
151  }
152  }
153 
159  public function saveToDb($original_id = "")
160  {
164 
165  $this->ensureNoInvalidObligation($this->getId());
166  parent::saveToDb($original_id);
167  }
168 
172  protected function rebuildThumbnails()
173  {
174  if ($this->isSingleline && ($this->getThumbSize())) {
175  foreach ($this->getAnswers() as $answer) {
176  if (strlen($answer->getImage())) {
177  $this->generateThumbForFile($this->getImagePath(), $answer->getImage());
178  }
179  }
180  }
181  }
182 
186  public function getThumbPrefix()
187  {
188  return "thumb.";
189  }
190 
195  protected function generateThumbForFile($path, $file)
196  {
197  $filename = $path . $file;
198  if (@file_exists($filename)) {
199  $thumbpath = $path . $this->getThumbPrefix() . $file;
200  $path_info = @pathinfo($filename);
201  $ext = "";
202  switch (strtoupper($path_info['extension'])) {
203  case 'PNG':
204  $ext = 'PNG';
205  break;
206  case 'GIF':
207  $ext = 'GIF';
208  break;
209  default:
210  $ext = 'JPEG';
211  break;
212  }
213  ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbSize());
214  }
215  }
216 
222  public function loadFromDb($question_id)
223  {
224  global $ilDB;
225  $hasimages = 0;
226 
227  $result = $ilDB->queryF(
228  "SELECT qpl_questions.*, " . $this->getAdditionalTableName() . ".* FROM qpl_questions LEFT JOIN " . $this->getAdditionalTableName() . " ON " . $this->getAdditionalTableName() . ".question_fi = qpl_questions.question_id WHERE qpl_questions.question_id = %s",
229  array("integer"),
230  array($question_id)
231  );
232  if ($result->numRows() == 1) {
233  $data = $ilDB->fetchAssoc($result);
234  $this->setId($question_id);
235  $this->setObjId($data["obj_fi"]);
236  $this->setTitle($data["title"]);
237  $this->setNrOfTries($data['nr_of_tries']);
238  $this->setComment($data["description"]);
239  $this->setOriginalId($data["original_id"]);
240  $this->setAuthor($data["author"]);
241  $this->setPoints($data["points"]);
242  $this->setOwner($data["owner"]);
243  include_once("./Services/RTE/classes/class.ilRTE.php");
244  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
245  $shuffle = (is_null($data['shuffle'])) ? true : $data['shuffle'];
246  $this->setShuffle($shuffle);
247  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
248  $this->setThumbSize($data['thumb_size']);
249  $this->isSingleline = ($data['allow_images']) ? false : true;
250  $this->lastChange = $data['tstamp'];
251  $this->setSelectionLimit((int) $data['selection_limit'] > 0 ? (int) $data['selection_limit'] : null);
252  $this->feedback_setting = $data['feedback_setting'];
253 
254  try {
255  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
256  } catch (ilTestQuestionPoolException $e) {
257  }
258  }
259 
260  $result = $ilDB->queryF(
261  "SELECT * FROM qpl_a_mc WHERE question_fi = %s ORDER BY aorder ASC",
262  array('integer'),
263  array($question_id)
264  );
265  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMultipleResponseImage.php";
266  if ($result->numRows() > 0) {
267  while ($data = $ilDB->fetchAssoc($result)) {
268  $imagefilename = $this->getImagePath() . $data["imagefile"];
269  if (!@file_exists($imagefilename)) {
270  $data["imagefile"] = "";
271  }
272  include_once("./Services/RTE/classes/class.ilRTE.php");
273  $data["answertext"] = ilRTE::_replaceMediaObjectImageSrc($data["answertext"], 1);
274  array_push($this->answers, new ASS_AnswerMultipleResponseImage($data["answertext"], $data["points"], $data["aorder"], $data["points_unchecked"], $data["imagefile"]));
275  }
276  }
277 
278  parent::loadFromDb($question_id);
279  }
280 
284  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
285  {
286  if ($this->id <= 0) {
287  // The question has not been saved. It cannot be duplicated
288  return;
289  }
290  // duplicate the question in database
291  $this_id = $this->getId();
292  $thisObjId = $this->getObjId();
293 
294  $clone = $this;
295  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
297  $clone->id = -1;
298 
299  if ((int) $testObjId > 0) {
300  $clone->setObjId($testObjId);
301  }
302 
303  if ($title) {
304  $clone->setTitle($title);
305  }
306 
307  if ($author) {
308  $clone->setAuthor($author);
309  }
310  if ($owner) {
311  $clone->setOwner($owner);
312  }
313 
314  if ($for_test) {
315  $clone->saveToDb($original_id);
316  } else {
317  $clone->saveToDb();
318  }
319 
320  // copy question page content
321  $clone->copyPageOfQuestion($this_id);
322  // copy XHTML media objects
323  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
324  // duplicate the images
325  $clone->duplicateImages($this_id, $thisObjId);
326 
327  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
328 
329  return $clone->id;
330  }
331 
335  public function copyObject($target_questionpool_id, $title = "")
336  {
337  if ($this->id <= 0) {
338  // The question has not been saved. It cannot be duplicated
339  return;
340  }
341  // duplicate the question in database
342  $clone = $this;
343  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
345  $clone->id = -1;
346  $source_questionpool_id = $this->getObjId();
347  $clone->setObjId($target_questionpool_id);
348  if ($title) {
349  $clone->setTitle($title);
350  }
351  $clone->saveToDb();
352  // copy question page content
353  $clone->copyPageOfQuestion($original_id);
354  // copy XHTML media objects
355  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
356  // duplicate the image
357  $clone->copyImages($original_id, $source_questionpool_id);
358 
359  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
360 
361  return $clone->id;
362  }
363 
364  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
365  {
366  if ($this->id <= 0) {
367  // The question has not been saved. It cannot be duplicated
368  return;
369  }
370 
371  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
372 
373  $sourceQuestionId = $this->id;
374  $sourceParentId = $this->getObjId();
375 
376  // duplicate the question in database
377  $clone = $this;
378  $clone->id = -1;
379 
380  $clone->setObjId($targetParentId);
381 
382  if ($targetQuestionTitle) {
383  $clone->setTitle($targetQuestionTitle);
384  }
385 
386  $clone->saveToDb();
387  // copy question page content
388  $clone->copyPageOfQuestion($sourceQuestionId);
389  // copy XHTML media objects
390  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
391  // duplicate the image
392  $clone->copyImages($sourceQuestionId, $sourceParentId);
393 
394  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
395 
396  return $clone->id;
397  }
398 
405  public function getOutputType()
406  {
407  return $this->output_type;
408  }
409 
418  {
419  $this->output_type = $output_type;
420  }
421 
435  public function addAnswer(
436  $answertext = "",
437  $points = 0.0,
438  $points_unchecked = 0.0,
439  $order = 0,
440  $answerimage = ""
441  ) {
442  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMultipleResponseImage.php";
443  if (array_key_exists($order, $this->answers)) {
444  // insert answer
445  $answer = new ASS_AnswerMultipleResponseImage($answertext, $points, $order, $points_unchecked, $answerimage);
446  $newchoices = array();
447  for ($i = 0; $i < $order; $i++) {
448  array_push($newchoices, $this->answers[$i]);
449  }
450  array_push($newchoices, $answer);
451  for ($i = $order; $i < count($this->answers); $i++) {
452  $changed = $this->answers[$i];
453  $changed->setOrder($i+1);
454  array_push($newchoices, $changed);
455  }
456  $this->answers = $newchoices;
457  } else {
458  // add answer
459  $answer = new ASS_AnswerMultipleResponseImage($answertext, $points, count($this->answers), $points_unchecked, $answerimage);
460  array_push($this->answers, $answer);
461  }
462  }
463 
470  public function getAnswerCount()
471  {
472  return count($this->answers);
473  }
474 
483  public function getAnswer($index = 0)
484  {
485  if ($index < 0) {
486  return null;
487  }
488  if (count($this->answers) < 1) {
489  return null;
490  }
491  if ($index >= count($this->answers)) {
492  return null;
493  }
494 
495  return $this->answers[$index];
496  }
497 
505  public function deleteAnswer($index = 0)
506  {
507  if ($index < 0) {
508  return;
509  }
510  if (count($this->answers) < 1) {
511  return;
512  }
513  if ($index >= count($this->answers)) {
514  return;
515  }
516  $answer = $this->answers[$index];
517  if (strlen($answer->getImage())) {
518  $this->deleteImage($answer->getImage());
519  }
520  unset($this->answers[$index]);
521  $this->answers = array_values($this->answers);
522  for ($i = 0; $i < count($this->answers); $i++) {
523  if ($this->answers[$i]->getOrder() > $index) {
524  $this->answers[$i]->setOrder($i);
525  }
526  }
527  }
528 
534  public function flushAnswers()
535  {
536  $this->answers = array();
537  }
538 
544  public function getMaximumPoints()
545  {
546  $points = 0;
547  $allpoints = 0;
548  foreach ($this->answers as $key => $value) {
549  if ($value->getPoints() > $value->getPointsUnchecked()) {
550  $allpoints += $value->getPoints();
551  } else {
552  $allpoints += $value->getPointsUnchecked();
553  }
554  }
555  return $allpoints;
556  }
557 
569  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
570  {
571  if ($returndetails) {
572  throw new ilTestException('return details not implemented for ' . __METHOD__);
573  }
574 
575  global $ilDB;
576 
577  $found_values = array();
578  if (is_null($pass)) {
579  $pass = $this->getSolutionMaxPass($active_id);
580  }
581  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
582  while ($data = $ilDB->fetchAssoc($result)) {
583  if (strcmp($data["value1"], "") != 0) {
584  array_push($found_values, $data["value1"]);
585  }
586  }
587 
588  $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
589 
590  return $points;
591  }
592 
593  public function validateSolutionSubmit()
594  {
595  $submit = $this->getSolutionSubmit();
596 
597  if ($this->getSelectionLimit()) {
598  if (count($submit) > $this->getSelectionLimit()) {
599  $failureMsg = sprintf(
600  $this->lng->txt('ass_mc_sel_lim_exhausted_hint'),
601  $this->getSelectionLimit(),
602  $this->getAnswerCount()
603  );
604 
605  ilUtil::sendFailure($failureMsg, true);
606  return false;
607  }
608  }
609 
610  return true;
611  }
612 
613  protected function isForcedEmptySolution($solutionSubmit)
614  {
615  if (!count($solutionSubmit) && !empty($_POST['tst_force_form_diff_input'])) {
616  return true;
617  }
618 
619  return false;
620  }
621 
630  public function saveWorkingData($active_id, $pass = null, $authorized = true)
631  {
633  global $ilDB;
634 
635  if (is_null($pass)) {
636  include_once "./Modules/Test/classes/class.ilObjTest.php";
637  $pass = ilObjTest::_getPass($active_id);
638  }
639 
640  $entered_values = 0;
641 
642  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
643  $this->removeCurrentSolution($active_id, $pass, $authorized);
644 
645  $solutionSubmit = $this->getSolutionSubmit();
646 
647  foreach ($solutionSubmit as $value) {
648  if (strlen($value)) {
649  $this->saveCurrentSolution($active_id, $pass, $value, null, $authorized);
650  $entered_values++;
651  }
652  }
653 
654  // fau: testNav - write a dummy entry for the evil mc questions with "None of the above" checked
655  if ($this->isForcedEmptySolution($solutionSubmit)) {
656  $this->saveCurrentSolution($active_id, $pass, 'mc_none_above', null, $authorized);
657  $entered_values++;
658  }
659  // fau.
660  });
661 
662  if ($entered_values) {
663  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
665  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
666  }
667  } else {
668  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
670  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
671  }
672  }
673 
674  return true;
675  }
676 
677  public function saveAdditionalQuestionDataToDb()
678  {
680  global $ilDB;
681  $oldthumbsize = 0;
682  if ($this->isSingleline && ($this->getThumbSize())) {
683  // get old thumbnail size
684  $result = $ilDB->queryF(
685  "SELECT thumb_size FROM " . $this->getAdditionalTableName(
686  ) . " WHERE question_fi = %s",
687  array( "integer" ),
688  array( $this->getId() )
689  );
690  if ($result->numRows() == 1) {
691  $data = $ilDB->fetchAssoc($result);
692  $oldthumbsize = $data['thumb_size'];
693  }
694  }
695 
696  if (!$this->isSingleline) {
697  ilUtil::delDir($this->getImagePath());
698  }
699 
700  // save additional data
701  $ilDB->replace(
702  $this->getAdditionalTableName(),
703  array(
704  'shuffle' => array('text', $this->getShuffle()),
705  'allow_images' => array('text', $this->isSingleline ? 0 : 1),
706  'thumb_size' => array('integer', strlen($this->getThumbSize()) ? $this->getThumbSize() : null),
707  'selection_limit' => array('integer', $this->getSelectionLimit()),
708  'feedback_setting' => array('integer', $this->getSpecificFeedbackSetting())
709  ),
710  array(
711  'question_fi' => array('integer', $this->getId())
712  )
713  );
714  }
715 
716  public function saveAnswerSpecificDataToDb()
717  {
719  global $ilDB;
720  $ilDB->manipulateF(
721  "DELETE FROM qpl_a_mc WHERE question_fi = %s",
722  array( 'integer' ),
723  array( $this->getId() )
724  );
725 
726  foreach ($this->answers as $key => $value) {
727  $answer_obj = $this->answers[$key];
728  $next_id = $ilDB->nextId('qpl_a_mc');
729  $ilDB->manipulateF(
730  "INSERT INTO qpl_a_mc (answer_id, question_fi, answertext, points, points_unchecked, aorder, imagefile, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
731  array( 'integer', 'integer', 'text', 'float', 'float', 'integer', 'text', 'integer' ),
732  array(
733  $next_id,
734  $this->getId(),
735  ilRTE::_replaceMediaObjectImageSrc($answer_obj->getAnswertext(), 0),
736  $answer_obj->getPoints(),
737  $answer_obj->getPointsUnchecked(),
738  $answer_obj->getOrder(),
739  $answer_obj->getImage(),
740  time()
741  )
742  );
743  }
744  $this->rebuildThumbnails();
745  }
746 
750  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
751  {
752  // nothing to rework!
753  }
754 
755  public function syncWithOriginal()
756  {
757  if ($this->getOriginalId()) {
758  $this->syncImages();
759  parent::syncWithOriginal();
760  }
761  }
762 
768  public function getQuestionType()
769  {
770  return "assMultipleChoice";
771  }
772 
778  public function getAdditionalTableName()
779  {
780  return "qpl_qst_mc";
781  }
782 
788  public function getAnswerTableName()
789  {
790  return "qpl_a_mc";
791  }
792 
800  public function setImageFile($image_filename, $image_tempfilename = "")
801  {
802  $result = 0;
803  if (!empty($image_tempfilename)) {
804  $image_filename = str_replace(" ", "_", $image_filename);
805  $imagepath = $this->getImagePath();
806  if (!file_exists($imagepath)) {
807  ilUtil::makeDirParents($imagepath);
808  }
809  if (!ilUtil::moveUploadedFile($image_tempfilename, $image_filename, $imagepath . $image_filename)) {
810  $result = 2;
811  } else {
812  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
813  $mimetype = ilObjMediaObject::getMimeType($imagepath . $image_filename);
814  if (!preg_match("/^image/", $mimetype)) {
815  unlink($imagepath . $image_filename);
816  $result = 1;
817  } else {
818  // create thumbnail file
819  if ($this->isSingleline && ($this->getThumbSize())) {
820  $this->generateThumbForFile($imagepath, $image_filename);
821  }
822  }
823  }
824  }
825  return $result;
826  }
827 
833  protected function deleteImage($image_filename)
834  {
835  $imagepath = $this->getImagePath();
836  @unlink($imagepath . $image_filename);
837  $thumbpath = $imagepath . $this->getThumbPrefix() . $image_filename;
838  @unlink($thumbpath);
839  }
840 
841  public function duplicateImages($question_id, $objectId = null)
842  {
844  global $ilLog;
845 
846  $imagepath = $this->getImagePath();
847  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
848 
849  if ((int) $objectId > 0) {
850  $imagepath_original = str_replace("/$this->obj_id/", "/$objectId/", $imagepath_original);
851  }
852 
853  foreach ($this->answers as $answer) {
854  $filename = $answer->getImage();
855  if (strlen($filename)) {
856  if (!file_exists($imagepath)) {
857  ilUtil::makeDirParents($imagepath);
858  }
859 
860  if (file_exists($imagepath_original . $filename)) {
861  if (!copy($imagepath_original . $filename, $imagepath . $filename)) {
862  $ilLog->warning(sprintf(
863  "Could not clone source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
864  $imagepath_original . $filename,
865  $imagepath . $filename,
866  $question_id,
867  $this->id,
868  $objectId,
869  $this->obj_id
870  ));
871  }
872  }
873 
874  if (file_exists($imagepath_original . $this->getThumbPrefix() . $filename)) {
875  if (!copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
876  $ilLog->warning(sprintf(
877  "Could not clone thumbnail source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
878  $imagepath_original . $this->getThumbPrefix() . $filename,
879  $imagepath . $this->getThumbPrefix() . $filename,
880  $question_id,
881  $this->id,
882  $objectId,
883  $this->obj_id
884  ));
885  }
886  }
887  }
888  }
889  }
890 
891  public function copyImages($question_id, $source_questionpool)
892  {
893  global $ilLog;
894  $imagepath = $this->getImagePath();
895  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
896  $imagepath_original = str_replace("/$this->obj_id/", "/$source_questionpool/", $imagepath_original);
897  foreach ($this->answers as $answer) {
898  $filename = $answer->getImage();
899  if (strlen($filename)) {
900  if (!file_exists($imagepath)) {
901  ilUtil::makeDirParents($imagepath);
902  }
903  if (!@copy($imagepath_original . $filename, $imagepath . $filename)) {
904  $ilLog->write("image could not be duplicated!!!!", $ilLog->ERROR);
905  $ilLog->write("object: " . print_r($this, true), $ilLog->ERROR);
906  }
907  if (@file_exists($imagepath_original . $this->getThumbPrefix() . $filename)) {
908  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
909  $ilLog->write("image thumbnail could not be duplicated!!!!", $ilLog->ERROR);
910  $ilLog->write("object: " . print_r($this, true), $ilLog->ERROR);
911  }
912  }
913  }
914  }
915  }
916 
920  protected function syncImages()
921  {
922  global $ilLog;
923 
924  $imagepath = $this->getImagePath();
925 
926  $question_id = $this->getOriginalId();
927  $originalObjId = parent::lookupParentObjId($this->getOriginalId());
928  $imagepath_original = $this->getImagePath($question_id, $originalObjId);
929 
930  ilUtil::delDir($imagepath_original);
931  foreach ($this->answers as $answer) {
932  $filename = $answer->getImage();
933  if (strlen($filename)) {
934  if (@file_exists($imagepath . $filename)) {
935  if (!file_exists($imagepath)) {
936  ilUtil::makeDirParents($imagepath);
937  }
938  if (!file_exists($imagepath_original)) {
939  ilUtil::makeDirParents($imagepath_original);
940  }
941  if (!@copy($imagepath . $filename, $imagepath_original . $filename)) {
942  $ilLog->write("image could not be duplicated!!!!", $ilLog->ERROR);
943  $ilLog->write("object: " . print_r($this, true), $ilLog->ERROR);
944  }
945  }
946  if (@file_exists($imagepath . $this->getThumbPrefix() . $filename)) {
947  if (!@copy($imagepath . $this->getThumbPrefix() . $filename, $imagepath_original . $this->getThumbPrefix() . $filename)) {
948  $ilLog->write("image thumbnail could not be duplicated!!!!", $ilLog->ERROR);
949  $ilLog->write("object: " . print_r($this, true), $ilLog->ERROR);
950  }
951  }
952  }
953  }
954  }
955 
959  public function getRTETextWithMediaObjects()
960  {
961  $text = parent::getRTETextWithMediaObjects();
962  foreach ($this->answers as $index => $answer) {
963  $text .= $this->feedbackOBJ->getSpecificAnswerFeedbackContent($this->getId(), $index);
964  $answer_obj = $this->answers[$index];
965  $text .= $answer_obj->getAnswertext();
966  }
967  return $text;
968  }
969 
973  public function &getAnswers()
974  {
975  return $this->answers;
976  }
977 
981  public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
982  {
983  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
984 
985  $solution = $this->getSolutionValues($active_id, $pass);
986 
987  $i = 1;
988  foreach ($this->getAnswers() as $id => $answer) {
989  $worksheet->setCell($startrow + $i, 0, $answer->getAnswertext());
990  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
991  $checked = false;
992  foreach ($solution as $solutionvalue) {
993  if ($id == $solutionvalue["value1"]) {
994  $checked = true;
995  }
996  }
997  if ($checked) {
998  $worksheet->setCell($startrow + $i, 1, 1);
999  } else {
1000  $worksheet->setCell($startrow + $i, 1, 0);
1001  }
1002  $i++;
1003  }
1004 
1005  return $startrow + $i + 1;
1006  }
1007 
1008  public function getThumbSize()
1009  {
1010  return $this->thumb_size;
1011  }
1012 
1013  public function setThumbSize($a_size)
1014  {
1015  $this->thumb_size = $a_size;
1016  }
1017 
1022  {
1023  foreach ($this->getAnswers() as $answer) {
1024  /* @var ASS_AnswerBinaryStateImage $answer */
1025  $answer->setAnswertext($migrator->migrateToLmContent($answer->getAnswertext()));
1026  }
1027  }
1028 
1032  public function toJSON()
1033  {
1034  require_once './Services/RTE/classes/class.ilRTE.php';
1035  $result = array();
1036  $result['id'] = (int) $this->getId();
1037  $result['type'] = (string) $this->getQuestionType();
1038  $result['title'] = (string) $this->getTitle();
1039  $result['question'] = $this->formatSAQuestion($this->getQuestion());
1040  $result['nr_of_tries'] = (int) $this->getNrOfTries();
1041  $result['shuffle'] = (bool) $this->getShuffle();
1042  $result['selection_limit'] = (int) $this->getSelectionLimit();
1043  $result['feedback'] = array(
1044  'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
1045  'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
1046  );
1047 
1048  $answers = array();
1049  $has_image = false;
1050  foreach ($this->getAnswers() as $key => $answer_obj) {
1051  if ((string) $answer_obj->getImage()) {
1052  $has_image = true;
1053  }
1054  array_push($answers, array(
1055  "answertext" => (string) $this->formatSAQuestion($answer_obj->getAnswertext()),
1056  "points_checked" => (float) $answer_obj->getPointsChecked(),
1057  "points_unchecked" => (float) $answer_obj->getPointsUnchecked(),
1058  "order" => (int) $answer_obj->getOrder(),
1059  "image" => (string) $answer_obj->getImage(),
1060  "feedback" => $this->formatSAQuestion(
1061  $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), $key)
1062  )
1063  ));
1064  }
1065  $result['answers'] = $answers;
1066 
1067  if ($has_image) {
1068  $result['path'] = $this->getImagePathWeb();
1069  $result['thumb'] = $this->getThumbSize();
1070  }
1071 
1072  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1073  $result['mobs'] = $mobs;
1074 
1075  return json_encode($result);
1076  }
1077 
1078  public function removeAnswerImage($index)
1079  {
1080  $answer = $this->answers[$index];
1081  if (is_object($answer)) {
1082  $this->deleteImage($answer->getImage());
1083  $answer->setImage('');
1084  }
1085  }
1086 
1087  public function getMultilineAnswerSetting()
1088  {
1089  global $ilUser;
1090 
1091  $multilineAnswerSetting = $ilUser->getPref("tst_multiline_answers");
1092  if ($multilineAnswerSetting != 1) {
1093  $multilineAnswerSetting = 0;
1094  }
1095  return $multilineAnswerSetting;
1096  }
1097 
1098  public function setMultilineAnswerSetting($a_setting = 0)
1099  {
1100  global $ilUser;
1101  $ilUser->writePref("tst_multiline_answers", $a_setting);
1102  }
1103 
1113  public function setSpecificFeedbackSetting($a_feedback_setting)
1114  {
1115  $this->feedback_setting = $a_feedback_setting;
1116  }
1117 
1127  public function getSpecificFeedbackSetting()
1128  {
1129  if ($this->feedback_setting) {
1130  return $this->feedback_setting;
1131  } else {
1132  return 1;
1133  }
1134  }
1135 
1137  {
1138  return 'feedback_correct_sc_mc';
1139  }
1140 
1152  public function isAnswered($active_id, $pass = null)
1153  {
1154  $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1155 
1156  return $numExistingSolutionRecords > 0;
1157  }
1158 
1170  public static function isObligationPossible($questionId)
1171  {
1173  global $ilDB;
1174 
1175  $query = "
1176  SELECT SUM(points) points_for_checked_answers
1177  FROM qpl_a_mc
1178  WHERE question_fi = %s AND points > 0
1179  ";
1180 
1181  $res = $ilDB->queryF($query, array('integer'), array($questionId));
1182 
1183  $row = $ilDB->fetchAssoc($res);
1184 
1185  return $row['points_for_checked_answers'] > 0;
1186  }
1187 
1196  public function ensureNoInvalidObligation($questionId)
1197  {
1199  global $ilDB;
1200 
1201  $query = "
1202  SELECT SUM(qpl_a_mc.points) points_for_checked_answers,
1203  test_question_id
1204 
1205  FROM tst_test_question
1206 
1207  INNER JOIN qpl_a_mc
1208  ON qpl_a_mc.question_fi = tst_test_question.question_fi
1209 
1210  WHERE tst_test_question.question_fi = %s
1211  AND tst_test_question.obligatory = 1
1212 
1213  GROUP BY test_question_id
1214  ";
1215 
1216  $res = $ilDB->queryF($query, array('integer'), array($questionId));
1217 
1218  $updateTestQuestionIds = array();
1219 
1220  while ($row = $ilDB->fetchAssoc($res)) {
1221  if ($row['points_for_checked_answers'] <= 0) {
1222  $updateTestQuestionIds[] = $row['test_question_id'];
1223  }
1224  }
1225 
1226  if (count($updateTestQuestionIds)) {
1227  $test_question_id__IN__updateTestQuestionIds = $ilDB->in(
1228  'test_question_id',
1229  $updateTestQuestionIds,
1230  false,
1231  'integer'
1232  );
1233 
1234  $query = "
1235  UPDATE tst_test_question
1236  SET obligatory = 0
1237  WHERE $test_question_id__IN__updateTestQuestionIds
1238  ";
1239 
1240  $ilDB->manipulate($query);
1241  }
1242  }
1243 
1247  protected function getSolutionSubmit()
1248  {
1249  $solutionSubmit = array();
1250  foreach ($_POST as $key => $value) {
1251  if (preg_match("/^multiple_choice_result_(\d+)/", $key)) {
1252  if (strlen($value)) {
1253  $solutionSubmit[] = $value;
1254  }
1255  }
1256  }
1257  return $solutionSubmit;
1258  }
1259 
1265  protected function calculateReachedPointsForSolution($found_values, $active_id = 0)
1266  {
1267  $points = 0;
1268  foreach ($this->answers as $key => $answer) {
1269  if (in_array($key, $found_values)) {
1270  $points += $answer->getPoints();
1271  } else {
1272  $points += $answer->getPointsUnchecked();
1273  }
1274  }
1275  if ($active_id) {
1276  include_once "./Modules/Test/classes/class.ilObjTest.php";
1277  $mc_scoring = ilObjTest::_getMCScoring($active_id);
1278  if (($mc_scoring == 0) && (count($found_values) == 0)) {
1279  $points = 0;
1280  }
1281  }
1282  return $points;
1283  }
1284 
1293  public function getOperators($expression)
1294  {
1295  require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
1297  }
1298 
1303  public function getExpressionTypes()
1304  {
1305  return array(
1310  );
1311  }
1312 
1321  public function getUserQuestionResult($active_id, $pass)
1322  {
1324  global $ilDB;
1325  $result = new ilUserQuestionResult($this, $active_id, $pass);
1326 
1327  $maxStep = $this->lookupMaxStep($active_id, $pass);
1328 
1329  if ($maxStep !== null) {
1330  $data = $ilDB->queryF(
1331  "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
1332  array("integer", "integer", "integer","integer"),
1333  array($active_id, $pass, $this->getId(), $maxStep)
1334  );
1335  } else {
1336  $data = $ilDB->queryF(
1337  "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
1338  array("integer", "integer", "integer"),
1339  array($active_id, $pass, $this->getId())
1340  );
1341  }
1342 
1343  while ($row = $ilDB->fetchAssoc($data)) {
1344  $result->addKeyValue($row["value1"], $row["value1"]);
1345  }
1346 
1347  $points = $this->calculateReachedPoints($active_id, $pass);
1348  $max_points = $this->getMaximumPoints();
1349 
1350  $result->setReachedPercentage(($points/$max_points) * 100);
1351 
1352  return $result;
1353  }
1354 
1363  public function getAvailableAnswerOptions($index = null)
1364  {
1365  if ($index !== null) {
1366  return $this->getAnswer($index);
1367  } else {
1368  return $this->getAnswers();
1369  }
1370  }
1371 
1372  protected function buildTestPresentationConfig()
1373  {
1374  $config = parent::buildTestPresentationConfig();
1375  $config->setUseUnchangedAnswerLabel($this->lng->txt('tst_mc_label_none_above'));
1376  return $config;
1377  }
1378 }
flushAnswers()
Deletes all answers.
static isObligationPossible($questionId)
returns boolean wether it is possible to set this question type as obligatory or not considering the ...
calculateReachedPointsForSolution($found_values, $active_id=0)
static makeDirParents($a_dir)
Create a new directory and all parent directories.
static logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
getId()
Gets the id of the assQuestion object.
addAnswer( $answertext="", $points=0.0, $points_unchecked=0.0, $order=0, $answerimage="")
Adds a possible answer for a multiple choice question.
Add rich text string
static _getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
static _getOriginalId($question_id)
Returns the original id of a question.
formatSAQuestion($a_q)
Format self assessment question.
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
$worksheet
toJSON()
Returns a JSON representation of the question.
Class iQuestionCondition.
static getMimeType($a_file, $a_external=null)
get mime type for file
generateThumbForFile($path, $file)
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
isComplete()
Returns true, if a multiple choice question is complete for use.
static getNumExistingSolutionRecords($activeId, $pass, $questionId)
returns the number of existing solution records for the given test active / pass and given question i...
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
Duplicates an assMultipleChoiceQuestion.
copyObject($target_questionpool_id, $title="")
Copies an assMultipleChoice object.
$result
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
rebuildThumbnails()
Rebuild the thumbnail images with a new thumbnail size.
reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
{}
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
Abstract basic class which is to be extended by the concrete assessment question type classes...
ASS_AnswerBinaryStateImage is a class for answers with a binary state indicator (checked/unchecked, set/unset) and an image file.
getSolutionValues($active_id, $pass=null, $authorized=true)
Loads solutions of a given user from the database an returns it.
setSelectionLimit($selectionLimit)
setId($id=-1)
Sets the id of the assQuestion object.
getImagePathWeb()
Returns the web image path for web accessable images of a question.
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
Sets the estimated working time of a question from given hour, minute and second. ...
setOutputType($output_type=OUTPUT_ORDER)
Sets the output type of the assMultipleChoice object.
getQuestionType()
Returns the question type of the question.
$index
Definition: metadata.php:60
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
setNrOfTries($a_nr_of_tries)
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
loadFromDb($question_id)
Loads a assMultipleChoice object from a database.
setShuffle($shuffle=true)
Sets the shuffle flag.
static _replaceMediaObjectImageSrc($a_text, $a_direction=0, $nic=IL_INST_ID)
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
Class for multiple choice tests.
getObjId()
Get the object id of the container object.
getShuffle()
Gets the shuffle flag.
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
Base Exception for all Exceptions relating to Modules/Test.
saveToDb($original_id="")
Saves a assMultipleChoice object to a database.
deleteAnswer($index=0)
Deletes an answer with a given index.
isAnswered($active_id, $pass=null)
returns boolean wether the question is answered during test pass or not
static _getLogLanguage()
retrieve the log language for assessment logging
setAuthor($author="")
Sets the authors name of the assQuestion object.
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
foreach($_POST as $key=> $value) $res
$mobs
Class ilUserQuestionResult.
getAnswerCount()
Returns the number of answers.
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true, $tstamp=null)
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
getSpecificFeedbackSetting()
Gets the current feedback settings in effect for the question.
isForcedEmptySolution($solutionSubmit)
$text
Definition: errorreport.php:18
Interface ilObjAnswerScoringAdjustable.
getQuestion()
Gets the question string of the question object.
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
static convertImage( $a_from, $a_to, $a_target_format="", $a_geometry="", $a_background_color="")
convert image
$ilUser
Definition: imgupload.php:18
__construct( $title="", $comment="", $author="", $owner=-1, $question="", $output_type=OUTPUT_ORDER)
assMultipleChoice constructor
syncImages()
Sync images of a MC question on synchronisation with the original question.
getOperators($expression)
Get all available operations for a specific question.
setIsSingleline($isSingleline)
$query
getAnswer($index=0)
Returns an answer with a given index.
& getAnswers()
Returns a reference to the answers array.
setSpecificFeedbackSetting($a_feedback_setting)
Sets the feedback settings in effect for the question.
deleteImage($image_filename)
Deletes an image file.
getExpressionTypes()
Get all available expression types for a specific question.
Create styles array
The data for the language used.
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
setPoints($a_points)
Sets the maximum available points for the question.
saveQuestionDataToDb($original_id="")
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
setImageFile($image_filename, $image_tempfilename="")
Sets the image file and uploads the image to the object&#39;s image directory.
setQuestion($question="")
Sets the question string of the question object.
Interface ilObjQuestionScoringAdjustable.
removeCurrentSolution($active_id, $pass, $authorized=true)
getAdditionalTableName()
Returns the name of the additional question data table in the database.
static _getMCScoring($active_id)
Gets the scoring type for multiple choice questions.
global $ilDB
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
{}
setOriginalId($original_id)
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass...
$i
Definition: disco.tpl.php:19
getAnswerTableName()
Returns the name of the answer table in the database.
getTitle()
Gets the title string of the assQuestion object.
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
const OUTPUT_ORDER
getOutputType()
Gets the multiple choice output type which is either OUTPUT_ORDER (=0) or OUTPUT_RANDOM (=1)...
setTitle($title="")
Sets the title string of the assQuestion object.
setObjId($obj_id=0)
Set the object id of the container object.
static delDir($a_dir, $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
$key
Definition: croninfo.php:18
setComment($comment="")
Sets the comment string of the assQuestion object.
$_POST["username"]
setMultilineAnswerSetting($a_setting=0)
copyImages($question_id, $source_questionpool)
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.