ILIAS  release_4-3 Revision
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.assMatchingQuestion.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
5 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
6 include_once "./Modules/Test/classes/inc.AssessmentConstants.php";
7 
22 {
31 
41 
47  protected $terms;
48  protected $definitions;
49 
55  var $thumb_geometry = 100;
56 
63 
76  function __construct(
77  $title = "",
78  $comment = "",
79  $author = "",
80  $owner = -1,
81  $question = "",
83  )
84  {
86  $this->matchingpairs = array();
87  $this->matching_type = $matching_type;
88  $this->terms = array();
89  $this->definitions = array();
90  }
91 
98  function isComplete()
99  {
100  if (strlen($this->title) and ($this->author) and ($this->question) and (count($this->matchingpairs)) and ($this->getMaximumPoints() > 0))
101  {
102  return true;
103  }
104  else
105  {
106  return false;
107  }
108  }
109 
115  public function saveToDb($original_id = "")
116  {
117  global $ilDB;
118 
120 
121  // save additional data
122  $affectedRows = $ilDB->manipulateF("DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
123  array("integer"),
124  array($this->getId())
125  );
126  $affectedRows = $ilDB->manipulateF("INSERT INTO " . $this->getAdditionalTableName() . " (question_fi, shuffle, matching_type, thumb_geometry, element_height) VALUES (%s, %s, %s, %s, %s)",
127  array("integer", "text", "text","integer","integer"),
128  array(
129  $this->getId(),
130  $this->shuffle,
131  $this->matching_type,
132  $this->getThumbGeometry(),
133  ($this->getElementHeight() >= 20) ? $this->getElementHeight() : NULL
134  )
135  );
136 
137  // delete old terms
138  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_a_mterm WHERE question_fi = %s",
139  array('integer'),
140  array($this->getId())
141  );
142 
143  // delete old definitions
144  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_a_mdef WHERE question_fi = %s",
145  array('integer'),
146  array($this->getId())
147  );
148 
149  $termids = array();
150  // write terms
151  foreach ($this->terms as $key => $term)
152  {
153  $next_id = $ilDB->nextId('qpl_a_mterm');
154  $affectedRows = $ilDB->manipulateF("INSERT INTO qpl_a_mterm (term_id, question_fi, picture, term) VALUES (%s, %s, %s, %s)",
155  array('integer','integer','text', 'text'),
156  array($next_id, $this->getId(), $term->picture, $term->text)
157  );
158  $termids[$term->identifier] = $next_id;
159  }
160 
161  $definitionids = array();
162  // write definitions
163  foreach ($this->definitions as $key => $definition)
164  {
165  $next_id = $ilDB->nextId('qpl_a_mdef');
166  $affectedRows = $ilDB->manipulateF("INSERT INTO qpl_a_mdef (def_id, question_fi, picture, definition, morder) VALUES (%s, %s, %s, %s, %s)",
167  array('integer','integer','text', 'text', 'integer'),
168  array($next_id, $this->getId(), $definition->picture, $definition->text, $definition->identifier)
169  );
170  $definitionids[$definition->identifier] = $next_id;
171  }
172 
173  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_a_matching WHERE question_fi = %s",
174  array('integer'),
175  array($this->getId())
176  );
177  $matchingpairs = $this->getMatchingPairs();
178  foreach ($matchingpairs as $key => $pair)
179  {
180  $next_id = $ilDB->nextId('qpl_a_matching');
181  $affectedRows = $ilDB->manipulateF("INSERT INTO qpl_a_matching (answer_id, question_fi, points, term_fi, definition_fi) VALUES (%s, %s, %s, %s, %s)",
182  array('integer','integer','float','integer','integer'),
183  array(
184  $next_id,
185  $this->getId(),
186  $pair->points,
187  $termids[$pair->term->identifier],
188  $definitionids[$pair->definition->identifier]
189  )
190  );
191  }
192 
193  $this->rebuildThumbnails();
194 
196  }
197 
204  public function loadFromDb($question_id)
205  {
206  global $ilDB;
207 
208  $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",
209  array("integer"),
210  array($question_id)
211  );
212  if ($result->numRows() == 1)
213  {
214  $data = $ilDB->fetchAssoc($result);
215  $this->setId($question_id);
216  $this->setObjId($data["obj_fi"]);
217  $this->setTitle($data["title"]);
218  $this->setComment($data["description"]);
219  $this->setOriginalId($data["original_id"]);
220  $this->setNrOfTries($data['nr_of_tries']);
221  $this->setAuthor($data["author"]);
222  $this->setPoints($data["points"]);
223  $this->setOwner($data["owner"]);
224  include_once("./Services/RTE/classes/class.ilRTE.php");
225  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
226  $this->setThumbGeometry($data["thumb_geometry"]);
227  $this->setElementHeight($data["element_height"]);
228  $this->setShuffle($data["shuffle"]);
229  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
230  }
231 
232  $termids = array();
233  $result = $ilDB->queryF("SELECT * FROM qpl_a_mterm WHERE question_fi = %s ORDER BY term_id ASC",
234  array('integer'),
235  array($question_id)
236  );
237  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingTerm.php";
238  $this->terms = array();
239  if ($result->numRows() > 0)
240  {
241  while ($data = $ilDB->fetchAssoc($result))
242  {
243  $term = new assAnswerMatchingTerm($data['term'], $data['picture'], $data['term_id']);
244  array_push($this->terms, $term);
245  $termids[$data['term_id']] = $term;
246  }
247  }
248 
249  $definitionids = array();
250  $result = $ilDB->queryF("SELECT * FROM qpl_a_mdef WHERE question_fi = %s ORDER BY def_id ASC",
251  array('integer'),
252  array($question_id)
253  );
254  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingDefinition.php";
255  $this->definitions = array();
256  if ($result->numRows() > 0)
257  {
258  while ($data = $ilDB->fetchAssoc($result))
259  {
260  $definition = new assAnswerMatchingDefinition($data['definition'], $data['picture'], $data['morder']);
261  array_push($this->definitions, $definition);
262  $definitionids[$data['def_id']] = $definition;
263  }
264  }
265 
266  $this->matchingpairs = array();
267  $result = $ilDB->queryF("SELECT * FROM qpl_a_matching WHERE question_fi = %s ORDER BY answer_id",
268  array('integer'),
269  array($question_id)
270  );
271  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingPair.php";
272  if ($result->numRows() > 0)
273  {
274  while ($data = $ilDB->fetchAssoc($result))
275  {
276  array_push($this->matchingpairs, new assAnswerMatchingPair($termids[$data['term_fi']], $definitionids[$data['definition_fi']], $data['points']));
277  }
278  }
279  parent::loadFromDb($question_id);
280  }
281 
282 
286  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
287  {
288  if ($this->id <= 0)
289  {
290  // The question has not been saved. It cannot be duplicated
291  return;
292  }
293  // duplicate the question in database
294  $this_id = $this->getId();
295 
296  if( (int)$testObjId > 0 )
297  {
298  $thisObjId = $this->getObjId();
299  }
300 
301  $clone = $this;
302  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
304  $clone->id = -1;
305 
306  if( (int)$testObjId > 0 )
307  {
308  $clone->setObjId($testObjId);
309  }
310 
311  if ($title)
312  {
313  $clone->setTitle($title);
314  }
315  if ($author)
316  {
317  $clone->setAuthor($author);
318  }
319  if ($owner)
320  {
321  $clone->setOwner($owner);
322  }
323  if ($for_test)
324  {
325  $clone->saveToDb($original_id);
326  }
327  else
328  {
329  $clone->saveToDb();
330  }
331 
332  // copy question page content
333  $clone->copyPageOfQuestion($this_id);
334  // copy XHTML media objects
335  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
336  // duplicate the generic feedback
337  $clone->duplicateGenericFeedback($this_id);
338  // duplicate the specific feedback
339  $clone->duplicateSpecificFeedback($this_id);
340 
341  // duplicate the image
342  $clone->duplicateImages($this_id, $thisObjId);
343 
344  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
345 
346  return $clone->id;
347  }
348 
352  public function copyObject($target_questionpool, $title = "")
353  {
354  if ($this->id <= 0)
355  {
356  // The question has not been saved. It cannot be duplicated
357  return;
358  }
359  // duplicate the question in database
360  $clone = $this;
361  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
363  $clone->id = -1;
364  if ($title)
365  {
366  $clone->setTitle($title);
367  }
368  $source_questionpool = $this->getObjId();
369  $clone->setObjId($target_questionpool);
370  $clone->saveToDb();
371 
372  // copy question page content
373  $clone->copyPageOfQuestion($original_id);
374  // copy XHTML media objects
375  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
376  // duplicate the generic feedback
377  $clone->duplicateGenericFeedback($original_id);
378  // duplicate specific feedback
379  $clone->duplicateSpecificFeedback($original_id);
380 
381  // duplicate the image
382  $clone->copyImages($original_id, $source_questionpool);
383  $clone->onCopy($this->getObjId(), $this->getId());
384  return $clone->id;
385  }
386 
387  public function duplicateImages($question_id, $objectId = null)
388  {
389  global $ilLog;
390  $imagepath = $this->getImagePath();
391  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
392 
393  if( (int)$objectId > 0 )
394  {
395  $imagepath_original = str_replace("/$this->obj_id/", "/$objectId/", $imagepath_original);
396  }
397 
398  foreach ($this->terms as $term)
399  {
400  if (strlen($term->picture))
401  {
402  $filename = $term->picture;
403  if (!file_exists($imagepath))
404  {
405  ilUtil::makeDirParents($imagepath);
406  }
407  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
408  {
409  $ilLog->write("matching question image could not be duplicated: $imagepath_original$filename");
410  }
411  if (@file_exists($imagepath_original . $this->getThumbPrefix() . $filename))
412  {
413  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
414  {
415  $ilLog->write("matching question image thumbnail could not be duplicated: $imagepath_original" . $this->getThumbPrefix() . $filename);
416  }
417  }
418  }
419  }
420  foreach ($this->definitions as $definition)
421  {
422  if (strlen($definition->picture))
423  {
424  $filename = $definition->picture;
425  if (!file_exists($imagepath))
426  {
427  ilUtil::makeDirParents($imagepath);
428  }
429  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
430  {
431  $ilLog->write("matching question image could not be duplicated: $imagepath_original$filename");
432  }
433  if (@file_exists($imagepath_original . $this->getThumbPrefix() . $filename))
434  {
435  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
436  {
437  $ilLog->write("matching question image thumbnail could not be duplicated: $imagepath_original" . $this->getThumbPrefix() . $filename);
438  }
439  }
440  }
441  }
442  }
443 
444  public function copyImages($question_id, $source_questionpool)
445  {
446  global $ilLog;
447 
448  $imagepath = $this->getImagePath();
449  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
450  $imagepath_original = str_replace("/$this->obj_id/", "/$source_questionpool/", $imagepath_original);
451  foreach ($this->terms as $term)
452  {
453  if (strlen($term->picture))
454  {
455  if (!file_exists($imagepath))
456  {
457  ilUtil::makeDirParents($imagepath);
458  }
459  $filename = $term->picture;
460  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
461  {
462  $ilLog->write("matching question image could not be copied: $imagepath_original$filename");
463  }
464  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
465  {
466  $ilLog->write("matching question image thumbnail could not be copied: $imagepath_original" . $this->getThumbPrefix() . $filename);
467  }
468  }
469  }
470  foreach ($this->definitions as $definition)
471  {
472  if (strlen($definition->picture))
473  {
474  $filename = $definition->picture;
475  if (!file_exists($imagepath))
476  {
477  ilUtil::makeDirParents($imagepath);
478  }
479  if (!copy($imagepath_original . $filename, $imagepath . $filename))
480  {
481  $ilLog->write("matching question image could not be copied: $imagepath_original$filename");
482  }
483  if (!copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
484  {
485  $ilLog->write("matching question image thumbnail could not be copied: $imagepath_original" . $this->getThumbPrefix() . $filename);
486  }
487  }
488  }
489  }
490 
501  public function insertMatchingPair($position, $term = null, $definition = null, $points = 0.0)
502  {
503  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingPair.php";
504  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingTerm.php";
505  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingDefinition.php";
506  if (is_null($term)) $term = new assAnswerMatchingTerm();
507  if (is_null($definition)) $definition = new assAnswerMatchingDefinition();
508  $pair = new assAnswerMatchingPair($term, $definition, $points);
509  if ($position < count($this->matchingpairs))
510  {
511  $part1 = array_slice($this->matchingpairs, 0, $position);
512  $part2 = array_slice($this->matchingpairs, $position);
513  $this->matchingpairs = array_merge($part1, array($pair), $part2);
514  }
515  else
516  {
517  array_push($this->matchingpairs, $pair);
518  }
519  }
529  public function addMatchingPair($term = null, $definition = null, $points = 0.0)
530  {
531  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingPair.php";
532  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingTerm.php";
533  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingDefinition.php";
534  if (is_null($term)) $term = new assAnswerMatchingTerm();
535  if (is_null($definition)) $definition = new assAnswerMatchingDefinition();
536  $pair = new assAnswerMatchingPair($term, $definition, $points);
537  array_push($this->matchingpairs, $pair);
538  }
539 
543  public function getTermWithIdentifier($a_identifier)
544  {
545  foreach ($this->terms as $term)
546  {
547  if ($term->identifier == $a_identifier) return $term;
548  }
549  return null;
550  }
551 
555  public function getDefinitionWithIdentifier($a_identifier)
556  {
557  foreach ($this->definitions as $definition)
558  {
559  if ($definition->identifier == $a_identifier) return $definition;
560  }
561  return null;
562  }
563 
572  public function getMatchingPair($index = 0)
573  {
574  if ($index < 0)
575  {
576  return NULL;
577  }
578  if (count($this->matchingpairs) < 1)
579  {
580  return NULL;
581  }
582  if ($index >= count($this->matchingpairs))
583  {
584  return NULL;
585  }
586  return $this->matchingpairs[$index];
587  }
588 
596  public function deleteMatchingPair($index = 0)
597  {
598  if ($index < 0)
599  {
600  return;
601  }
602  if (count($this->matchingpairs) < 1)
603  {
604  return;
605  }
606  if ($index >= count($this->matchingpairs))
607  {
608  return;
609  }
610  unset($this->matchingpairs[$index]);
611  $this->matchingpairs = array_values($this->matchingpairs);
612  }
613 
618  public function flushMatchingPairs()
619  {
620  $this->matchingpairs = array();
621  }
622 
629  public function getMatchingPairCount()
630  {
631  return count($this->matchingpairs);
632  }
633 
640  public function getTerms()
641  {
642  return $this->terms;
643  }
644 
651  public function getDefinitions()
652  {
653  return $this->definitions;
654  }
655 
662  public function getTermCount()
663  {
664  return count($this->terms);
665  }
666 
673  public function getDefinitionCount()
674  {
675  return count($this->definitions);
676  }
677 
684  public function addTerm($term)
685  {
686  array_push($this->terms, $term);
687  }
688 
695  public function addDefinition($definition)
696  {
697  array_push($this->definitions, $definition);
698  }
699 
706  public function insertTerm($position, $term = null)
707  {
708  if (is_null($term))
709  {
710  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingTerm.php";
711  $term = new assAnswerMatchingTerm();
712  }
713  if ($position < count($this->terms))
714  {
715  $part1 = array_slice($this->terms, 0, $position);
716  $part2 = array_slice($this->terms, $position);
717  $this->terms = array_merge($part1, array($term), $part2);
718  }
719  else
720  {
721  array_push($this->terms, $term);
722  }
723  }
724 
731  public function insertDefinition($position, $definition = null)
732  {
733  if (is_null($definition))
734  {
735  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingDefinition.php";
736  $definition = new assAnswerMatchingDefinition();
737  }
738  if ($position < count($this->definitions))
739  {
740  $part1 = array_slice($this->definitions, 0, $position);
741  $part2 = array_slice($this->definitions, $position);
742  $this->definitions = array_merge($part1, array($definition), $part2);
743  }
744  else
745  {
746  array_push($this->definitions, $definition);
747  }
748  }
749 
754  public function flushTerms()
755  {
756  $this->terms = array();
757  }
758 
763  public function flushDefinitions()
764  {
765  $this->definitions = array();
766  }
767 
774  public function deleteTerm($position)
775  {
776  unset($this->terms[$position]);
777  $this->terms = array_values($this->terms);
778  }
779 
786  public function deleteDefinition($position)
787  {
788  unset($this->definitions[$position]);
789  $this->definitions = array_values($this->definitions);
790  }
791 
799  public function setTerm($term, $index)
800  {
801  $this->terms[$index] = $term;
802  }
803 
814  public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE)
815  {
816  if( $returndetails )
817  {
818  throw new ilTestException('return details not implemented for '.__METHOD__);
819  }
820 
821  global $ilDB;
822 
823  $found_value1 = array();
824  $found_value2 = array();
825  if (is_null($pass))
826  {
827  $pass = $this->getSolutionMaxPass($active_id);
828  }
829  $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
830  array('integer','integer','integer'),
831  array($active_id, $this->getId(), $pass)
832  );
833  while ($data = $ilDB->fetchAssoc($result))
834  {
835  if (strcmp($data["value1"], "") != 0)
836  {
837  array_push($found_value1, $data["value1"]);
838  array_push($found_value2, $data["value2"]);
839  }
840  }
841  $points = 0;
842  foreach ($found_value2 as $key => $value)
843  {
844  foreach ($this->matchingpairs as $pair)
845  {
846  if (($pair->definition->identifier == $value) && ($pair->term->identifier == $found_value1[$key]))
847  {
848  $points += $pair->points;
849  }
850  }
851  }
852 
853  return $points;
854  }
855 
859  function getMaximumPoints()
860  {
861  $points = 0;
862  foreach ($this->matchingpairs as $key => $pair)
863  {
864  if ($pair->points > 0)
865  {
866  $points += $pair->points;
867  }
868  }
869  return $points;
870  }
871 
881  {
882  $extension = "";
883  if (preg_match("/.*\\.(\\w+)$/", $filename, $matches))
884  {
885  $extension = $matches[1];
886  }
887  return md5($filename) . "." . $extension;
888  }
889 
890  public function removeTermImage($index)
891  {
892  $term = $this->terms[$index];
893  if (is_object($term))
894  {
895  $this->deleteImagefile($term->picture);
896  $term->picture = null;
897  }
898  }
899 
900  public function removeDefinitionImage($index)
901  {
902  $definition = $this->definitions[$index];
903  if (is_object($definition))
904  {
905  $this->deleteImagefile($definition->picture);
906  $definition->picture = null;
907  }
908  }
909 
910 
917  public function deleteImagefile($filename)
918  {
919  $deletename = $filename;
920  $result = @unlink($this->getImagePath().$deletename);
921  $result = $result & @unlink($this->getImagePath().$this->getThumbPrefix() . $deletename);
922  return $result;
923  }
924 
933  function setImageFile($image_tempfilename, $image_filename, $previous_filename = '')
934  {
935  $result = TRUE;
936  if (strlen($image_tempfilename))
937  {
938  $image_filename = str_replace(" ", "_", $image_filename);
939  $imagepath = $this->getImagePath();
940  if (!file_exists($imagepath))
941  {
942  ilUtil::makeDirParents($imagepath);
943  }
944  $savename = $image_filename;
945  if (!ilUtil::moveUploadedFile($image_tempfilename, $savename, $imagepath.$savename))
946  {
947  $result = FALSE;
948  }
949  else
950  {
951  // create thumbnail file
952  $thumbpath = $imagepath . $this->getThumbPrefix() . $savename;
953  ilUtil::convertImage($imagepath.$savename, $thumbpath, "JPEG", $this->getThumbGeometry());
954  }
955  if ($result && (strcmp($image_filename, $previous_filename) != 0) && (strlen($previous_filename)))
956  {
957  $this->deleteImagefile($previous_filename);
958  }
959  }
960  return $result;
961  }
962 
969  function checkSaveData()
970  {
971  $result = true;
972  $matching_values = array();
973  foreach ($_POST['matching'][$this->getId()] as $definition => $term)
974  {
975  if ($term > 0)
976  {
977  array_push($matching_values, $term);
978  }
979  }
980 
981  $check_matching = array_flip($matching_values);
982  if (count($check_matching) != count($matching_values))
983  {
984  $result = false;
985  ilUtil::sendFailure($this->lng->txt("duplicate_matching_values_selected"), true);
986  }
987  return $result;
988  }
989 
998  public function saveWorkingData($active_id, $pass = NULL)
999  {
1000  global $ilDB;
1001  global $ilUser;
1002  $saveWorkingDataResult = $this->checkSaveData();
1003  $entered_values = 0;
1004  if ($saveWorkingDataResult)
1005  {
1006  if (is_null($pass))
1007  {
1008  include_once "./Modules/Test/classes/class.ilObjTest.php";
1009  $pass = ilObjTest::_getPass($active_id);
1010  }
1011 
1012  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
1013  array('integer','integer','integer'),
1014  array($active_id, $this->getId(), $pass)
1015  );
1016 
1017  foreach ($_POST['matching'][$this->getId()] as $definition => $term)
1018  {
1019  $entered_values++;
1020  $next_id = $ilDB->nextId('tst_solutions');
1021  $affectedRows = $ilDB->insert("tst_solutions", array(
1022  "solution_id" => array("integer", $next_id),
1023  "active_fi" => array("integer", $active_id),
1024  "question_fi" => array("integer", $this->getId()),
1025  "value1" => array("clob", $term),
1026  "value2" => array("clob", $definition),
1027  "pass" => array("integer", $pass),
1028  "tstamp" => array("integer", time())
1029  ));
1030  }
1031  $saveWorkingDataResult = true;
1032  }
1033  if ($entered_values)
1034  {
1035  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1037  {
1038  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
1039  }
1040  }
1041  else
1042  {
1043  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1045  {
1046  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
1047  }
1048  }
1049 
1050  return $saveWorkingDataResult;
1051  }
1052 
1061  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
1062  {
1063  // nothing to rework!
1064  }
1065 
1066  public function getRandomId()
1067  {
1068  mt_srand((double)microtime()*1000000);
1069  $random_number = mt_rand(1, 100000);
1070  $found = FALSE;
1071  while ($found)
1072  {
1073  $found = FALSE;
1074  foreach ($this->matchingpairs as $key => $pair)
1075  {
1076  if (($pair->term->identifier == $random_number) || ($pair->definition->identifier == $random_number))
1077  {
1078  $found = TRUE;
1079  $random_number++;
1080  }
1081  }
1082  }
1083  return $random_number;
1084  }
1085 
1092  public function setShuffle($shuffle)
1093  {
1094  switch ($shuffle)
1095  {
1096  case 0:
1097  case 1:
1098  case 2:
1099  case 3:
1100  $this->shuffle = $shuffle;
1101  break;
1102  default:
1103  $this->shuffle = 1;
1104  break;
1105  }
1106  }
1107 
1113  public function getQuestionType()
1114  {
1115  return "assMatchingQuestion";
1116  }
1117 
1123  public function getAdditionalTableName()
1124  {
1125  return "qpl_qst_matching";
1126  }
1127 
1133  public function getAnswerTableName()
1134  {
1135  return array("qpl_a_matching", "qpl_a_mterm");
1136  }
1137 
1142  public function getRTETextWithMediaObjects()
1143  {
1145  }
1146 
1150  public function &getMatchingPairs()
1151  {
1152  return $this->matchingpairs;
1153  }
1154 
1160  public function supportsJavascriptOutput()
1161  {
1162  return TRUE;
1163  }
1164 
1177  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
1178  {
1179  include_once ("./Services/Excel/classes/class.ilExcelUtils.php");
1180  $solutions = $this->getSolutionValues($active_id, $pass);
1181  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
1182  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
1183  $imagepath = $this->getImagePath();
1184  $i = 1;
1185  foreach ($solutions as $solution)
1186  {
1187  $matches_written = FALSE;
1188  foreach ($this->getMatchingPairs() as $idx => $pair)
1189  {
1190  if (!$matches_written) $worksheet->writeString($startrow + $i, 1, ilExcelUtils::_convert_text($this->lng->txt("matches")));
1191  $matches_written = TRUE;
1192  if ($pair->definition->identifier == $solution["value2"])
1193  {
1194  if (strlen($pair->definition->text))
1195  {
1196  $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($pair->definition->text));
1197  }
1198  else
1199  {
1200  $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($pair->definition->picture));
1201  }
1202  }
1203  if ($pair->term->identifier == $solution["value1"])
1204  {
1205  if (strlen($pair->term->text))
1206  {
1207  $worksheet->writeString($startrow + $i, 2, ilExcelUtils::_convert_text($pair->term->text));
1208  }
1209  else
1210  {
1211  $worksheet->writeString($startrow + $i, 2, ilExcelUtils::_convert_text($pair->term->picture));
1212  }
1213  }
1214  }
1215  $i++;
1216  }
1217  return $startrow + $i + 1;
1218  }
1219 
1225  public function getThumbGeometry()
1226  {
1227  return $this->thumb_geometry;
1228  }
1229 
1235  public function getThumbSize()
1236  {
1237  return $this->getThumbGeometry();
1238  }
1239 
1245  public function setThumbGeometry($a_geometry)
1246  {
1247  $this->thumb_geometry = ($a_geometry < 1) ? 100 : $a_geometry;
1248  }
1249 
1255  public function getElementHeight()
1256  {
1257  return $this->element_height;
1258  }
1259 
1265  public function setElementHeight($a_height)
1266  {
1267  $this->element_height = ($a_height < 20) ? "" : $a_height;
1268  }
1269 
1273  public function rebuildThumbnails()
1274  {
1275  foreach ($this->terms as $term)
1276  {
1277  if (strlen($term->picture)) $this->generateThumbForFile($this->getImagePath(), $term->picture);
1278  }
1279  foreach ($this->definitions as $definition)
1280  {
1281  if (strlen($definition->picture)) $this->generateThumbForFile($this->getImagePath(), $definition->picture);
1282  }
1283  }
1284 
1285  public function getThumbPrefix()
1286  {
1287  return "thumb.";
1288  }
1289 
1290  protected function generateThumbForFile($path, $file)
1291  {
1292  $filename = $path . $file;
1293  if (@file_exists($filename))
1294  {
1295  $thumbpath = $path . $this->getThumbPrefix() . $file;
1296  $path_info = @pathinfo($filename);
1297  $ext = "";
1298  switch (strtoupper($path_info['extension']))
1299  {
1300  case 'PNG':
1301  $ext = 'PNG';
1302  break;
1303  case 'GIF':
1304  $ext = 'GIF';
1305  break;
1306  default:
1307  $ext = 'JPEG';
1308  break;
1309  }
1310  ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbGeometry());
1311  }
1312  }
1313 
1314  public function getEstimatedElementHeight()
1315  {
1316  $hasImages = false;
1317  foreach ($this->terms as $term)
1318  {
1319  if (strlen($term->picture))
1320  {
1321  $hasImages = true;
1322  }
1323  }
1324  foreach ($this->definitions as $definition)
1325  {
1326  if (strlen($definition->picture))
1327  {
1328  $hasImages = true;
1329  }
1330  }
1331  if ($hasImages)
1332  { // 40 is approx. the height of the preview image
1333  return max($this->getElementHeight(), $this->getThumbSize() + 40);
1334  }
1335  else
1336  {
1337  return ($this->getElementHeight()) ? $this->getElementHeight() : 0;
1338  }
1339  }
1340 
1345  public function toJSON()
1346  {
1347  include_once("./Services/RTE/classes/class.ilRTE.php");
1348  $result = array();
1349  $result['id'] = (int) $this->getId();
1350  $result['type'] = (string) $this->getQuestionType();
1351  $result['title'] = (string) $this->getTitle();
1352  $result['question'] = $this->formatSAQuestion($this->getQuestion());
1353  $result['nr_of_tries'] = (int) $this->getNrOfTries();
1354  $result['shuffle'] = true;
1355  $result['feedback'] = array(
1356  "onenotcorrect" => nl2br(ilRTE::_replaceMediaObjectImageSrc($this->getFeedbackGeneric(0), 0)),
1357  "allcorrect" => nl2br(ilRTE::_replaceMediaObjectImageSrc($this->getFeedbackGeneric(1), 0))
1358  );
1359 
1360  $terms = array("" => array("id"=>"-1",
1361  "term"=>$this->lng->txt("please_select")));
1362  foreach ($this->getTerms() as $term)
1363  {
1364  $terms[(int)$term->identifier] = array(
1365  "term" => $term->text,
1366  "id" =>(int)$term->identifier
1367  );
1368  }
1369  // $terms = $this->pcArrayShuffle($terms);
1370 
1371  // alex 9.9.2010 as a fix for bug 6513 I added the question id
1372  // to the "def_id" in the array. The $pair->definition->identifier is not
1373  // unique, since it gets it value from the morder table field
1374  // this value is not changed, when a question is copied.
1375  // thus copying the same question on a page results in problems
1376  // when the second one (the copy) is answered.
1377 
1378  $pairs = array();
1379  foreach ($this->getDefinitions() as $def)
1380  {
1381  array_push($pairs, array(
1382  "definition" => (string) $def->text,
1383  "def_id" => (int) $this->getId().$def->identifier,
1384  "terms" => $terms
1385  ));
1386  }
1387  $result['pairs'] = $pairs;
1388 
1389  // #10353
1390  $match = $points = array();
1391  foreach ($this->getMatchingPairs() as $pair)
1392  {
1393  $pid = $pair->definition->identifier;
1394 
1395  // we only need pairs with max. points for def-id
1396  if(!isset($match[$pid]) || $match[$pid]["points"] < $pair->points)
1397  {
1398  $match[$pid] = array(
1399  "term_id" => (int) $pair->term->identifier,
1400  "def_id" => (int) $this->getId().$pair->definition->identifier,
1401  "points" => (int) $pair->points
1402  );
1403  }
1404  }
1405  $result['match'] = array_values($match);
1406 
1407  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1408  $result['mobs'] = $mobs;
1409 
1410  return json_encode($result);
1411  }
1412 
1420  function saveFeedbackSingleAnswer($answer_index, $feedback)
1421  {
1422  global $ilDB;
1423 
1424  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_fb_matching WHERE question_fi = %s AND answer = %s",
1425  array('integer','integer'),
1426  array($this->getId(), $answer_index)
1427  );
1428  if (strlen($feedback))
1429  {
1430  include_once("./Services/RTE/classes/class.ilRTE.php");
1431  $next_id = $ilDB->nextId('qpl_fb_matching');
1433  $ilDB->insert('qpl_fb_matching', array(
1434  'feedback_id' => array( 'integer', $next_id ),
1435  'question_fi' => array( 'integer', $this->getId() ),
1436  'answer' => array( 'integer', $answer_index ),
1437  'feedback' => array( 'clob', ilRTE::_replaceMediaObjectImageSrc($feedback, 0) ),
1438  'tstamp' => array( 'integer', time() ),
1439  )
1440  );
1441  }
1442  }
1443 
1451  function getFeedbackSingleAnswer($answer_index)
1452  {
1453  global $ilDB;
1454 
1455  $feedback = "";
1456  $result = $ilDB->queryF("SELECT * FROM qpl_fb_matching WHERE question_fi = %s AND answer = %s",
1457  array('integer','integer'),
1458  array($this->getId(), $answer_index)
1459  );
1460  if ($result->numRows())
1461  {
1462  $row = $ilDB->fetchAssoc($result);
1463  include_once("./Services/RTE/classes/class.ilRTE.php");
1464  $feedback = ilRTE::_replaceMediaObjectImageSrc($row["feedback"], 1);
1465  }
1466  return $feedback;
1467  }
1468 
1469  protected function deleteFeedbackSpecific($question_id)
1470  {
1471  global $ilDB;
1472  $ilDB->manipulateF(
1473  'DELETE
1474  FROM qpl_fb_matching
1475  WHERE question_fi = %s',
1476  array('integer'),
1477  array($question_id)
1478  );
1479  }
1480 
1487  function duplicateSpecificFeedback($original_id)
1488  {
1489  global $ilDB;
1490 
1491  $feedback = "";
1492  $result = $ilDB->queryF("SELECT * FROM qpl_fb_matching WHERE question_fi = %s",
1493  array('integer'),
1494  array($original_id)
1495  );
1496  if ($result->numRows())
1497  {
1498  while ($row = $ilDB->fetchAssoc($result))
1499  {
1500  $next_id = $ilDB->nextId('qpl_fb_matching');
1502  $ilDB->insert('qpl_fb_matching', array(
1503  'feedback_id' => array( 'integer', $next_id ),
1504  'question_fi' => array( 'integer', $this->getId() ),
1505  'answer' => array( 'integer', $row["answer"] ),
1506  'feedback' => array( 'clob', $row["feedback"] ),
1507  'tstamp' => array( 'integer', time() ),
1508  )
1509  );
1510  }
1511  }
1512  }
1513 }