ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.assKprimChoice.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/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
6 require_once 'Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
7 require_once 'Modules/TestQuestionPool/interfaces/interface.ilAssSpecificFeedbackOptionLabelProvider.php';
8 
16 {
18 
20 
21  const ANSWER_TYPE_SINGLE_LINE = 'singleLine';
22  const ANSWER_TYPE_MULTI_LINE = 'multiLine';
23 
24  const OPTION_LABEL_RIGHT_WRONG = 'right_wrong';
25  const OPTION_LABEL_PLUS_MINUS = 'plus_minus';
26  const OPTION_LABEL_APPLICABLE_OR_NOT = 'applicable_or_not';
27  const OPTION_LABEL_ADEQUATE_OR_NOT = 'adequate_or_not';
28  const OPTION_LABEL_CUSTOM = 'customlabel';
29 
30  const DEFAULT_THUMB_SIZE = 150;
31  const THUMB_PREFIX = 'thumb.';
32 
34 
35  private $answerType;
36 
37  private $thumbSize;
38 
40 
41  private $optionLabel;
42 
44 
46 
48 
49  private $answers;
50 
51  public function __construct($title = '', $comment = '', $author = '', $owner = -1, $question = '')
52  {
53  parent::__construct($title, $comment, $author, $owner, $question);
54 
55  $this->shuffleAnswersEnabled = true;
56  $this->answerType = self::ANSWER_TYPE_SINGLE_LINE;
57  $this->thumbSize = self::DEFAULT_THUMB_SIZE;
58  $this->scorePartialSolutionEnabled = true;
59  $this->optionLabel = self::OPTION_LABEL_RIGHT_WRONG;
60  $this->customTrueOptionLabel = '';
61  $this->customFalseOptionLabel = '';
62 
63  require_once 'Modules/TestQuestionPool/classes/feedback/class.ilAssConfigurableMultiOptionQuestionFeedback.php';
65 
66  $this->answers = array();
67 
68  $this->setPoints('');
69  }
70 
71  public function getQuestionType()
72  {
73  return 'assKprimChoice';
74  }
75 
76  public function getAdditionalTableName()
77  {
78  return "qpl_qst_kprim";
79  }
80 
81  public function getAnswerTableName()
82  {
83  return "qpl_a_kprim";
84  }
85 
87  {
88  $this->shuffleAnswersEnabled = $shuffleAnswersEnabled;
89  }
90 
91  public function isShuffleAnswersEnabled()
92  {
94  }
95 
96  public function setAnswerType($answerType)
97  {
98  $this->answerType = $answerType;
99  }
100 
101  public function getAnswerType()
102  {
103  return $this->answerType;
104  }
105 
106  public function setThumbSize($thumbSize)
107  {
108  $this->thumbSize = $thumbSize;
109  }
110 
111  public function getThumbSize()
112  {
113  return $this->thumbSize;
114  }
115 
117  {
118  $this->scorePartialSolutionEnabled = $scorePartialSolutionEnabled;
119  }
120 
122  {
124  }
125 
126  public function setOptionLabel($optionLabel)
127  {
128  $this->optionLabel = $optionLabel;
129  }
130 
131  public function getOptionLabel()
132  {
133  return $this->optionLabel;
134  }
135 
137  {
138  $this->customTrueOptionLabel = $customTrueOptionLabel;
139  }
140 
141  public function getCustomTrueOptionLabel()
142  {
144  }
145 
147  {
148  $this->customFalseOptionLabel = $customFalseOptionLabel;
149  }
150 
151  public function getCustomFalseOptionLabel()
152  {
154  }
155 
157  {
158  $this->specificFeedbackSetting = $specificFeedbackSetting;
159  }
160 
161  public function getSpecificFeedbackSetting()
162  {
164  }
165 
166  public function setAnswers($answers)
167  {
168  $this->answers = $answers;
169  }
170 
171  public function getAnswers()
172  {
173  return $this->answers;
174  }
175 
176  public function getAnswer($position)
177  {
178  foreach ($this->getAnswers() as $answer) {
179  if ($answer->getPosition() == $position) {
180  return $answer;
181  }
182  }
183 
184  return null;
185  }
186 
187  public function addAnswer(ilAssKprimChoiceAnswer $answer)
188  {
189  $this->answers[] = $answer;
190  }
191 
192  public function loadFromDb($questionId)
193  {
194  $res = $this->db->queryF($this->buildQuestionDataQuery(), array('integer'), array($questionId));
195 
196  while ($data = $this->db->fetchAssoc($res)) {
197  $this->setId($questionId);
198 
199  $this->setOriginalId($data['original_id']);
200 
201  $this->setObjId($data['obj_fi']);
202 
203  $this->setTitle($data['title']);
204  $this->setNrOfTries($data['nr_of_tries']);
205  $this->setComment($data['description']);
206  $this->setAuthor($data['author']);
207  $this->setPoints($data['points']);
208  $this->setOwner($data['owner']);
209  $this->setEstimatedWorkingTimeFromDurationString($data['working_time']);
210  $this->setLastChange($data['tstamp']);
211  require_once 'Services/RTE/classes/class.ilRTE.php';
212  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data['question_text'], 1));
213 
214  $this->setShuffleAnswersEnabled((bool) $data['shuffle_answers']);
215 
216  if ($this->isValidAnswerType($data['answer_type'])) {
217  $this->setAnswerType($data['answer_type']);
218  }
219 
220  if (is_numeric($data['thumb_size'])) {
221  $this->setThumbSize((int) $data['thumb_size']);
222  }
223 
224  if ($this->isValidOptionLabel($data['opt_label'])) {
225  $this->setOptionLabel($data['opt_label']);
226  }
227 
228  $this->setCustomTrueOptionLabel($data['custom_true']);
229  $this->setCustomFalseOptionLabel($data['custom_false']);
230 
231  if ($data['score_partsol'] !== null) {
232  $this->setScorePartialSolutionEnabled((bool) $data['score_partsol']);
233  }
234 
235  if (isset($data['feedback_setting'])) {
236  $this->setSpecificFeedbackSetting((int) $data['feedback_setting']);
237  }
238 
239  try {
240  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
241  } catch (ilTestQuestionPoolException $e) {
242  }
243  }
244 
245  $this->loadAnswerData($questionId);
246 
247  parent::loadFromDb($questionId);
248  }
249 
250  private function loadAnswerData($questionId)
251  {
252  global $ilDB;
253 
254  $res = $this->db->queryF(
255  "SELECT * FROM {$this->getAnswerTableName()} WHERE question_fi = %s ORDER BY position ASC",
256  array('integer'),
257  array($questionId)
258  );
259 
260  require_once 'Modules/TestQuestionPool/classes/class.ilAssKprimChoiceAnswer.php';
261  require_once 'Services/RTE/classes/class.ilRTE.php';
262 
263  while ($data = $ilDB->fetchAssoc($res)) {
264  $answer = new ilAssKprimChoiceAnswer();
265 
266  $answer->setPosition($data['position']);
267 
268  $answer->setAnswertext(ilRTE::_replaceMediaObjectImageSrc($data['answertext'], 1));
269 
270  $answer->setImageFile($data['imagefile']);
271  $answer->setThumbPrefix($this->getThumbPrefix());
272  $answer->setImageFsDir($this->getImagePath());
273  $answer->setImageWebDir($this->getImagePathWeb());
274 
275  $answer->setCorrectness($data['correctness']);
276 
277  $this->answers[$answer->getPosition()] = $answer;
278  }
279 
280  for ($i = count($this->answers); $i < self::NUM_REQUIRED_ANSWERS; $i++) {
281  $answer = new ilAssKprimChoiceAnswer();
282 
283  $answer->setPosition($i);
284 
285  $this->answers[$answer->getPosition()] = $answer;
286  }
287  }
288 
289  public function saveToDb($originalId = '')
290  {
291  $this->saveQuestionDataToDb($originalId);
292 
295 
296  parent::saveToDb($originalId);
297  }
298 
300  {
301  $this->db->replace(
302  $this->getAdditionalTableName(),
303  array(
304  'question_fi' => array('integer', $this->getId())
305  ),
306  array(
307  'shuffle_answers' => array('integer', (int) $this->isShuffleAnswersEnabled()),
308  'answer_type' => array('text', $this->getAnswerType()),
309  'thumb_size' => array('integer', (int) $this->getThumbSize()),
310  'opt_label' => array('text', $this->getOptionLabel()),
311  'custom_true' => array('text', $this->getCustomTrueOptionLabel()),
312  'custom_false' => array('text', $this->getCustomFalseOptionLabel()),
313  'score_partsol' => array('integer', (int) $this->isScorePartialSolutionEnabled()),
314  'feedback_setting' => array('integer', (int) $this->getSpecificFeedbackSetting())
315  )
316  );
317  }
318 
319  public function saveAnswerSpecificDataToDb()
320  {
321  foreach ($this->getAnswers() as $answer) {
322  $this->db->replace(
323  $this->getAnswerTableName(),
324  array(
325  'question_fi' => array('integer', (int) $this->getId()),
326  'position' => array('integer', (int) $answer->getPosition())
327  ),
328  array(
329  'answertext' => array('text', $answer->getAnswertext()),
330  'imagefile' => array('text', $answer->getImageFile()),
331  'correctness' => array('integer', (int) $answer->getCorrectness())
332  )
333  );
334  }
335 
336  $this->rebuildThumbnails();
337  }
338 
339  public function isComplete()
340  {
341  foreach (array($this->title, $this->author, $this->question) as $text) {
342  if (!strlen($text)) {
343  return false;
344  }
345  }
346 
347  if ($this->getMaximumPoints() <= 0) {
348  return false;
349  }
350 
351  foreach ($this->getAnswers() as $answer) {
352  /* @var ilAssKprimChoiceAnswer $answer */
353 
354  if (is_null($answer->getCorrectness())) {
355  return false;
356  }
357 
358  if (!strlen($answer->getAnswertext()) && !strlen($answer->getImageFile())) {
359  return false;
360  }
361  }
362 
363  return true;
364  }
365 
374  public function saveWorkingData($active_id, $pass = null, $authorized = true)
375  {
377  $ilDB = $GLOBALS['DIC']['ilDB'];
378 
379  if (is_null($pass)) {
380  include_once "./Modules/Test/classes/class.ilObjTest.php";
381  $pass = ilObjTest::_getPass($active_id);
382  }
383 
384  $entered_values = 0;
385 
386  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
387  $this->removeCurrentSolution($active_id, $pass, $authorized);
388 
389  $solutionSubmit = $this->getSolutionSubmit();
390 
391  foreach ($solutionSubmit as $answerIndex => $answerValue) {
392  $this->saveCurrentSolution($active_id, $pass, (int) $answerIndex, (int) $answerValue, $authorized);
393  $entered_values++;
394  }
395  });
396 
397  if ($entered_values) {
398  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
400  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
401  }
402  } else {
403  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
405  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
406  }
407  }
408 
409  return true;
410  }
411 
415  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
416  {
417  // nothing to rework!
418  }
419 
430  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
431  {
432  if ($returndetails) {
433  throw new ilTestException('return details not implemented for ' . __METHOD__);
434  }
435 
436  global $ilDB;
437 
438  $found_values = array();
439  if (is_null($pass)) {
440  $pass = $this->getSolutionMaxPass($active_id);
441  }
442 
443  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
444 
445  while ($data = $ilDB->fetchAssoc($result)) {
446  $found_values[(int) $data['value1']] = (int) $data['value2'];
447  }
448 
449  $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
450 
451  return $points;
452  }
453 
454  public function getValidAnswerTypes()
455  {
456  return array(self::ANSWER_TYPE_SINGLE_LINE, self::ANSWER_TYPE_MULTI_LINE);
457  }
458 
460  {
461  $validTypes = $this->getValidAnswerTypes();
462  return in_array($answerType, $validTypes);
463  }
464 
466  {
468  }
469 
475  {
476  return array(
477  self::ANSWER_TYPE_SINGLE_LINE => $lng->txt('answers_singleline'),
478  self::ANSWER_TYPE_MULTI_LINE => $lng->txt('answers_multiline')
479  );
480  }
481 
482  public function getValidOptionLabels()
483  {
484  return array(
485  self::OPTION_LABEL_RIGHT_WRONG,
486  self::OPTION_LABEL_PLUS_MINUS,
487  self::OPTION_LABEL_APPLICABLE_OR_NOT,
488  self::OPTION_LABEL_ADEQUATE_OR_NOT,
489  self::OPTION_LABEL_CUSTOM
490  );
491  }
492 
494  {
495  return array(
496  self::OPTION_LABEL_RIGHT_WRONG => $lng->txt('option_label_right_wrong'),
497  self::OPTION_LABEL_PLUS_MINUS => $lng->txt('option_label_plus_minus'),
498  self::OPTION_LABEL_APPLICABLE_OR_NOT => $lng->txt('option_label_applicable_or_not'),
499  self::OPTION_LABEL_ADEQUATE_OR_NOT => $lng->txt('option_label_adequate_or_not'),
500  self::OPTION_LABEL_CUSTOM => $lng->txt('option_label_custom')
501  );
502  }
503 
505  {
506  $validLabels = $this->getValidOptionLabels();
507  return in_array($optionLabel, $validLabels);
508  }
509 
511  {
512  switch ($optionLabel) {
513  case self::OPTION_LABEL_RIGHT_WRONG:
514  return $lng->txt('option_label_right');
515 
516  case self::OPTION_LABEL_PLUS_MINUS:
517  return $lng->txt('option_label_plus');
518 
519  case self::OPTION_LABEL_APPLICABLE_OR_NOT:
520  return $lng->txt('option_label_applicable');
521 
522  case self::OPTION_LABEL_ADEQUATE_OR_NOT:
523  return $lng->txt('option_label_adequate');
524 
525  case self::OPTION_LABEL_CUSTOM:
526  return $this->getCustomTrueOptionLabel();
527  }
528  }
529 
531  {
532  switch ($optionLabel) {
533  case self::OPTION_LABEL_RIGHT_WRONG:
534  return $lng->txt('option_label_wrong');
535 
536  case self::OPTION_LABEL_PLUS_MINUS:
537  return $lng->txt('option_label_minus');
538 
539  case self::OPTION_LABEL_APPLICABLE_OR_NOT:
540  return $lng->txt('option_label_not_applicable');
541 
542  case self::OPTION_LABEL_ADEQUATE_OR_NOT:
543  return $lng->txt('option_label_not_adequate');
544 
545  case self::OPTION_LABEL_CUSTOM:
546  return $this->getCustomFalseOptionLabel();
547  }
548  }
549 
551  {
552  return sprintf(
553  $lng->txt('kprim_instruction_text'),
556  );
557  }
558 
559  public function isCustomOptionLabel($labelValue)
560  {
561  return $labelValue == self::OPTION_LABEL_CUSTOM;
562  }
563 
564  public function getThumbPrefix()
565  {
566  return self::THUMB_PREFIX;
567  }
568 
569  public function rebuildThumbnails()
570  {
571  if ($this->isSingleLineAnswerType($this->getAnswerType()) && $this->getThumbSize()) {
572  foreach ($this->getAnswers() as $answer) {
573  if (strlen($answer->getImageFile())) {
574  $this->generateThumbForFile($answer->getImageFsDir(), $answer->getImageFile());
575  }
576  }
577  }
578  }
579 
580  protected function generateThumbForFile($path, $file)
581  {
582  $filename = $path . $file;
583  if (@file_exists($filename)) {
584  $thumbpath = $path . $this->getThumbPrefix() . $file;
585  $path_info = @pathinfo($filename);
586  $ext = "";
587  switch (strtoupper($path_info['extension'])) {
588  case 'PNG':
589  $ext = 'PNG';
590  break;
591  case 'GIF':
592  $ext = 'GIF';
593  break;
594  default:
595  $ext = 'JPEG';
596  break;
597  }
598  ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbSize());
599  }
600  }
601 
603  {
604  foreach ($answers as $answer) {
605  /* @var ilAssKprimChoiceAnswer $answer */
606 
607  if (!isset($files[$answer->getPosition()])) {
608  continue;
609  }
610 
611  $this->handleFileUpload($answer, $files[$answer->getPosition()]);
612  }
613  }
614 
615  private function handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
616  {
617  $imagePath = $this->getImagePath();
618 
619  if (!file_exists($imagePath)) {
620  ilUtil::makeDirParents($imagePath);
621  }
622 
623  $filename = $this->buildHashedImageFilename($fileData['name'], true);
624 
625  $answer->setImageFsDir($imagePath);
626  $answer->setImageFile($filename);
627 
628  if (!ilUtil::moveUploadedFile($fileData['tmp_name'], $fileData['name'], $answer->getImageFsPath())) {
629  return 2;
630  }
631 
632  return 0;
633  }
634 
635  public function removeAnswerImage($position)
636  {
637  $answer = $this->getAnswer($position);
638 
639  if (file_exists($answer->getImageFsPath())) {
640  ilUtil::delDir($answer->getImageFsPath());
641  }
642 
643  if (file_exists($answer->getThumbFsPath())) {
644  ilUtil::delDir($answer->getThumbFsPath());
645  }
646 
647  $answer->setImageFile(null);
648  }
649 
650  protected function getSolutionSubmit()
651  {
652  $solutionSubmit = array();
653  foreach ($_POST as $key => $value) {
654  $matches = null;
655 
656  if (preg_match("/^kprim_choice_result_(\d+)/", $key, $matches)) {
657  if (strlen($value)) {
658  $solutionSubmit[$matches[1]] = $value;
659  }
660  }
661  }
662  return $solutionSubmit;
663  }
664 
665  protected function calculateReachedPointsForSolution($found_values, $active_id = 0)
666  {
667  $numCorrect = 0;
668 
669  foreach ($this->getAnswers() as $key => $answer) {
670  if (!isset($found_values[$answer->getPosition()])) {
671  continue;
672  }
673 
674  if ($found_values[$answer->getPosition()] == $answer->getCorrectness()) {
675  $numCorrect++;
676  }
677  }
678 
679  if ($numCorrect >= self::NUM_REQUIRED_ANSWERS) {
680  $points = $this->getPoints();
681  } elseif ($this->isScorePartialSolutionEnabled() && $numCorrect >= self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS) {
682  $points = $this->getPoints() / 2;
683  } else {
684  $points = 0;
685  }
686 
687  if ($active_id) {
688  include_once "./Modules/Test/classes/class.ilObjTest.php";
689  $mc_scoring = ilObjTest::_getMCScoring($active_id);
690  if (($mc_scoring == 0) && (count($found_values) == 0)) {
691  $points = 0;
692  }
693  }
694  return $points;
695  }
696 
697  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
698  {
699  if ($this->id <= 0) {
700  // The question has not been saved. It cannot be duplicated
701  return;
702  }
703  // duplicate the question in database
704  $this_id = $this->getId();
705  $thisObjId = $this->getObjId();
706 
707  $clone = $this;
708  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
710  $clone->id = -1;
711 
712  if ((int) $testObjId > 0) {
713  $clone->setObjId($testObjId);
714  }
715 
716  if ($title) {
717  $clone->setTitle($title);
718  }
719 
720  if ($author) {
721  $clone->setAuthor($author);
722  }
723  if ($owner) {
724  $clone->setOwner($owner);
725  }
726 
727  if ($for_test) {
728  $clone->saveToDb($original_id);
729  } else {
730  $clone->saveToDb();
731  }
732 
733  // copy question page content
734  $clone->copyPageOfQuestion($this_id);
735  // copy XHTML media objects
736  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
737  // duplicate the images
738  $clone->cloneAnswerImages($this_id, $thisObjId, $clone->getId(), $clone->getObjId());
739 
740  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
741 
742  return $clone->id;
743  }
744 
745  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
746  {
747  if ($this->id <= 0) {
748  // The question has not been saved. It cannot be duplicated
749  return;
750  }
751 
752  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
753 
754  $sourceQuestionId = $this->id;
755  $sourceParentId = $this->getObjId();
756 
757  // duplicate the question in database
758  $clone = $this;
759  $clone->id = -1;
760 
761  $clone->setObjId($targetParentId);
762 
763  if ($targetQuestionTitle) {
764  $clone->setTitle($targetQuestionTitle);
765  }
766 
767  $clone->saveToDb();
768  // copy question page content
769  $clone->copyPageOfQuestion($sourceQuestionId);
770  // copy XHTML media objects
771  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
772  // duplicate the image
773  $clone->cloneAnswerImages($sourceQuestionId, $sourceParentId, $clone->getId(), $clone->getObjId());
774 
775  $clone->onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $clone->getId());
776 
777  return $clone->id;
778  }
779 
783  public function copyObject($target_questionpool_id, $title = "")
784  {
785  if ($this->id <= 0) {
786  // The question has not been saved. It cannot be duplicated
787  return;
788  }
789  // duplicate the question in database
790  $clone = $this;
791  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
793  $clone->id = -1;
794  $source_questionpool_id = $this->getObjId();
795  $clone->setObjId($target_questionpool_id);
796  if ($title) {
797  $clone->setTitle($title);
798  }
799  $clone->saveToDb();
800  // copy question page content
801  $clone->copyPageOfQuestion($original_id);
802  // copy XHTML media objects
803  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
804  // duplicate the image
805  $clone->cloneAnswerImages($original_id, $source_questionpool_id, $clone->getId(), $clone->getObjId());
806 
807  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
808 
809  return $clone->id;
810  }
811 
812  protected function beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
813  {
814  parent::beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
815 
816  $question = self::_instanciateQuestion($origQuestionId);
817 
818  foreach ($question->getAnswers() as $answer) {
819  $question->removeAnswerImage($answer->getPosition());
820  }
821  }
822 
823  protected function afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
824  {
825  parent::afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
826 
827  $this->cloneAnswerImages($dupQuestionId, $dupParentObjId, $origQuestionId, $origParentObjId);
828  }
829 
830  protected function cloneAnswerImages($sourceQuestionId, $sourceParentId, $targetQuestionId, $targetParentId)
831  {
833  global $ilLog;
834 
835  $sourcePath = $this->buildImagePath($sourceQuestionId, $sourceParentId);
836  $targetPath = $this->buildImagePath($targetQuestionId, $targetParentId);
837 
838  foreach ($this->getAnswers() as $answer) {
839  $filename = $answer->getImageFile();
840 
841  if (strlen($filename)) {
842  if (!file_exists($targetPath)) {
843  ilUtil::makeDirParents($targetPath);
844  }
845 
846  if (file_exists($sourcePath . $filename)) {
847  if (!copy($sourcePath . $filename, $targetPath . $filename)) {
848  $ilLog->warning(sprintf(
849  "Could not clone source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
850  $sourcePath . $filename,
851  $targetPath . $filename,
852  $sourceQuestionId,
853  $targetQuestionId,
854  $sourceParentId,
855  $targetParentId
856  ));
857  }
858  }
859 
860  if (file_exists($sourcePath . $this->getThumbPrefix() . $filename)) {
861  if (!copy($sourcePath . $this->getThumbPrefix() . $filename, $targetPath . $this->getThumbPrefix() . $filename)) {
862  $ilLog->warning(sprintf(
863  "Could not clone thumbnail source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
864  $sourcePath . $this->getThumbPrefix() . $filename,
865  $targetPath . $this->getThumbPrefix() . $filename,
866  $sourceQuestionId,
867  $targetQuestionId,
868  $sourceParentId,
869  $targetParentId
870  ));
871  }
872  }
873  }
874  }
875  }
876 
877  protected function getRTETextWithMediaObjects()
878  {
879  $combinedText = parent::getRTETextWithMediaObjects();
880 
881  foreach ($this->getAnswers() as $answer) {
882  $combinedText .= $answer->getAnswertext();
883  }
884 
885  return $combinedText;
886  }
887 
892  {
893  foreach ($this->getAnswers() as $answer) {
894  /* @var ilAssKprimChoiceAnswer $answer */
895  $answer->setAnswertext($migrator->migrateToLmContent($answer->getAnswertext()));
896  }
897  }
898 
902  public function toJSON()
903  {
904  $this->lng->loadLanguageModule('assessment');
905 
906  require_once './Services/RTE/classes/class.ilRTE.php';
907  $result = array();
908  $result['id'] = (int) $this->getId();
909  $result['type'] = (string) $this->getQuestionType();
910  $result['title'] = (string) $this->getTitle();
911  $result['question'] = $this->formatSAQuestion($this->getQuestion());
912  $result['instruction'] = $this->getInstructionTextTranslation(
913  $this->lng,
914  $this->getOptionLabel()
915  );
916  $result['nr_of_tries'] = (int) $this->getNrOfTries();
917  $result['shuffle'] = (bool) $this->isShuffleAnswersEnabled();
918  $result['feedback'] = array(
919  'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
920  'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
921  );
922 
923  $result['trueOptionLabel'] = $this->getTrueOptionLabelTranslation($this->lng, $this->getOptionLabel());
924  $result['falseOptionLabel'] = $this->getFalseOptionLabelTranslation($this->lng, $this->getOptionLabel());
925 
926  $result['num_allowed_failures'] = $this->getNumAllowedFailures();
927 
928  $answers = array();
929  $has_image = false;
930 
931  foreach ($this->getAnswers() as $key => $answer) {
932  if (strlen((string) $answer->getImageFile())) {
933  $has_image = true;
934  }
935 
936  $answers[] = array(
937  'answertext' => (string) $this->formatSAQuestion($answer->getAnswertext()),
938  'correctness' => (bool) $answer->getCorrectness(),
939  'order' => (int) $answer->getPosition(),
940  'image' => (string) $answer->getImageFile(),
941  'feedback' => $this->formatSAQuestion(
942  $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), $key)
943  )
944  );
945  }
946 
947  $result['answers'] = $answers;
948 
949  if ($has_image) {
950  $result['path'] = $this->getImagePathWeb();
951  $result['thumb'] = $this->getThumbSize();
952  }
953 
954  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
955  $result['mobs'] = $mobs;
956 
957  return json_encode($result);
958  }
959 
960  private function getNumAllowedFailures()
961  {
962  if ($this->isScorePartialSolutionEnabled()) {
963  return self::NUM_REQUIRED_ANSWERS - self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS;
964  }
965 
966  return 0;
967  }
968 
970  {
971  return 'feedback_correct_kprim';
972  }
973 
974  public static function isObligationPossible($questionId)
975  {
976  return true;
977  }
978 
979  public function isAnswered($active_id, $pass = null)
980  {
981  $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
982 
983  return $numExistingSolutionRecords >= 4;
984  }
985 
989  public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
990  {
991  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
992 
993  $solution = $this->getSolutionValues($active_id, $pass);
994 
995  $i = 1;
996  foreach ($this->getAnswers() as $id => $answer) {
997  $worksheet->setCell($startrow + $i, 0, $answer->getAnswertext());
998  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
999  $correctness = false;
1000  foreach ($solution as $solutionvalue) {
1001  if ($id == $solutionvalue['value1']) {
1002  $correctness = $solutionvalue['value2'];
1003  break;
1004  }
1005  }
1006  $worksheet->setCell($startrow + $i, 1, $correctness);
1007  $i++;
1008  }
1009 
1010  return $startrow + $i + 1;
1011  }
1012 
1013  public function moveAnswerDown($position)
1014  {
1015  if ($position < 0 || $position >= (self::NUM_REQUIRED_ANSWERS - 1)) {
1016  return false;
1017  }
1018 
1019  for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
1020  if ($i == $position) {
1021  $movingAnswer = $this->answers[$i];
1022  $targetAnswer = $this->answers[ $i + 1 ];
1023 
1024  $movingAnswer->setPosition($position + 1);
1025  $targetAnswer->setPosition($position);
1026 
1027  $this->answers[ $i + 1 ] = $movingAnswer;
1028  $this->answers[$i] = $targetAnswer;
1029  }
1030  }
1031  }
1032 
1033  public function moveAnswerUp($position)
1034  {
1035  if ($position <= 0 || $position > (self::NUM_REQUIRED_ANSWERS - 1)) {
1036  return false;
1037  }
1038 
1039  for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
1040  if ($i == $position) {
1041  $movingAnswer = $this->answers[$i];
1042  $targetAnswer = $this->answers[ $i - 1 ];
1043 
1044  $movingAnswer->setPosition($position - 1);
1045  $targetAnswer->setPosition($position);
1046 
1047  $this->answers[ $i - 1 ] = $movingAnswer;
1048  $this->answers[$i] = $targetAnswer;
1049  }
1050  }
1051 
1052  return true;
1053  }
1054 }
$files
Definition: add-vimline.php:18
getValidOptionLabelsTranslated(ilLanguage $lng)
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.
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.
$worksheet
isValidAnswerType($answerType)
reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
{}
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static getNumExistingSolutionRecords($activeId, $pass, $questionId)
returns the number of existing solution records for the given test active / pass and given question i...
buildHashedImageFilename($plain_image_filename, $unique=false)
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
$result
setScorePartialSolutionEnabled($scorePartialSolutionEnabled)
getPoints()
Returns the maximum available points for the question.
static isObligationPossible($questionId)
isCustomOptionLabel($labelValue)
Abstract basic class which is to be extended by the concrete assessment question type classes...
__construct($title='', $comment='', $author='', $owner=-1, $question='')
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
getSolutionValues($active_id, $pass=null, $authorized=true)
Loads solutions of a given user from the database an returns it.
setId($id=-1)
Sets the id of the assQuestion object.
getImagePathWeb()
Returns the web image path for web accessable images of a question.
const PARTIAL_SCORING_NUM_CORRECT_ANSWERS
removeAnswerImage($position)
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
copyObject($target_questionpool_id, $title="")
Copies an assMultipleChoice object.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
setEstimatedWorkingTimeFromDurationString($durationString)
Sets the estimated working time of a question from a given datetime string.
setNrOfTries($a_nr_of_tries)
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
setCustomTrueOptionLabel($customTrueOptionLabel)
isValidOptionLabel($optionLabel)
loadAnswerData($questionId)
getTrueOptionLabelTranslation(ilLanguage $lng, $optionLabel)
static _replaceMediaObjectImageSrc($a_text, $a_direction=0, $nic=IL_INST_ID)
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
getObjId()
Get the object id of the container object.
beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
addAnswer(ilAssKprimChoiceAnswer $answer)
Base Exception for all Exceptions relating to Modules/Test.
setOptionLabel($optionLabel)
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
isSingleLineAnswerType($answerType)
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
$mobs
getAnswerTypeSelectOptions(ilLanguage $lng)
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.
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
$text
Definition: errorreport.php:18
setShuffleAnswersEnabled($shuffleAnswersEnabled)
Interface ilObjAnswerScoringAdjustable.
const OPTION_LABEL_APPLICABLE_OR_NOT
handleFileUploads($answers, $files)
getQuestion()
Gets the question string of the question object.
static convertImage( $a_from, $a_to, $a_target_format="", $a_geometry="", $a_background_color="")
convert image
isAnswered($active_id, $pass=null)
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
{}
Create styles array
The data for the language used.
setPoints($a_points)
Sets the maximum available points for the question.
saveQuestionDataToDb($original_id="")
setCustomFalseOptionLabel($customFalseOptionLabel)
getFalseOptionLabelTranslation(ilLanguage $lng, $optionLabel)
calculateReachedPointsForSolution($found_values, $active_id=0)
setQuestion($question="")
Sets the question string of the question object.
Interface ilObjQuestionScoringAdjustable.
removeCurrentSolution($active_id, $pass, $authorized=true)
setSpecificFeedbackSetting($specificFeedbackSetting)
static _getMCScoring($active_id)
Gets the scoring type for multiple choice questions.
buildImagePath($questionId, $parentObjectId)
global $ilDB
setOriginalId($original_id)
generateThumbForFile($path, $file)
setLastChange($lastChange)
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass...
$i
Definition: disco.tpl.php:19
getTitle()
Gets the title string of the assQuestion object.
handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
language handling
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
txt($a_topic, $a_default_lang_fallback_mod="")
gets the text for a given topic if the topic is not in the list, the topic itself with "-" will be re...
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.
setAnswerType($answerType)
$_POST["username"]
getInstructionTextTranslation(ilLanguage $lng, $optionLabel)
saveToDb($originalId='')
toJSON()
Returns a JSON representation of the question.
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)