ILIAS  release_4-4 Revision
All Data Structures Namespaces Files Functions Variables Modules Pages
class.assOrderingQuestion.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 
23 {
31  var $answers;
32 
42 
48  var $thumb_geometry = 100;
49 
56 
57  public $old_ordering_depth = array();
58  public $leveled_ordering = array();
59 
72  public function __construct(
73  $title = "",
74  $comment = "",
75  $author = "",
76  $owner = -1,
77  $question = "",
78  $ordering_type = OQ_TERMS
79  )
80  {
81  parent::__construct($title, $comment, $author, $owner, $question);
82  $this->answers = array();
83  $this->ordering_type = $ordering_type;
84  }
85 
91  public function isComplete()
92  {
93  if (
94  strlen($this->title) &&
95  $this->author &&
96  $this->question &&
97  count($this->answers) &&
98  $this->getMaximumPoints() > 0
99  )
100  {
101  return true;
102  }
103  return false;
104  }
105 
113  public function saveToDb($original_id = "")
114  {
115  global $ilDB;
116 
120  parent::saveToDb($original_id);
121  }
122 
130  function loadFromDb($question_id)
131  {
132  global $ilDB;
133 
134  $result = $ilDB->queryF("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",
135  array("integer"),
136  array($question_id)
137  );
138  if ($result->numRows() == 1)
139  {
140  $data = $ilDB->fetchAssoc($result);
141  $this->setId($question_id);
142  $this->setObjId($data["obj_fi"]);
143  $this->setTitle($data["title"]);
144  $this->setComment($data["description"]);
145  $this->setOriginalId($data["original_id"]);
146  $this->setAuthor($data["author"]);
147  $this->setNrOfTries($data['nr_of_tries']);
148  $this->setPoints($data["points"]);
149  $this->setOwner($data["owner"]);
150  include_once("./Services/RTE/classes/class.ilRTE.php");
151  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
152  $this->ordering_type = strlen($data["ordering_type"]) ? $data["ordering_type"] : OQ_TERMS;
153  $this->thumb_geometry = $data["thumb_geometry"];
154  $this->element_height = $data["element_height"];
155  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
156 
157  try
158  {
159  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
160  }
162  {
163  }
164  }
165 
166  $result = $ilDB->queryF("SELECT * FROM qpl_a_ordering WHERE question_fi = %s ORDER BY solution_order ASC",
167  array('integer'),
168  array($question_id)
169  );
170 
171  include_once "./Modules/TestQuestionPool/classes/class.assAnswerOrdering.php";
172  if ($result->numRows() > 0)
173  {
174  while ($data = $ilDB->fetchAssoc($result))
175  {
176  include_once("./Services/RTE/classes/class.ilRTE.php");
177  $data["answertext"] = ilRTE::_replaceMediaObjectImageSrc($data["answertext"], 1);
178  array_push($this->answers, new ASS_AnswerOrdering($data["answertext"], $data["random_id"], $data['depth'] ? $data['depth'] : 0));
179  }
180  }
181  parent::loadFromDb($question_id);
182  }
183 
189  function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
190  {
191  if ($this->id <= 0)
192  {
193  // The question has not been saved. It cannot be duplicated
194  return;
195  }
196  // duplicate the question in database
197  $this_id = $this->getId();
198  $thisObjId = $this->getObjId();
199 
200  $clone = $this;
201  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
203  $clone->id = -1;
204 
205  if( (int)$testObjId > 0 )
206  {
207  $clone->setObjId($testObjId);
208  }
209 
210  if ($title)
211  {
212  $clone->setTitle($title);
213  }
214  if ($author)
215  {
216  $clone->setAuthor($author);
217  }
218  if ($owner)
219  {
220  $clone->setOwner($owner);
221  }
222  if ($for_test)
223  {
224  $clone->saveToDb($original_id);
225  }
226  else
227  {
228  $clone->saveToDb();
229  }
230 
231  // copy question page content
232  $clone->copyPageOfQuestion($this_id);
233  // copy XHTML media objects
234  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
235  // duplicate the image
236  $clone->duplicateImages($this_id, $thisObjId, $clone->getId(), $testObjId);
237 
238  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
239 
240  return $clone->id;
241  }
242 
248  function copyObject($target_questionpool_id, $title = "")
249  {
250  if ($this->id <= 0)
251  {
252  // The question has not been saved. It cannot be duplicated
253  return;
254  }
255  // duplicate the question in database
256  $clone = $this;
257  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
259  $clone->id = -1;
260  $source_questionpool_id = $this->getObjId();
261  $clone->setObjId($target_questionpool_id);
262  if ($title)
263  {
264  $clone->setTitle($title);
265  }
266 
267  $clone->saveToDb();
268 
269  // copy question page content
270  $clone->copyPageOfQuestion($original_id);
271  // copy XHTML media objects
272  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
273  // duplicate the image
274  $clone->copyImages($original_id, $source_questionpool_id);
275 
276  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
277 
278  return $clone->id;
279  }
280 
281  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
282  {
283  if ($this->id <= 0)
284  {
285  // The question has not been saved. It cannot be duplicated
286  return;
287  }
288 
289  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
290 
291  $sourceQuestionId = $this->id;
292  $sourceParentId = $this->getObjId();
293 
294  // duplicate the question in database
295  $clone = $this;
296  $clone->id = -1;
297 
298  $clone->setObjId($targetParentId);
299 
300  if ($targetQuestionTitle)
301  {
302  $clone->setTitle($targetQuestionTitle);
303  }
304 
305  $clone->saveToDb();
306  // copy question page content
307  $clone->copyPageOfQuestion($sourceQuestionId);
308  // copy XHTML media objects
309  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
310  // duplicate the image
311  $clone->copyImages($sourceQuestionId, $sourceParentId);
312 
313  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
314 
315  return $clone->id;
316  }
317 
318  function duplicateImages($src_question_id, $src_object_id, $dest_question_id, $dest_object_id)
319  {
320  global $ilLog;
321  if ($this->getOrderingType() == OQ_PICTURES)
322  {
323  $imagepath_original = $this->getImagePath($src_question_id, $src_object_id);
324  $imagepath = $this->getImagePath($dest_question_id, $dest_object_id);
325 
326  if (!file_exists($imagepath)) {
327  ilUtil::makeDirParents($imagepath);
328  }
329  foreach ($this->answers as $answer)
330  {
331  $filename = $answer->getAnswertext();
332  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
333  {
334  $ilLog->write("image could not be duplicated!!!!");
335  }
336  if (@file_exists($imagepath_original. $this->getThumbPrefix(). $filename))
337  {
338  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
339  {
340  $ilLog->write("image thumbnail could not be duplicated!!!!");
341  }
342  }
343  }
344  }
345  }
346 
347  function copyImages($question_id, $source_questionpool)
348  {
349  global $ilLog;
350  if ($this->getOrderingType() == OQ_PICTURES)
351  {
352  $imagepath = $this->getImagePath();
353  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
354  $imagepath_original = str_replace("/$this->obj_id/", "/$source_questionpool/", $imagepath_original);
355  if (!file_exists($imagepath)) {
356  ilUtil::makeDirParents($imagepath);
357  }
358  foreach ($this->answers as $answer)
359  {
360  $filename = $answer->getAnswertext();
361  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
362  {
363  $ilLog->write("Ordering Question image could not be copied: $imagepath_original$filename");
364  }
365  if (@file_exists($imagepath_original. $this->getThumbPrefix(). $filename))
366  {
367  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
368  {
369  $ilLog->write("Ordering Question image thumbnail could not be copied: $imagepath_original" . $this->getThumbPrefix() . $filename);
370  }
371  }
372  }
373  }
374  }
375 
383  function setOrderingType($ordering_type = OQ_TERMS)
384  {
385  $this->ordering_type = $ordering_type;
386  }
387 
395  function getOrderingType()
396  {
397  return $this->ordering_type;
398  }
399 
414  function addAnswer($answertext = "", $solution_order = -1 ,$depth = 0)
415  {
416  include_once "./Modules/TestQuestionPool/classes/class.assAnswerOrdering.php";
417  $answer = new ASS_AnswerOrdering($answertext, $this->getRandomID(), $depth);
418  if (($solution_order >= 0) && ($solution_order < count($this->answers)))
419  {
420  $part1 = array_slice($this->answers, 0, $solution_order);
421  $part2 = array_slice($this->answers, $solution_order);
422  $this->answers = array_merge($part1, array($answer), $part2);
423  }
424  else
425  {
426  array_push($this->answers, $answer);
427  }
428  }
429 
430  public function moveAnswerUp($position)
431  {
432  if ($position > 0)
433  {
434  $temp = $this->answers[$position-1];
435  $this->answers[$position-1] = $this->answers[$position];
436  $this->answers[$position] = $temp;
437  }
438  }
439 
440  public function moveAnswerDown($position)
441  {
442  if ($position < count($this->answers)-1)
443  {
444  $temp = $this->answers[$position+1];
445  $this->answers[$position+1] = $this->answers[$position];
446  $this->answers[$position] = $temp;
447  }
448  }
449 
450  protected function getRandomID()
451  {
452  $random_number = mt_rand(1, 100000);
453  $found = true;
454  while ($found)
455  {
456  $found = false;
457  foreach ($this->getAnswers() as $answer)
458  {
459  if ($answer->getRandomID() == $random_number)
460  {
461  $found = true;
462  $random_number++;
463  }
464  }
465  }
466  return $random_number;
467  }
468 
478  function getAnswer($index = 0)
479  {
480  if ($index < 0) return NULL;
481  if (count($this->answers) < 1) return NULL;
482  if ($index >= count($this->answers)) return NULL;
483  return $this->answers[$index];
484  }
485 
494  function deleteAnswer($index = 0)
495  {
496  if ($index < 0)
497  {
498  return;
499  }
500  if (count($this->answers) < 1)
501  {
502  return;
503  }
504  if ($index >= count($this->answers))
505  {
506  return;
507  }
508  unset($this->answers[$index]);
509  $this->answers = array_values($this->answers);
510  for ($i = 0; $i < count($this->answers); $i++)
511  {
512  if ($this->answers[$i]->getOrder() > $index)
513  {
514  $this->answers[$i]->setOrder($i);
515  }
516  }
517  }
518 
525  function flushAnswers()
526  {
527  $this->answers = array();
528  }
529 
537  function getAnswerCount()
538  {
539  return count($this->answers);
540  }
541 
549  {
550  if (count($this->answers) == 0)
551  {
552  $max = 0;
553  }
554  else
555  {
556  $max = $this->answers[0]->getSolutionOrder();
557  }
558  foreach ($this->answers as $key => $value)
559  {
560  if ($value->getSolutionOrder() > $max)
561  {
562  $max = $value->getSolutionOrder();
563  }
564  }
565  return $max;
566  }
567 
578  public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE)
579  {
580  if( $returndetails )
581  {
582  throw new ilTestException('return details not implemented for '.__METHOD__);
583  }
584 
585  global $ilDB;
586 
587  $found_value1 = array();
588  $found_value2 = array();
589  if (is_null($pass))
590  {
591  $pass = $this->getSolutionMaxPass($active_id);
592  }
593  $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
594  array('integer','integer','integer'),
595  array($active_id, $this->getId(), $pass)
596  );
597  $user_order = array();
598  $nested_solution = false;
599  while ($data = $ilDB->fetchAssoc($result))
600  {
601  if ((strcmp($data["value1"], "") != 0) && (strcmp($data["value2"], "") != 0))
602  {
603  if(strchr( $data['value2'],':') == true)
604  {
605 // //i.e. "61463:0" = "random_id:depth"
606  $current_solution = explode(':', $data['value2']);
607 
608  $user_order[$current_solution[0]]['index'] = $data["value1"];
609  $user_order[$current_solution[0]]['depth'] = $current_solution[1];
610  $user_order[$current_solution[0]]['random_id'] = $current_solution[0];
611 
612  $nested_solution = true;
613  }
614  else
615  {
616  $user_order[$data["value2"]] = $data["value1"];
617  $nested_solution = false;
618  }
619  }
620  }
621 
622  if($this->getOrderingType() != OQ_NESTED_PICTURES
623  && $this->getOrderingType() != OQ_NESTED_TERMS)
624  {
625  ksort($user_order);
626  $user_order = array_values($user_order);
627  }
628 
629  $points = 0;
630  $correctcount = 0;
631 
632  foreach ($this->answers as $index => $answer)
633  {
634  if($nested_solution == true)
635  {
636  $random_id = $answer->getRandomID();
637 
638  if($random_id == $user_order[$random_id]['random_id']
639  && $answer->getOrderingDepth() == $user_order[$random_id]['depth']
640  && $index == $user_order[$random_id]['index'])
641  {
642  $correctcount++;
643  }
644  }
645  else
646  {
647  if ($index == $user_order[$index])
648  {
649  $correctcount++;
650  }
651  }
652  }
653 
654  if ($correctcount == count($this->answers))
655  {
656  $points = $this->getPoints();
657  }
658 
659  return $points;
660  }
661 
668  public function getMaximumPoints()
669  {
670  return $this->getPoints();
671  }
672 
673  /*
674  * Returns the encrypted save filename of a matching picture
675  * Images are saved with an encrypted filename to prevent users from
676  * cheating by guessing the solution from the image filename
677  *
678  * @param string $filename Original filename
679  * @return string Encrypted filename
680  */
682  {
683  $extension = "";
684  if (preg_match("/.*\\.(\\w+)$/", $filename, $matches))
685  {
686  $extension = $matches[1];
687  }
688  return md5($filename) . "." . $extension;
689  }
690 
691  protected function cleanImagefiles()
692  {
693  if ($this->getOrderingType() == OQ_PICTURES)
694  {
695  if (@file_exists($this->getImagePath()))
696  {
697  $contents = ilUtil::getDir($this->getImagePath());
698  foreach ($contents as $f)
699  {
700  if (strcmp($f['type'], 'file') == 0)
701  {
702  $found = false;
703  foreach ($this->getAnswers() as $answer)
704  {
705  if (strcmp($f['entry'], $answer->getAnswertext()) == 0) $found = true;
706  if (strcmp($f['entry'], $this->getThumbPrefix() . $answer->getAnswertext()) == 0) $found = true;
707  }
708  if (!$found)
709  {
710  if (@file_exists($this->getImagePath() . $f['entry'])) @unlink($this->getImagePath() . $f['entry']);
711  }
712  }
713  }
714  }
715  }
716  else
717  {
718  if (@file_exists($this->getImagePath()))
719  {
720  ilUtil::delDir($this->getImagePath());
721  }
722  }
723  }
724 
725  /*
726  * Deletes an imagefile from the system if the file is deleted manually
727  *
728  * @param string $filename Image file filename
729  * @return boolean Success
730  */
731  public function deleteImagefile($filename)
732  {
733  $deletename = $$filename;
734  $result = @unlink($this->getImagePath().$deletename);
735  $result = $result & @unlink($this->getImagePath().$this->getThumbPrefix() . $deletename);
736  return $result;
737  }
738 
747  function setImageFile($image_tempfilename, $image_filename, $previous_filename)
748  {
749  $result = TRUE;
750  if (strlen($image_tempfilename))
751  {
752  $image_filename = str_replace(" ", "_", $image_filename);
753  $imagepath = $this->getImagePath();
754  if (!file_exists($imagepath))
755  {
756  ilUtil::makeDirParents($imagepath);
757  }
758  $savename = $image_filename;
759  if (!ilUtil::moveUploadedFile($image_tempfilename, $savename, $imagepath.$savename))
760  {
761  $result = FALSE;
762  }
763  else
764  {
765  // create thumbnail file
766  $thumbpath = $imagepath . $this->getThumbPrefix() . $savename;
767  ilUtil::convertImage($imagepath.$savename, $thumbpath, "JPEG", $this->getThumbGeometry());
768  }
769  if ($result && (strcmp($image_filename, $previous_filename) != 0) && (strlen($previous_filename)))
770  {
771  $this->deleteImagefile($previous_filename);
772  }
773  }
774  return $result;
775  }
776 
784  function checkSaveData()
785  {
786  $result = true;
787  if ($this->getOutputType() == OUTPUT_JAVASCRIPT)
788  {
789  if (strlen($_POST["orderresult"]))
790  {
791  return $result;
792  }
793  else if(strlen($_POST['answers_ordering']))
794  {
795  $answers_ordering = $_POST['answers_ordering'];
796  $new_hierarchy = json_decode($answers_ordering);
797  $with_random_id = true;
798  $this->setLeveledOrdering($new_hierarchy, $with_random_id);
799 
800  //return value as "random_id:depth"
801  return serialize($this->leveled_ordering);
802  }
803  }
804  $order_values = array();
805  foreach ($_POST as $key => $value)
806  {
807  if (preg_match("/^order_(\d+)/", $key, $matches))
808  {
809  if (strcmp($value, "") != 0)
810  {
811  array_push($order_values, $value);
812  }
813  }
814  }
815  $check_order = array_flip($order_values);
816  if (count($check_order) != count($order_values))
817  {
818  // duplicate order values!!!
819  $result = false;
820  ilUtil::sendInfo($this->lng->txt("duplicate_order_values_entered"), TRUE);
821  }
822  return $result;
823  }
824 
833  public function saveWorkingData($active_id, $pass = NULL)
834  {
835  global $ilDB;
836  global $ilUser;
837 
838  $saveWorkingDataResult = $this->checkSaveData();
839  $entered_values = 0;
840  if ($saveWorkingDataResult)
841  {
842  if (is_null($pass))
843  {
844  include_once "./Modules/Test/classes/class.ilObjTest.php";
845  $pass = ilObjTest::_getPass($active_id);
846  }
847 
848  $this->getProcessLocker()->requestUserSolutionUpdateLock();
849 
850  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
851  array('integer','integer','integer'),
852  array($active_id, $this->getId(), $pass)
853  );
854  if (array_key_exists("orderresult", $_POST))
855  {
856  $orderresult = $_POST["orderresult"];
857  if (strlen($orderresult))
858  {
859  $orderarray = explode(":", $orderresult);
860  $ordervalue = 1;
861  foreach ($orderarray as $index)
862  {
863  if (preg_match("/id_(\\d+)/", $index, $idmatch))
864  {
865  $randomid = $idmatch[1];
866  foreach ($this->getAnswers() as $answeridx => $answer)
867  {
868  if ($answer->getRandomID() == $randomid)
869  {
870  $next_id = $ilDB->nextId('tst_solutions');
871  $affectedRows = $ilDB->insert("tst_solutions", array(
872  "solution_id" => array("integer", $next_id),
873  "active_fi" => array("integer", $active_id),
874  "question_fi" => array("integer", $this->getId()),
875  "value1" => array("clob", $answeridx),
876  "value2" => array("clob", trim($ordervalue)),
877  "pass" => array("integer", $pass),
878  "tstamp" => array("integer", time())
879  ));
880  $ordervalue++;
881  $entered_values++;
882  }
883  }
884  }
885  }
886  }
887  }
888  else if($this->getOrderingType() == OQ_NESTED_TERMS
889  ||$this->getOrderingType() == OQ_NESTED_PICTURES)
890  {
891  $answers_ordering = $_POST['answers_ordering__participant'];
892  $user_solution_hierarchy = json_decode($answers_ordering);
893  $with_random_id = true;
894  $this->setLeveledOrdering($user_solution_hierarchy, $with_random_id);
895 
896  $index = 0;
897  foreach($this->leveled_ordering as $random_id=>$depth)
898  {
899  $value_2 = implode(':',array($random_id,$depth));
900  $next_id = $ilDB->nextId('tst_solutions');
901 
902  $affectedRows = $ilDB->insert("tst_solutions", array(
903  "solution_id" => array("integer", $next_id),
904  "active_fi" => array("integer", $active_id),
905  "question_fi" => array("integer", $this->getId()),
906  "value1" => array("clob", $index),
907  "value2" => array("clob", $value_2),
908  "pass" => array("integer", $pass),
909  "tstamp" => array("integer", time())
910  ));
911  $index++;
912  $entered_values++;
913  }
914  }
915  else
916  {
917  foreach ($_POST as $key => $value)
918  {
919  if (preg_match("/^order_(\d+)/", $key, $matches))
920  {
921  if (!(preg_match("/initial_value_\d+/", $value)))
922  {
923  if (strlen($value))
924  {
925  foreach ($this->getAnswers() as $answeridx => $answer)
926  {
927  if ($answer->getRandomID() == $matches[1])
928  {
929  $next_id = $ilDB->nextId('tst_solutions');
930  $affectedRows = $ilDB->insert("tst_solutions", array(
931  "solution_id" => array("integer", $next_id),
932  "active_fi" => array("integer", $active_id),
933  "question_fi" => array("integer", $this->getId()),
934  "value1" => array("clob", $answeridx),
935  "value2" => array("clob", $value),
936  "pass" => array("integer", $pass),
937  "tstamp" => array("integer", time())
938  ));
939  $entered_values++;
940  }
941  }
942  }
943  }
944  }
945  }
946  }
947 
948  $this->getProcessLocker()->releaseUserSolutionUpdateLock();
949  }
950  if ($entered_values)
951  {
952  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
954  {
955  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
956  }
957  }
958  else
959  {
960  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
962  {
963  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
964  }
965  }
966 
967  return $saveWorkingDataResult;
968  }
969 
970  public function saveAdditionalQuestionDataToDb()
971  {
973  global $ilDB;
974 
975  // save additional data
976  $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
977  array( "integer" ),
978  array( $this->getId() )
979  );
980 
981  $ilDB->manipulateF( "INSERT INTO " . $this->getAdditionalTableName() . " (question_fi, ordering_type, thumb_geometry, element_height)
982  VALUES (%s, %s, %s, %s)",
983  array( "integer", "text", "integer", "integer" ),
984  array(
985  $this->getId(),
986  $this->ordering_type,
987  $this->getThumbGeometry(),
988  ($this->getElementHeight() > 20) ? $this->getElementHeight() : NULL
989  )
990  );
991  }
992 
993  public function saveAnswerSpecificDataToDb()
994  {
996  global $ilDB;
997 
998  $ilDB->manipulateF( "DELETE FROM qpl_a_ordering WHERE question_fi = %s",
999  array( 'integer' ),
1000  array( $this->getId() )
1001  );
1002 
1003  foreach ($this->answers as $key => $value)
1004  {
1005  $answer_obj = $this->answers[$key];
1006  $next_id = $ilDB->nextId( 'qpl_a_ordering' );
1007  $ilDB->insert( 'qpl_a_ordering',
1008  array(
1009  'answer_id' => array( 'integer', $next_id ),
1010  'question_fi' => array( 'integer', $this->getId() ),
1011 // 'answertext' => array( 'text', ilRTE::_replaceMediaObjectImageSrc( $answer_obj->getAnswertext(), 0 ) ),
1012  'answertext' => array( 'text', $answer_obj->getAnswertext()),
1013  'solution_order' => array( 'integer', $key ),
1014  'random_id' => array( 'integer', $answer_obj->getRandomID() ),
1015  'tstamp' => array( 'integer', time() ),
1016  'depth' => array( 'integer', $answer_obj->getOrderingDepth() )
1017  )
1018  );
1019  }
1020 
1021  if ($this->getOrderingType() == OQ_PICTURES)
1022  {
1023  $this->rebuildThumbnails();
1024  $this->cleanImagefiles();
1025  }
1026  }
1027 
1036  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
1037  {
1038  // nothing to rework!
1039  }
1040 
1047  function getQuestionType()
1048  {
1049  return "assOrderingQuestion";
1050  }
1051 
1059  {
1060  return "qpl_qst_ordering";
1061  }
1062 
1070  {
1071  return "qpl_a_ordering";
1072  }
1073 
1079  {
1080  $text = parent::getRTETextWithMediaObjects();
1081  foreach ($this->answers as $index => $answer)
1082  {
1083  $answer_obj = $this->answers[$index];
1084  $text .= $answer_obj->getAnswertext();
1085  }
1086  return $text;
1087  }
1088 
1092  function &getAnswers()
1093  {
1094  return $this->answers;
1095  }
1096 
1103  public function supportsJavascriptOutput()
1104  {
1105  return TRUE;
1106  }
1107 
1108  public function supportsNonJsOutput()
1109  {
1110  return false;
1111  }
1112 
1125  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
1126  {
1127  include_once ("./Services/Excel/classes/class.ilExcelUtils.php");
1128  $solutions = $this->getSolutionValues($active_id, $pass);
1129  $sol = array();
1130  foreach ($solutions as $solution)
1131  {
1132  $sol[$solution["value1"]] = $solution["value2"];
1133  }
1134  asort($sol);
1135  $sol = array_keys($sol);
1136  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
1137  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
1138  $i = 1;
1139  $answers = $this->getAnswers();
1140  foreach ($sol as $idx)
1141  {
1142  foreach ($solutions as $solution)
1143  {
1144  if ($solution["value1"] == $idx) $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($solution["value2"]));
1145  }
1146  $worksheet->writeString($startrow + $i, 1, ilExcelUtils::_convert_text($answers[$idx]->getAnswertext()));
1147  $i++;
1148  }
1149  return $startrow + $i + 1;
1150  }
1151 
1152  /*
1153  * Get the thumbnail geometry
1154  *
1155  * @return integer Geometry
1156  */
1157  public function getThumbGeometry()
1158  {
1159  return $this->thumb_geometry;
1160  }
1161 
1162  public function getThumbSize()
1163  {
1164  return $this->getThumbGeometry();
1165  }
1166 
1167  /*
1168  * Set the thumbnail geometry
1169  *
1170  * @param integer $a_geometry Geometry
1171  */
1172  public function setThumbGeometry($a_geometry)
1173  {
1174  $this->thumb_geometry = ($a_geometry < 1) ? 100 : $a_geometry;
1175  }
1176 
1177  /*
1178  * Get the minimum element height
1179  *
1180  * @return integer Height
1181  */
1182  public function getElementHeight()
1183  {
1184  return $this->element_height;
1185  }
1186 
1187  /*
1188  * Set the minimum element height
1189  *
1190  * @param integer $a_height Height
1191  */
1192  public function setElementHeight($a_height)
1193  {
1194  $this->element_height = ($a_height < 20) ? "" : $a_height;
1195  }
1196 
1197  /*
1198  * Rebuild the thumbnail images with a new thumbnail size
1199  */
1200  public function rebuildThumbnails()
1201  {
1202  if ($this->getOrderingType() == OQ_PICTURES || $this->getOrderingType() == OQ_NESTED_PICTURES)
1203  {
1204  foreach ($this->getAnswers() as $answer)
1205  {
1206  $this->generateThumbForFile($this->getImagePath(), $answer->getAnswertext());
1207  }
1208  }
1209  }
1210 
1211  public function getThumbPrefix()
1212  {
1213  return "thumb.";
1214  }
1215 
1216  protected function generateThumbForFile($path, $file)
1217  {
1218  $filename = $path . $file;
1219  if (@file_exists($filename))
1220  {
1221  $thumbpath = $path . $this->getThumbPrefix() . $file;
1222  $path_info = @pathinfo($filename);
1223  $ext = "";
1224  switch (strtoupper($path_info['extension']))
1225  {
1226  case 'PNG':
1227  $ext = 'PNG';
1228  break;
1229  case 'GIF':
1230  $ext = 'GIF';
1231  break;
1232  default:
1233  $ext = 'JPEG';
1234  break;
1235  }
1236  ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbGeometry());
1237  }
1238  }
1239 
1243  public function toJSON()
1244  {
1245  include_once("./Services/RTE/classes/class.ilRTE.php");
1246  $result = array();
1247  $result['id'] = (int) $this->getId();
1248  $result['type'] = (string) $this->getQuestionType();
1249  $result['title'] = (string) $this->getTitle();
1250  $result['question'] = $this->formatSAQuestion($this->getQuestion());
1251  $result['nr_of_tries'] = (int) $this->getNrOfTries();
1252  $result['shuffle'] = (bool) true;
1253  $result['points'] = (bool) $this->getPoints();
1254  $result['feedback'] = array(
1255  "onenotcorrect" => $this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false),
1256  "allcorrect" => $this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true)
1257  );
1258  if ($this->getOrderingType() == OQ_PICTURES)
1259  {
1260  $result['path'] = $this->getImagePathWeb();
1261  }
1262 
1263  $counter = 1;
1264  $answers = array();
1265  foreach ($this->getAnswers() as $answer_obj)
1266  {
1267  $answers[$counter] = $answer_obj->getAnswertext();
1268  $counter++;
1269  }
1270  $answers = $this->pcArrayShuffle($answers);
1271  $arr = array();
1272  foreach ($answers as $order => $answer)
1273  {
1274  array_push($arr, array(
1275  "answertext" => (string) $answer,
1276  "order" => (int) $order
1277  ));
1278  }
1279  $result['answers'] = $arr;
1280 
1281  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1282  $result['mobs'] = $mobs;
1283 
1284  return json_encode($result);
1285  }
1286 
1287  public function removeAnswerImage($index)
1288  {
1289  $answer = $this->answers[$index];
1290  if (is_object($answer))
1291  {
1292  $this->deleteImagefile($answer->getAnswertext());
1293  $answer->setAnswertext('');
1294  }
1295  }
1296 
1297  /***
1298  * @param object $child
1299  * @param integer $ordering_depth
1300  * @param bool $with_random_id
1301  */
1302  private function getDepthRecursive($child, $ordering_depth, $with_random_id = false)
1303  {
1304  if($with_random_id == true)
1305  {
1306  // for test ouput
1307  if(is_array($child->children))
1308  {
1309  foreach($child->children as $grand_child)
1310  {
1311  $ordering_depth++;
1312  $this->leveled_ordering[$child->id] = $ordering_depth;
1313  $this->getDepthRecursive($grand_child, $ordering_depth, true);
1314  }
1315  }
1316  else
1317  {
1318  $ordering_depth++;
1319  $this->leveled_ordering[$child->id] = $ordering_depth;
1320  }
1321  }
1322  else
1323  {
1324  if(is_array($child->children))
1325  {
1326  foreach($child->children as $grand_child)
1327  {
1328  $ordering_depth++;
1329  $this->leveled_ordering[] = $ordering_depth;
1330  $this->getDepthRecursive($grand_child, $ordering_depth);
1331  }
1332  }
1333  else
1334  {
1335  $ordering_depth++;
1336  $this->leveled_ordering[] = $ordering_depth;
1337  }
1338  }
1339  }
1340 
1341  /***
1342  * @param array $new_hierarchy
1343  * @param bool $with_random_id
1344  */
1345  public function setLeveledOrdering($new_hierarchy, $with_random_id = false)
1346  {
1347  if($with_random_id == true)
1348  {
1349  //for test output
1350  foreach($new_hierarchy as $id)
1351  {
1352  $ordering_depth = 0;
1353  $this->leveled_ordering[$id->id] = $ordering_depth;
1354 
1355  if(is_array($id->children))
1356  {
1357  foreach($id->children as $child)
1358  {
1359  $this->getDepthRecursive($child, $ordering_depth, true);
1360 }
1361  }
1362  }
1363  }
1364  else
1365  {
1366  foreach($new_hierarchy as $id)
1367  {
1368  $ordering_depth = 0;
1369  $this->leveled_ordering[] = $ordering_depth;
1370 
1371  if(is_array($id->children))
1372  {
1373  foreach($id->children as $child)
1374  {
1375  $this->getDepthRecursive($child, $ordering_depth, $with_random_id);
1376  }
1377  }
1378  }
1379  }
1380  }
1381  public function getLeveledOrdering()
1382  {
1383  return $this->leveled_ordering;
1384  }
1385 
1386  public function getOldLeveledOrdering()
1387  {
1388  global $ilDB;
1389 
1390  $res = $ilDB->queryF('SELECT depth FROM qpl_a_ordering WHERE question_fi = %s ORDER BY solution_order ASC',
1391  array('integer'), array($this->getId()));
1392  while($row = $ilDB->fetchAssoc($res))
1393  {
1394  $this->old_ordering_depth[] = $row['depth'];
1395  }
1397  }
1398 
1399  /***
1400  * @param integer $a_random_id
1401  * @return integer
1402  */
1403  public function lookupSolutionOrderByRandomid($a_random_id)
1404  {
1405  global $ilDB;
1406 
1407  $res = $ilDB->queryF('SELECT solution_order FROM qpl_a_ordering WHERE random_id = %s',
1408  array('integer'), array($a_random_id));
1409  $row = $ilDB->fetchAssoc($res);
1410 
1411  return $row['solution_order'];
1412  }
1413 
1414  /***
1415  * @param integer $a_random_id
1416  * @return string
1417  */
1418  public function lookupAnswerTextByRandomId($a_random_id)
1419  {
1420  global $ilDB;
1421 
1422  $res = $ilDB->queryF('SELECT answertext FROM qpl_a_ordering WHERE random_id = %s',
1423  array('integer'), array($a_random_id));
1424  $row = $ilDB->fetchAssoc($res);
1425 
1426  return $row['answertext'];
1427  }
1428 
1429  public function updateLeveledOrdering($a_index, $a_answer_text, $a_depth)
1430  {
1431  global $ilDB;
1432 
1433  $ilDB->update('qpl_a_ordering',
1434  array('solution_order'=> array('integer', $a_index),
1435  'depth' => array('integer', $a_depth)),
1436  array('answertext' => array('text', $a_answer_text)));
1437 
1438  return true;
1439  }
1440 }
1441 ?>
duplicateImages($src_question_id, $src_object_id, $dest_question_id, $dest_object_id)
static makeDirParents($a_dir)
Create a new directory and all parent directories.
getId()
Gets the id of the assQuestion object.
print $file
static _getOriginalId($question_id)
Returns the original id of a question.
formatSAQuestion($a_q)
Format self assessment question.
deleteAnswer($index=0)
Deletes an answer with a given index.
$_POST['username']
Definition: cron.php:12
$result
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
getPoints()
Returns the maximum available points for the question.
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
Class for ordering question answers.
& getAnswers()
Returns the answers array.
& getSolutionValues($active_id, $pass=NULL)
Loads solutions of a given user from the database an returns it.
Abstract basic class which is to be extended by the concrete assessment question type classes...
_getPass($active_id)
Retrieves the actual pass of a given user for a given test.
isComplete()
Returns true, if a ordering question is complete for use.
getOrderingType()
Returns the ordering question type.
_convert_text($a_text, $a_target="has been removed")
const OQ_NESTED_TERMS
getDepthRecursive($child, $ordering_depth, $with_random_id=false)
getQuestionType()
Returns the question type of the question.
calculateReachedPoints($active_id, $pass=NULL, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
setId($id=-1)
Sets the id of the assQuestion object.
copyObject($target_questionpool_id, $title="")
Copies an assOrderingQuestion object.
static getDir($a_dir, $a_rec=false, $a_sub_dir="")
get directory
saveWorkingData($active_id, $pass=NULL)
Saves the learners input of the question to the database.
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.
getAnswer($index=0)
Returns an ordering answer with a given index.
setNrOfTries($a_nr_of_tries)
_enabledAssessmentLogging()
check wether assessment logging is enabled or not
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
const OUTPUT_JAVASCRIPT
getObjId()
Get the object id of the container object.
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
Base Exception for all Exceptions relating to Modules/Test.
$mobs
checkSaveData()
Checks the data to be saved for consistency.
static sendInfo($a_info="", $a_keep=false)
Send Info Message to Screen.
fetchAssoc($a_set)
Fetch row as associative array from result set.
getMaxSolutionOrder()
Returns the maximum solution order of all ordering answers.
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
Duplicates an assOrderingQuestion.
setAuthor($author="")
Sets the authors name of the assQuestion object.
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
const OQ_PICTURES
Ordering question constants.
getAnswerTableName()
Returns the name of the answer table in the database.
static moveUploadedFile($a_file, $a_name, $a_target, $a_raise_errors=true, $a_mode="move_uploaded")
move uploaded file
setLeveledOrdering($new_hierarchy, $with_random_id=false)
Interface ilObjAnswerScoringAdjustable.
toJSON()
Returns a JSON representation of the question.
saveToDb($original_id="")
Saves a assOrderingQuestion object to a database.
__construct( $title="", $comment="", $author="", $owner=-1, $question="", $ordering_type=OQ_TERMS)
assOrderingQuestion constructor
getQuestion()
Gets the question string of the question object.
flushAnswers()
Deletes all answers.
_getLogLanguage()
retrieve the log language for assessment logging
$filename
Definition: buildRTE.php:89
_getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
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="")
setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
Creates an Excel worksheet for the detailed cumulated results of this question.
static _replaceMediaObjectImageSrc($a_text, $a_direction=0)
replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
while($lm_rec=$ilDB->fetchAssoc($lm_set)) $data
reworkWorkingData($active_id, $pass, $obligationsAnswered)
Reworks the allready saved working data if neccessary.
const OQ_TERMS
global $ilUser
Definition: imgupload.php:15
supportsJavascriptOutput()
Returns true if the question type supports JavaScript output.
setQuestion($question="")
Sets the question string of the question object.
static convertImage($a_from, $a_to, $a_target_format="", $a_geometry="", $a_background_color="")
convert image
Interface ilObjQuestionScoringAdjustable.
const OQ_NESTED_PICTURES
$path
Definition: index.php:22
setOriginalId($original_id)
Class for ordering questions.
updateLeveledOrdering($a_index, $a_answer_text, $a_depth)
logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
getTitle()
Gets the title string of the assQuestion object.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
setImageFile($image_tempfilename, $image_filename, $previous_filename)
Sets the image file and uploads the image to the object&#39;s image directory.
copyImages($question_id, $source_questionpool)
pcArrayShuffle($array)
Shuffles the values of a given array.
getOutputType()
Gets the output type.
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
setComment($comment="")
Sets the comment string of the assQuestion object.
loadFromDb($question_id)
Loads a assOrderingQuestion object from a database.
setOrderingType($ordering_type=OQ_TERMS)
Sets the ordering question type.
addAnswer($answertext="", $solution_order=-1, $depth=0)
Adds an answer for an ordering choice question.
getAdditionalTableName()
Returns the name of the additional question data table in the database.
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
getAnswerCount()
Returns the number of answers.