ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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 $DIC;
225  $ilDB = $DIC['ilDB'];
226  $hasimages = 0;
227 
228  $result = $ilDB->queryF(
229  "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",
230  array("integer"),
231  array($question_id)
232  );
233  if ($result->numRows() == 1) {
234  $data = $ilDB->fetchAssoc($result);
235  $this->setId($question_id);
236  $this->setObjId($data["obj_fi"]);
237  $this->setTitle($data["title"]);
238  $this->setNrOfTries($data['nr_of_tries']);
239  $this->setComment($data["description"]);
240  $this->setOriginalId($data["original_id"]);
241  $this->setAuthor($data["author"]);
242  $this->setPoints($data["points"]);
243  $this->setOwner($data["owner"]);
244  include_once("./Services/RTE/classes/class.ilRTE.php");
245  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
246  $shuffle = (is_null($data['shuffle'])) ? true : $data['shuffle'];
247  $this->setShuffle($shuffle);
248  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
249  $this->setThumbSize($data['thumb_size']);
250  $this->isSingleline = ($data['allow_images']) ? false : true;
251  $this->lastChange = $data['tstamp'];
252  $this->setSelectionLimit((int) $data['selection_limit'] > 0 ? (int) $data['selection_limit'] : null);
253  $this->feedback_setting = $data['feedback_setting'];
254 
255  try {
256  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
257  } catch (ilTestQuestionPoolException $e) {
258  }
259  }
260 
261  $result = $ilDB->queryF(
262  "SELECT * FROM qpl_a_mc WHERE question_fi = %s ORDER BY aorder ASC",
263  array('integer'),
264  array($question_id)
265  );
266  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMultipleResponseImage.php";
267  if ($result->numRows() > 0) {
268  while ($data = $ilDB->fetchAssoc($result)) {
269  $imagefilename = $this->getImagePath() . $data["imagefile"];
270  if (!@file_exists($imagefilename)) {
271  $data["imagefile"] = "";
272  }
273  include_once("./Services/RTE/classes/class.ilRTE.php");
274  $data["answertext"] = ilRTE::_replaceMediaObjectImageSrc($data["answertext"], 1);
275  array_push($this->answers, new ASS_AnswerMultipleResponseImage($data["answertext"], $data["points"], $data["aorder"], $data["points_unchecked"], $data["imagefile"]));
276  }
277  }
278 
279  parent::loadFromDb($question_id);
280  }
281 
285  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
286  {
287  if ($this->id <= 0) {
288  // The question has not been saved. It cannot be duplicated
289  return;
290  }
291  // duplicate the question in database
292  $this_id = $this->getId();
293  $thisObjId = $this->getObjId();
294 
295  $clone = $this;
296  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
298  $clone->id = -1;
299 
300  if ((int) $testObjId > 0) {
301  $clone->setObjId($testObjId);
302  }
303 
304  if ($title) {
305  $clone->setTitle($title);
306  }
307 
308  if ($author) {
309  $clone->setAuthor($author);
310  }
311  if ($owner) {
312  $clone->setOwner($owner);
313  }
314 
315  if ($for_test) {
316  $clone->saveToDb($original_id);
317  } else {
318  $clone->saveToDb();
319  }
320 
321  // copy question page content
322  $clone->copyPageOfQuestion($this_id);
323  // copy XHTML media objects
324  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
325  // duplicate the images
326  $clone->duplicateImages($this_id, $thisObjId);
327 
328  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
329 
330  return $clone->id;
331  }
332 
336  public function copyObject($target_questionpool_id, $title = "")
337  {
338  if ($this->id <= 0) {
339  // The question has not been saved. It cannot be duplicated
340  return;
341  }
342  // duplicate the question in database
343  $clone = $this;
344  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
346  $clone->id = -1;
347  $source_questionpool_id = $this->getObjId();
348  $clone->setObjId($target_questionpool_id);
349  if ($title) {
350  $clone->setTitle($title);
351  }
352  $clone->saveToDb();
353  // copy question page content
354  $clone->copyPageOfQuestion($original_id);
355  // copy XHTML media objects
356  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
357  // duplicate the image
358  $clone->copyImages($original_id, $source_questionpool_id);
359 
360  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
361 
362  return $clone->id;
363  }
364 
365  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
366  {
367  if ($this->id <= 0) {
368  // The question has not been saved. It cannot be duplicated
369  return;
370  }
371 
372  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
373 
374  $sourceQuestionId = $this->id;
375  $sourceParentId = $this->getObjId();
376 
377  // duplicate the question in database
378  $clone = $this;
379  $clone->id = -1;
380 
381  $clone->setObjId($targetParentId);
382 
383  if ($targetQuestionTitle) {
384  $clone->setTitle($targetQuestionTitle);
385  }
386 
387  $clone->saveToDb();
388  // copy question page content
389  $clone->copyPageOfQuestion($sourceQuestionId);
390  // copy XHTML media objects
391  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
392  // duplicate the image
393  $clone->copyImages($sourceQuestionId, $sourceParentId);
394 
395  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
396 
397  return $clone->id;
398  }
399 
406  public function getOutputType()
407  {
408  return $this->output_type;
409  }
410 
419  {
420  $this->output_type = $output_type;
421  }
422 
436  public function addAnswer(
437  $answertext = "",
438  $points = 0.0,
439  $points_unchecked = 0.0,
440  $order = 0,
441  $answerimage = ""
442  ) {
443  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMultipleResponseImage.php";
444  if (array_key_exists($order, $this->answers)) {
445  // insert answer
446  $answer = new ASS_AnswerMultipleResponseImage($answertext, $points, $order, $points_unchecked, $answerimage);
447  $newchoices = array();
448  for ($i = 0; $i < $order; $i++) {
449  array_push($newchoices, $this->answers[$i]);
450  }
451  array_push($newchoices, $answer);
452  for ($i = $order; $i < count($this->answers); $i++) {
453  $changed = $this->answers[$i];
454  $changed->setOrder($i + 1);
455  array_push($newchoices, $changed);
456  }
457  $this->answers = $newchoices;
458  } else {
459  // add answer
460  $answer = new ASS_AnswerMultipleResponseImage($answertext, $points, count($this->answers), $points_unchecked, $answerimage);
461  array_push($this->answers, $answer);
462  }
463  }
464 
471  public function getAnswerCount()
472  {
473  return count($this->answers);
474  }
475 
484  public function getAnswer($index = 0)
485  {
486  if ($index < 0) {
487  return null;
488  }
489  if (count($this->answers) < 1) {
490  return null;
491  }
492  if ($index >= count($this->answers)) {
493  return null;
494  }
495 
496  return $this->answers[$index];
497  }
498 
506  public function deleteAnswer($index = 0)
507  {
508  if ($index < 0) {
509  return;
510  }
511  if (count($this->answers) < 1) {
512  return;
513  }
514  if ($index >= count($this->answers)) {
515  return;
516  }
517  $answer = $this->answers[$index];
518  if (strlen($answer->getImage())) {
519  $this->deleteImage($answer->getImage());
520  }
521  unset($this->answers[$index]);
522  $this->answers = array_values($this->answers);
523  for ($i = 0; $i < count($this->answers); $i++) {
524  if ($this->answers[$i]->getOrder() > $index) {
525  $this->answers[$i]->setOrder($i);
526  }
527  }
528  }
529 
535  public function flushAnswers()
536  {
537  $this->answers = array();
538  }
539 
545  public function getMaximumPoints()
546  {
547  $points = 0;
548  $allpoints = 0;
549  foreach ($this->answers as $key => $value) {
550  if ($value->getPoints() > $value->getPointsUnchecked()) {
551  $allpoints += $value->getPoints();
552  } else {
553  $allpoints += $value->getPointsUnchecked();
554  }
555  }
556  return $allpoints;
557  }
558 
570  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
571  {
572  if ($returndetails) {
573  throw new ilTestException('return details not implemented for ' . __METHOD__);
574  }
575 
576  global $DIC;
577  $ilDB = $DIC['ilDB'];
578 
579  $found_values = array();
580  if (is_null($pass)) {
581  $pass = $this->getSolutionMaxPass($active_id);
582  }
583  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
584  while ($data = $ilDB->fetchAssoc($result)) {
585  if (strcmp($data["value1"], "") != 0) {
586  array_push($found_values, $data["value1"]);
587  }
588  }
589 
590  $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
591 
592  return $points;
593  }
594 
595  public function validateSolutionSubmit()
596  {
597  $submit = $this->getSolutionSubmit();
598 
599  if ($this->getSelectionLimit()) {
600  if (count($submit) > $this->getSelectionLimit()) {
601  $failureMsg = sprintf(
602  $this->lng->txt('ass_mc_sel_lim_exhausted_hint'),
603  $this->getSelectionLimit(),
604  $this->getAnswerCount()
605  );
606 
607  ilUtil::sendFailure($failureMsg, true);
608  return false;
609  }
610  }
611 
612  return true;
613  }
614 
615  protected function isForcedEmptySolution($solutionSubmit)
616  {
617  if (!count($solutionSubmit) && !empty($_POST['tst_force_form_diff_input'])) {
618  return true;
619  }
620 
621  return false;
622  }
623 
632  public function saveWorkingData($active_id, $pass = null, $authorized = true)
633  {
635  global $DIC;
636  $ilDB = $DIC['ilDB'];
637 
638  if (is_null($pass)) {
639  include_once "./Modules/Test/classes/class.ilObjTest.php";
640  $pass = ilObjTest::_getPass($active_id);
641  }
642 
643  $entered_values = 0;
644 
645  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
646  $this->removeCurrentSolution($active_id, $pass, $authorized);
647 
648  $solutionSubmit = $this->getSolutionSubmit();
649 
650  foreach ($solutionSubmit as $value) {
651  if (strlen($value)) {
652  $this->saveCurrentSolution($active_id, $pass, $value, null, $authorized);
653  $entered_values++;
654  }
655  }
656 
657  // fau: testNav - write a dummy entry for the evil mc questions with "None of the above" checked
658  if ($this->isForcedEmptySolution($solutionSubmit)) {
659  $this->saveCurrentSolution($active_id, $pass, 'mc_none_above', null, $authorized);
660  $entered_values++;
661  }
662  // fau.
663  });
664 
665  if ($entered_values) {
666  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
668  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
669  }
670  } else {
671  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
673  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
674  }
675  }
676 
677  return true;
678  }
679 
680  public function saveAdditionalQuestionDataToDb()
681  {
683  global $DIC;
684  $ilDB = $DIC['ilDB'];
685  $oldthumbsize = 0;
686  if ($this->isSingleline && ($this->getThumbSize())) {
687  // get old thumbnail size
688  $result = $ilDB->queryF(
689  "SELECT thumb_size FROM " . $this->getAdditionalTableName(
690  ) . " WHERE question_fi = %s",
691  array( "integer" ),
692  array( $this->getId() )
693  );
694  if ($result->numRows() == 1) {
695  $data = $ilDB->fetchAssoc($result);
696  $oldthumbsize = $data['thumb_size'];
697  }
698  }
699 
700  if (!$this->isSingleline) {
701  ilUtil::delDir($this->getImagePath());
702  }
703 
704  // save additional data
705  $ilDB->replace(
706  $this->getAdditionalTableName(),
707  array(
708  'shuffle' => array('text', $this->getShuffle()),
709  'allow_images' => array('text', $this->isSingleline ? 0 : 1),
710  'thumb_size' => array('integer', strlen($this->getThumbSize()) ? $this->getThumbSize() : null),
711  'selection_limit' => array('integer', $this->getSelectionLimit()),
712  'feedback_setting' => array('integer', $this->getSpecificFeedbackSetting())
713  ),
714  array(
715  'question_fi' => array('integer', $this->getId())
716  )
717  );
718  }
719 
720  public function saveAnswerSpecificDataToDb()
721  {
723  global $DIC;
724  $ilDB = $DIC['ilDB'];
725  $ilDB->manipulateF(
726  "DELETE FROM qpl_a_mc WHERE question_fi = %s",
727  array( 'integer' ),
728  array( $this->getId() )
729  );
730 
731  foreach ($this->answers as $key => $value) {
732  $answer_obj = $this->answers[$key];
733  $next_id = $ilDB->nextId('qpl_a_mc');
734  $ilDB->manipulateF(
735  "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)",
736  array( 'integer', 'integer', 'text', 'float', 'float', 'integer', 'text', 'integer' ),
737  array(
738  $next_id,
739  $this->getId(),
740  ilRTE::_replaceMediaObjectImageSrc($answer_obj->getAnswertext(), 0),
741  $answer_obj->getPoints(),
742  $answer_obj->getPointsUnchecked(),
743  $answer_obj->getOrder(),
744  $answer_obj->getImage(),
745  time()
746  )
747  );
748  }
749  $this->rebuildThumbnails();
750  }
751 
752  public function syncWithOriginal()
753  {
754  if ($this->getOriginalId()) {
755  $this->syncImages();
756  parent::syncWithOriginal();
757  }
758  }
759 
765  public function getQuestionType()
766  {
767  return "assMultipleChoice";
768  }
769 
775  public function getAdditionalTableName()
776  {
777  return "qpl_qst_mc";
778  }
779 
785  public function getAnswerTableName()
786  {
787  return "qpl_a_mc";
788  }
789 
797  public function setImageFile($image_filename, $image_tempfilename = "")
798  {
799  $result = 0;
800  if (!empty($image_tempfilename)) {
801  $image_filename = str_replace(" ", "_", $image_filename);
802  $imagepath = $this->getImagePath();
803  if (!file_exists($imagepath)) {
804  ilUtil::makeDirParents($imagepath);
805  }
806  if (!ilUtil::moveUploadedFile($image_tempfilename, $image_filename, $imagepath . $image_filename)) {
807  $result = 2;
808  } else {
809  include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
810  $mimetype = ilObjMediaObject::getMimeType($imagepath . $image_filename);
811  if (!preg_match("/^image/", $mimetype)) {
812  unlink($imagepath . $image_filename);
813  $result = 1;
814  } else {
815  // create thumbnail file
816  if ($this->isSingleline && ($this->getThumbSize())) {
817  $this->generateThumbForFile($imagepath, $image_filename);
818  }
819  }
820  }
821  }
822  return $result;
823  }
824 
830  protected function deleteImage($image_filename)
831  {
832  $imagepath = $this->getImagePath();
833  @unlink($imagepath . $image_filename);
834  $thumbpath = $imagepath . $this->getThumbPrefix() . $image_filename;
835  @unlink($thumbpath);
836  }
837 
838  public function duplicateImages($question_id, $objectId = null)
839  {
841  global $DIC;
842  $ilLog = $DIC['ilLog'];
843 
844  $imagepath = $this->getImagePath();
845  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
846 
847  if ((int) $objectId > 0) {
848  $imagepath_original = str_replace("/$this->obj_id/", "/$objectId/", $imagepath_original);
849  }
850 
851  foreach ($this->answers as $answer) {
852  $filename = $answer->getImage();
853  if (strlen($filename)) {
854  if (!file_exists($imagepath)) {
855  ilUtil::makeDirParents($imagepath);
856  }
857 
858  if (file_exists($imagepath_original . $filename)) {
859  if (!copy($imagepath_original . $filename, $imagepath . $filename)) {
860  $ilLog->warning(sprintf(
861  "Could not clone source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
862  $imagepath_original . $filename,
863  $imagepath . $filename,
864  $question_id,
865  $this->id,
866  $objectId,
867  $this->obj_id
868  ));
869  }
870  }
871 
872  if (file_exists($imagepath_original . $this->getThumbPrefix() . $filename)) {
873  if (!copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
874  $ilLog->warning(sprintf(
875  "Could not clone thumbnail source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
876  $imagepath_original . $this->getThumbPrefix() . $filename,
877  $imagepath . $this->getThumbPrefix() . $filename,
878  $question_id,
879  $this->id,
880  $objectId,
881  $this->obj_id
882  ));
883  }
884  }
885  }
886  }
887  }
888 
889  public function copyImages($question_id, $source_questionpool)
890  {
891  global $DIC;
892  $ilLog = $DIC['ilLog'];
893  $imagepath = $this->getImagePath();
894  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
895  $imagepath_original = str_replace("/$this->obj_id/", "/$source_questionpool/", $imagepath_original);
896  foreach ($this->answers as $answer) {
897  $filename = $answer->getImage();
898  if (strlen($filename)) {
899  if (!file_exists($imagepath)) {
900  ilUtil::makeDirParents($imagepath);
901  }
902  if (!@copy($imagepath_original . $filename, $imagepath . $filename)) {
903  $ilLog->write("image could not be duplicated!!!!", $ilLog->ERROR);
904  $ilLog->write("object: " . print_r($this, true), $ilLog->ERROR);
905  }
906  if (@file_exists($imagepath_original . $this->getThumbPrefix() . $filename)) {
907  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
908  $ilLog->write("image thumbnail could not be duplicated!!!!", $ilLog->ERROR);
909  $ilLog->write("object: " . print_r($this, true), $ilLog->ERROR);
910  }
911  }
912  }
913  }
914  }
915 
919  protected function syncImages()
920  {
921  global $DIC;
922  $ilLog = $DIC['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(), 0, $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(), 0, $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 $DIC;
1090  $ilUser = $DIC['ilUser'];
1091 
1092  $multilineAnswerSetting = $ilUser->getPref("tst_multiline_answers");
1093  if ($multilineAnswerSetting != 1) {
1094  $multilineAnswerSetting = 0;
1095  }
1096  return $multilineAnswerSetting;
1097  }
1098 
1099  public function setMultilineAnswerSetting($a_setting = 0)
1100  {
1101  global $DIC;
1102  $ilUser = $DIC['ilUser'];
1103  $ilUser->writePref("tst_multiline_answers", $a_setting);
1104  }
1105 
1115  public function setSpecificFeedbackSetting($a_feedback_setting)
1116  {
1117  $this->feedback_setting = $a_feedback_setting;
1118  }
1119 
1129  public function getSpecificFeedbackSetting()
1130  {
1131  if ($this->feedback_setting) {
1132  return $this->feedback_setting;
1133  } else {
1134  return 1;
1135  }
1136  }
1137 
1139  {
1140  return 'feedback_correct_sc_mc';
1141  }
1142 
1154  public function isAnswered($active_id, $pass = null)
1155  {
1156  $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1157 
1158  return $numExistingSolutionRecords > 0;
1159  }
1160 
1172  public static function isObligationPossible($questionId)
1173  {
1175  global $DIC;
1176  $ilDB = $DIC['ilDB'];
1177 
1178  $query = "
1179  SELECT SUM(points) points_for_checked_answers
1180  FROM qpl_a_mc
1181  WHERE question_fi = %s AND points > 0
1182  ";
1183 
1184  $res = $ilDB->queryF($query, array('integer'), array($questionId));
1185 
1186  $row = $ilDB->fetchAssoc($res);
1187 
1188  return $row['points_for_checked_answers'] > 0;
1189  }
1190 
1199  public function ensureNoInvalidObligation($questionId)
1200  {
1202  global $DIC;
1203  $ilDB = $DIC['ilDB'];
1204 
1205  $query = "
1206  SELECT SUM(qpl_a_mc.points) points_for_checked_answers,
1207  test_question_id
1208 
1209  FROM tst_test_question
1210 
1211  INNER JOIN qpl_a_mc
1212  ON qpl_a_mc.question_fi = tst_test_question.question_fi
1213 
1214  WHERE tst_test_question.question_fi = %s
1215  AND tst_test_question.obligatory = 1
1216 
1217  GROUP BY test_question_id
1218  ";
1219 
1220  $res = $ilDB->queryF($query, array('integer'), array($questionId));
1221 
1222  $updateTestQuestionIds = array();
1223 
1224  while ($row = $ilDB->fetchAssoc($res)) {
1225  if ($row['points_for_checked_answers'] <= 0) {
1226  $updateTestQuestionIds[] = $row['test_question_id'];
1227  }
1228  }
1229 
1230  if (count($updateTestQuestionIds)) {
1231  $test_question_id__IN__updateTestQuestionIds = $ilDB->in(
1232  'test_question_id',
1233  $updateTestQuestionIds,
1234  false,
1235  'integer'
1236  );
1237 
1238  $query = "
1239  UPDATE tst_test_question
1240  SET obligatory = 0
1241  WHERE $test_question_id__IN__updateTestQuestionIds
1242  ";
1243 
1244  $ilDB->manipulate($query);
1245  }
1246  }
1247 
1251  protected function getSolutionSubmit()
1252  {
1253  $solutionSubmit = array();
1254  foreach ($_POST as $key => $value) {
1255  if (preg_match("/^multiple_choice_result_(\d+)/", $key)) {
1256  if (strlen($value)) {
1257  $solutionSubmit[] = $value;
1258  }
1259  }
1260  }
1261  return $solutionSubmit;
1262  }
1263 
1269  protected function calculateReachedPointsForSolution($found_values, $active_id = 0)
1270  {
1271  $points = 0;
1272  foreach ($this->answers as $key => $answer) {
1273  if (in_array($key, $found_values)) {
1274  $points += $answer->getPoints();
1275  } else {
1276  $points += $answer->getPointsUnchecked();
1277  }
1278  }
1279  if ($active_id) {
1280  include_once "./Modules/Test/classes/class.ilObjTest.php";
1281  $mc_scoring = ilObjTest::_getMCScoring($active_id);
1282  if (($mc_scoring == 0) && (count($found_values) == 0)) {
1283  $points = 0;
1284  }
1285  }
1286  return $points;
1287  }
1288 
1297  public function getOperators($expression)
1298  {
1299  require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
1301  }
1302 
1307  public function getExpressionTypes()
1308  {
1309  return array(
1314  );
1315  }
1316 
1325  public function getUserQuestionResult($active_id, $pass)
1326  {
1328  global $DIC;
1329  $ilDB = $DIC['ilDB'];
1330  $result = new ilUserQuestionResult($this, $active_id, $pass);
1331 
1332  $maxStep = $this->lookupMaxStep($active_id, $pass);
1333 
1334  if ($maxStep !== null) {
1335  $data = $ilDB->queryF(
1336  "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
1337  array("integer", "integer", "integer","integer"),
1338  array($active_id, $pass, $this->getId(), $maxStep)
1339  );
1340  } else {
1341  $data = $ilDB->queryF(
1342  "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
1343  array("integer", "integer", "integer"),
1344  array($active_id, $pass, $this->getId())
1345  );
1346  }
1347 
1348  while ($row = $ilDB->fetchAssoc($data)) {
1349  $result->addKeyValue($row["value1"], $row["value1"]);
1350  }
1351 
1352  $points = $this->calculateReachedPoints($active_id, $pass);
1353  $max_points = $this->getMaximumPoints();
1354 
1355  $result->setReachedPercentage(($points / $max_points) * 100);
1356 
1357  return $result;
1358  }
1359 
1368  public function getAvailableAnswerOptions($index = null)
1369  {
1370  if ($index !== null) {
1371  return $this->getAnswer($index);
1372  } else {
1373  return $this->getAnswers();
1374  }
1375  }
1376 
1377  protected function buildTestPresentationConfig()
1378  {
1379  $config = parent::buildTestPresentationConfig();
1380  $config->setUseUnchangedAnswerLabel($this->lng->txt('tst_mc_label_none_above'));
1381  return $config;
1382  }
1383 }
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.
static _getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
$path
Definition: aliased.php:25
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 ...
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.
$config
Definition: bootstrap.php:15
$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.
global $DIC
Definition: saml.php:7
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.
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
$filename
Definition: buildRTE.php:89
$row
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.
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.
$data
Definition: bench.php:6