ILIAS  Release_4_4_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.assMatchingQuestion.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 {
32 
42 
48  protected $terms;
49  protected $definitions;
50 
56  var $thumb_geometry = 100;
57 
64 
79  public function __construct(
80  $title = "",
81  $comment = "",
82  $author = "",
83  $owner = -1,
84  $question = "",
86  )
87  {
89  $this->matchingpairs = array();
90  $this->matching_type = $matching_type;
91  $this->terms = array();
92  $this->definitions = array();
93  }
94 
100  public function isComplete()
101  {
102  if (strlen($this->title)
103  && $this->author
104  && $this->question
105  && count($this->matchingpairs)
106  && $this->getMaximumPoints() > 0
107  )
108  {
109  return true;
110  }
111  return false;
112  }
113 
120  public function saveToDb($original_id = "")
121  {
122  global $ilDB;
123 
126  $this->saveAnswerSpecificDataToDb( $ilDB );
127 
128 
130  }
131 
132  public function saveAnswerSpecificDataToDb()
133  {
134  global $ilDB;
135  // delete old terms
136  $ilDB->manipulateF( "DELETE FROM qpl_a_mterm WHERE question_fi = %s",
137  array( 'integer' ),
138  array( $this->getId() )
139  );
140 
141  // delete old definitions
142  $ilDB->manipulateF( "DELETE FROM qpl_a_mdef WHERE question_fi = %s",
143  array( 'integer' ),
144  array( $this->getId() )
145  );
146 
147  $termids = array();
148  // write terms
149  foreach ($this->terms as $key => $term)
150  {
151  $next_id = $ilDB->nextId( 'qpl_a_mterm' );
152  $ilDB->manipulateF( "INSERT INTO qpl_a_mterm (term_id, question_fi, picture, term) VALUES (%s, %s, %s, %s)",
153  array( 'integer', 'integer', 'text', 'text' ),
154  array( $next_id, $this->getId(), $term->picture, $term->text )
155  );
156  $termids[$term->identifier] = $next_id;
157  }
158 
159  $definitionids = array();
160  // write definitions
161  foreach ($this->definitions as $key => $definition)
162  {
163  $next_id = $ilDB->nextId( 'qpl_a_mdef' );
164  $ilDB->manipulateF( "INSERT INTO qpl_a_mdef (def_id, question_fi, picture, definition, morder) VALUES (%s, %s, %s, %s, %s)",
165  array( 'integer', 'integer', 'text', 'text', 'integer' ),
166  array( $next_id, $this->getId(
167  ), $definition->picture, $definition->text, $definition->identifier )
168  );
169  $definitionids[$definition->identifier] = $next_id;
170  }
171 
172  $ilDB->manipulateF( "DELETE FROM qpl_a_matching WHERE question_fi = %s",
173  array( 'integer' ),
174  array( $this->getId() )
175  );
176  $matchingpairs = $this->getMatchingPairs();
177  foreach ($matchingpairs as $key => $pair)
178  {
179  $next_id = $ilDB->nextId( 'qpl_a_matching' );
180  $ilDB->manipulateF( "INSERT INTO qpl_a_matching (answer_id, question_fi, points, term_fi, definition_fi) VALUES (%s, %s, %s, %s, %s)",
181  array( 'integer', 'integer', 'float', 'integer', 'integer' ),
182  array(
183  $next_id,
184  $this->getId(),
185  $pair->points,
186  $termids[$pair->term->identifier],
187  $definitionids[$pair->definition->identifier]
188  )
189  );
190  }
191 
192  $this->rebuildThumbnails();
193  }
194 
196  {
197  global $ilDB;
198  // save additional data
199  $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
200  array( "integer" ),
201  array( $this->getId() )
202  );
203  $ilDB->manipulateF( "INSERT INTO " . $this->getAdditionalTableName(
204  ) . " (question_fi, shuffle, matching_type, thumb_geometry, element_height) VALUES (%s, %s, %s, %s, %s)",
205  array( "integer", "text", "text", "integer", "integer" ),
206  array(
207  $this->getId(),
208  $this->shuffle,
209  $this->matching_type,
210  $this->getThumbGeometry(),
211  ($this->getElementHeight() >= 20) ? $this->getElementHeight() : NULL
212  )
213  );
214  }
215 
222  public function loadFromDb($question_id)
223  {
224  global $ilDB;
225 
226  $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",
227  array("integer"),
228  array($question_id)
229  );
230  if ($result->numRows() == 1)
231  {
232  $data = $ilDB->fetchAssoc($result);
233  $this->setId($question_id);
234  $this->setObjId($data["obj_fi"]);
235  $this->setTitle($data["title"]);
236  $this->setComment($data["description"]);
237  $this->setOriginalId($data["original_id"]);
238  $this->setNrOfTries($data['nr_of_tries']);
239  $this->setAuthor($data["author"]);
240  $this->setPoints($data["points"]);
241  $this->setOwner($data["owner"]);
242  include_once("./Services/RTE/classes/class.ilRTE.php");
243  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
244  $this->setThumbGeometry($data["thumb_geometry"]);
245  $this->setElementHeight($data["element_height"]);
246  $this->setShuffle($data["shuffle"]);
247  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
248 
249  try
250  {
251  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
252  }
254  {
255  }
256  }
257 
258  $termids = array();
259  $result = $ilDB->queryF("SELECT * FROM qpl_a_mterm WHERE question_fi = %s ORDER BY term_id ASC",
260  array('integer'),
261  array($question_id)
262  );
263  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingTerm.php";
264  $this->terms = array();
265  if ($result->numRows() > 0)
266  {
267  while ($data = $ilDB->fetchAssoc($result))
268  {
269  $term = new assAnswerMatchingTerm($data['term'], $data['picture'], $data['term_id']);
270  array_push($this->terms, $term);
271  $termids[$data['term_id']] = $term;
272  }
273  }
274 
275  $definitionids = array();
276  $result = $ilDB->queryF("SELECT * FROM qpl_a_mdef WHERE question_fi = %s ORDER BY def_id ASC",
277  array('integer'),
278  array($question_id)
279  );
280  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingDefinition.php";
281  $this->definitions = array();
282  if ($result->numRows() > 0)
283  {
284  while ($data = $ilDB->fetchAssoc($result))
285  {
286  $definition = new assAnswerMatchingDefinition($data['definition'], $data['picture'], $data['morder']);
287  array_push($this->definitions, $definition);
288  $definitionids[$data['def_id']] = $definition;
289  }
290  }
291 
292  $this->matchingpairs = array();
293  $result = $ilDB->queryF("SELECT * FROM qpl_a_matching WHERE question_fi = %s ORDER BY answer_id",
294  array('integer'),
295  array($question_id)
296  );
297  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingPair.php";
298  if ($result->numRows() > 0)
299  {
300  while ($data = $ilDB->fetchAssoc($result))
301  {
302  array_push($this->matchingpairs, new assAnswerMatchingPair($termids[$data['term_fi']], $definitionids[$data['definition_fi']], $data['points']));
303  }
304  }
305  parent::loadFromDb($question_id);
306  }
307 
308 
312  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
313  {
314  if ($this->id <= 0)
315  {
316  // The question has not been saved. It cannot be duplicated
317  return;
318  }
319  // duplicate the question in database
320  $this_id = $this->getId();
321  $thisObjId = $this->getObjId();
322 
323  $clone = $this;
324  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
326  $clone->id = -1;
327 
328  if( (int)$testObjId > 0 )
329  {
330  $clone->setObjId($testObjId);
331  }
332 
333  if ($title)
334  {
335  $clone->setTitle($title);
336  }
337  if ($author)
338  {
339  $clone->setAuthor($author);
340  }
341  if ($owner)
342  {
343  $clone->setOwner($owner);
344  }
345  if ($for_test)
346  {
347  $clone->saveToDb($original_id);
348  }
349  else
350  {
351  $clone->saveToDb();
352  }
353 
354  // copy question page content
355  $clone->copyPageOfQuestion($this_id);
356  // copy XHTML media objects
357  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
358  // duplicate the image
359  $clone->duplicateImages($this_id, $thisObjId);
360 
361  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
362 
363  return $clone->id;
364  }
365 
369  public function copyObject($target_questionpool_id, $title = "")
370  {
371  if ($this->id <= 0)
372  {
373  // The question has not been saved. It cannot be duplicated
374  return;
375  }
376  // duplicate the question in database
377  $clone = $this;
378  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
380  $clone->id = -1;
381  $source_questionpool_id = $this->getObjId();
382  $clone->setObjId($target_questionpool_id);
383  if ($title)
384  {
385  $clone->setTitle($title);
386  }
387  $clone->saveToDb();
388  // copy question page content
389  $clone->copyPageOfQuestion($original_id);
390  // copy XHTML media objects
391  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
392  // duplicate the image
393  $clone->copyImages($original_id, $source_questionpool_id);
394 
395  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
396 
397  return $clone->id;
398  }
399 
400  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
401  {
402  if ($this->id <= 0)
403  {
404  // The question has not been saved. It cannot be duplicated
405  return;
406  }
407 
408  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
409 
410  $sourceQuestionId = $this->id;
411  $sourceParentId = $this->getObjId();
412 
413  // duplicate the question in database
414  $clone = $this;
415  $clone->id = -1;
416 
417  $clone->setObjId($targetParentId);
418 
419  if ($targetQuestionTitle)
420  {
421  $clone->setTitle($targetQuestionTitle);
422  }
423 
424  $clone->saveToDb();
425  // copy question page content
426  $clone->copyPageOfQuestion($sourceQuestionId);
427  // copy XHTML media objects
428  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
429  // duplicate the image
430  $clone->copyImages($sourceQuestionId, $sourceParentId);
431 
432  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
433 
434  return $clone->id;
435  }
436 
437  public function duplicateImages($question_id, $objectId = null)
438  {
439  global $ilLog;
440  $imagepath = $this->getImagePath();
441  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
442 
443  if( (int)$objectId > 0 )
444  {
445  $imagepath_original = str_replace("/$this->obj_id/", "/$objectId/", $imagepath_original);
446  }
447 
448  foreach ($this->terms as $term)
449  {
450  if (strlen($term->picture))
451  {
452  $filename = $term->picture;
453  if (!file_exists($imagepath))
454  {
455  ilUtil::makeDirParents($imagepath);
456  }
457  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
458  {
459  $ilLog->write("matching question image could not be duplicated: $imagepath_original$filename");
460  }
461  if (@file_exists($imagepath_original . $this->getThumbPrefix() . $filename))
462  {
463  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
464  {
465  $ilLog->write("matching question image thumbnail could not be duplicated: $imagepath_original" . $this->getThumbPrefix() . $filename);
466  }
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 duplicated: $imagepath_original$filename");
482  }
483  if (@file_exists($imagepath_original . $this->getThumbPrefix() . $filename))
484  {
485  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
486  {
487  $ilLog->write("matching question image thumbnail could not be duplicated: $imagepath_original" . $this->getThumbPrefix() . $filename);
488  }
489  }
490  }
491  }
492  }
493 
494  public function copyImages($question_id, $source_questionpool)
495  {
496  global $ilLog;
497 
498  $imagepath = $this->getImagePath();
499  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
500  $imagepath_original = str_replace("/$this->obj_id/", "/$source_questionpool/", $imagepath_original);
501  foreach ($this->terms as $term)
502  {
503  if (strlen($term->picture))
504  {
505  if (!file_exists($imagepath))
506  {
507  ilUtil::makeDirParents($imagepath);
508  }
509  $filename = $term->picture;
510  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
511  {
512  $ilLog->write("matching question image could not be copied: $imagepath_original$filename");
513  }
514  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
515  {
516  $ilLog->write("matching question image thumbnail could not be copied: $imagepath_original" . $this->getThumbPrefix() . $filename);
517  }
518  }
519  }
520  foreach ($this->definitions as $definition)
521  {
522  if (strlen($definition->picture))
523  {
524  $filename = $definition->picture;
525  if (!file_exists($imagepath))
526  {
527  ilUtil::makeDirParents($imagepath);
528  }
529  if (!copy($imagepath_original . $filename, $imagepath . $filename))
530  {
531  $ilLog->write("matching question image could not be copied: $imagepath_original$filename");
532  }
533  if (!copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
534  {
535  $ilLog->write("matching question image thumbnail could not be copied: $imagepath_original" . $this->getThumbPrefix() . $filename);
536  }
537  }
538  }
539  }
540 
551  public function insertMatchingPair($position, $term = null, $definition = null, $points = 0.0)
552  {
553  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingPair.php";
554  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingTerm.php";
555  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingDefinition.php";
556  if (is_null($term)) $term = new assAnswerMatchingTerm();
557  if (is_null($definition)) $definition = new assAnswerMatchingDefinition();
558  $pair = new assAnswerMatchingPair($term, $definition, $points);
559  if ($position < count($this->matchingpairs))
560  {
561  $part1 = array_slice($this->matchingpairs, 0, $position);
562  $part2 = array_slice($this->matchingpairs, $position);
563  $this->matchingpairs = array_merge($part1, array($pair), $part2);
564  }
565  else
566  {
567  array_push($this->matchingpairs, $pair);
568  }
569  }
570 
582  public function addMatchingPair($term = null, $definition = null, $points = 0.0)
583  {
584  require_once './Modules/TestQuestionPool/classes/class.assAnswerMatchingPair.php';
585  require_once './Modules/TestQuestionPool/classes/class.assAnswerMatchingTerm.php';
586  require_once './Modules/TestQuestionPool/classes/class.assAnswerMatchingDefinition.php';
587  if (is_null($term))
588  {
589  $term = new assAnswerMatchingTerm();
590  }
591  if (is_null($definition))
592  {
593  $definition = new assAnswerMatchingDefinition();
594  }
595  $pair = new assAnswerMatchingPair($term, $definition, $points);
596  array_push($this->matchingpairs, $pair);
597  }
598 
602  public function getTermWithIdentifier($a_identifier)
603  {
604  foreach ($this->terms as $term)
605  {
606  if ($term->identifier == $a_identifier) return $term;
607  }
608  return null;
609  }
610 
614  public function getDefinitionWithIdentifier($a_identifier)
615  {
616  foreach ($this->definitions as $definition)
617  {
618  if ($definition->identifier == $a_identifier) return $definition;
619  }
620  return null;
621  }
622 
631  public function getMatchingPair($index = 0)
632  {
633  if ($index < 0)
634  {
635  return NULL;
636  }
637  if (count($this->matchingpairs) < 1)
638  {
639  return NULL;
640  }
641  if ($index >= count($this->matchingpairs))
642  {
643  return NULL;
644  }
645  return $this->matchingpairs[$index];
646  }
647 
655  public function deleteMatchingPair($index = 0)
656  {
657  if ($index < 0)
658  {
659  return;
660  }
661  if (count($this->matchingpairs) < 1)
662  {
663  return;
664  }
665  if ($index >= count($this->matchingpairs))
666  {
667  return;
668  }
669  unset($this->matchingpairs[$index]);
670  $this->matchingpairs = array_values($this->matchingpairs);
671  }
672 
677  public function flushMatchingPairs()
678  {
679  $this->matchingpairs = array();
680  }
681 
688  public function getMatchingPairCount()
689  {
690  return count($this->matchingpairs);
691  }
692 
699  public function getTerms()
700  {
701  return $this->terms;
702  }
703 
710  public function getDefinitions()
711  {
712  return $this->definitions;
713  }
714 
721  public function getTermCount()
722  {
723  return count($this->terms);
724  }
725 
732  public function getDefinitionCount()
733  {
734  return count($this->definitions);
735  }
736 
743  public function addTerm($term)
744  {
745  array_push($this->terms, $term);
746  }
747 
754  public function addDefinition($definition)
755  {
756  array_push($this->definitions, $definition);
757  }
758 
765  public function insertTerm($position, $term = null)
766  {
767  if (is_null($term))
768  {
769  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingTerm.php";
770  $term = new assAnswerMatchingTerm();
771  }
772  if ($position < count($this->terms))
773  {
774  $part1 = array_slice($this->terms, 0, $position);
775  $part2 = array_slice($this->terms, $position);
776  $this->terms = array_merge($part1, array($term), $part2);
777  }
778  else
779  {
780  array_push($this->terms, $term);
781  }
782  }
783 
790  public function insertDefinition($position, $definition = null)
791  {
792  if (is_null($definition))
793  {
794  include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingDefinition.php";
795  $definition = new assAnswerMatchingDefinition();
796  }
797  if ($position < count($this->definitions))
798  {
799  $part1 = array_slice($this->definitions, 0, $position);
800  $part2 = array_slice($this->definitions, $position);
801  $this->definitions = array_merge($part1, array($definition), $part2);
802  }
803  else
804  {
805  array_push($this->definitions, $definition);
806  }
807  }
808 
813  public function flushTerms()
814  {
815  $this->terms = array();
816  }
817 
822  public function flushDefinitions()
823  {
824  $this->definitions = array();
825  }
826 
833  public function deleteTerm($position)
834  {
835  unset($this->terms[$position]);
836  $this->terms = array_values($this->terms);
837  }
838 
845  public function deleteDefinition($position)
846  {
847  unset($this->definitions[$position]);
848  $this->definitions = array_values($this->definitions);
849  }
850 
858  public function setTerm($term, $index)
859  {
860  $this->terms[$index] = $term;
861  }
862 
873  public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE)
874  {
875  if( $returndetails )
876  {
877  throw new ilTestException('return details not implemented for '.__METHOD__);
878  }
879 
880  global $ilDB;
881 
882  $found_value1 = array();
883  $found_value2 = array();
884  if (is_null($pass))
885  {
886  $pass = $this->getSolutionMaxPass($active_id);
887  }
888  $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
889  array('integer','integer','integer'),
890  array($active_id, $this->getId(), $pass)
891  );
892  while ($data = $ilDB->fetchAssoc($result))
893  {
894  if (strcmp($data["value1"], "") != 0)
895  {
896  array_push($found_value1, $data["value1"]);
897  array_push($found_value2, $data["value2"]);
898  }
899  }
900  $points = 0;
901  foreach ($found_value2 as $key => $value)
902  {
903  foreach ($this->matchingpairs as $pair)
904  {
905  if (($pair->definition->identifier == $value) && ($pair->term->identifier == $found_value1[$key]))
906  {
907  $points += $pair->points;
908  }
909  }
910  }
911 
912  return $points;
913  }
914 
918  function getMaximumPoints()
919  {
920  $points = 0;
921  foreach ($this->matchingpairs as $key => $pair)
922  {
923  if ($pair->points > 0)
924  {
925  $points += $pair->points;
926  }
927  }
928  return $points;
929  }
930 
940  {
941  $extension = "";
942  if (preg_match("/.*\\.(\\w+)$/", $filename, $matches))
943  {
944  $extension = $matches[1];
945  }
946  return md5($filename) . "." . $extension;
947  }
948 
949  public function removeTermImage($index)
950  {
951  $term = $this->terms[$index];
952  if (is_object($term))
953  {
954  $this->deleteImagefile($term->picture);
955  $term->picture = null;
956  }
957  }
958 
959  public function removeDefinitionImage($index)
960  {
961  $definition = $this->definitions[$index];
962  if (is_object($definition))
963  {
964  $this->deleteImagefile($definition->picture);
965  $definition->picture = null;
966  }
967  }
968 
969 
976  public function deleteImagefile($filename)
977  {
978  $deletename = $filename;
979  $result = @unlink($this->getImagePath().$deletename);
980  $result = $result & @unlink($this->getImagePath().$this->getThumbPrefix() . $deletename);
981  return $result;
982  }
983 
992  function setImageFile($image_tempfilename, $image_filename, $previous_filename = '')
993  {
994  $result = TRUE;
995  if (strlen($image_tempfilename))
996  {
997  $image_filename = str_replace(" ", "_", $image_filename);
998  $imagepath = $this->getImagePath();
999  if (!file_exists($imagepath))
1000  {
1001  ilUtil::makeDirParents($imagepath);
1002  }
1003  $savename = $image_filename;
1004  if (!ilUtil::moveUploadedFile($image_tempfilename, $savename, $imagepath.$savename))
1005  {
1006  $result = FALSE;
1007  }
1008  else
1009  {
1010  // create thumbnail file
1011  $thumbpath = $imagepath . $this->getThumbPrefix() . $savename;
1012  ilUtil::convertImage($imagepath.$savename, $thumbpath, "JPEG", $this->getThumbGeometry());
1013  }
1014  if ($result && (strcmp($image_filename, $previous_filename) != 0) && (strlen($previous_filename)))
1015  {
1016  $this->deleteImagefile($previous_filename);
1017  }
1018  }
1019  return $result;
1020  }
1021 
1028  function checkSaveData()
1029  {
1030  $result = true;
1031  $matching_values = array();
1032  foreach ($_POST['matching'][$this->getId()] as $definition => $term)
1033  {
1034  if ($term > 0)
1035  {
1036  array_push($matching_values, $term);
1037  }
1038  }
1039 
1040  $check_matching = array_flip($matching_values);
1041  if (count($check_matching) != count($matching_values))
1042  {
1043  $result = false;
1044  ilUtil::sendFailure($this->lng->txt("duplicate_matching_values_selected"), true);
1045  }
1046  return $result;
1047  }
1048 
1057  public function saveWorkingData($active_id, $pass = NULL)
1058  {
1059  global $ilDB;
1060  global $ilUser;
1061  $saveWorkingDataResult = $this->checkSaveData();
1062  $entered_values = 0;
1063  if ($saveWorkingDataResult)
1064  {
1065  if (is_null($pass))
1066  {
1067  include_once "./Modules/Test/classes/class.ilObjTest.php";
1068  $pass = ilObjTest::_getPass($active_id);
1069  }
1070 
1071  $this->getProcessLocker()->requestUserSolutionUpdateLock();
1072 
1073  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
1074  array('integer','integer','integer'),
1075  array($active_id, $this->getId(), $pass)
1076  );
1077 
1078  foreach ($_POST['matching'][$this->getId()] as $definition => $term)
1079  {
1080  $entered_values++;
1081  $next_id = $ilDB->nextId('tst_solutions');
1082  $affectedRows = $ilDB->insert("tst_solutions", array(
1083  "solution_id" => array("integer", $next_id),
1084  "active_fi" => array("integer", $active_id),
1085  "question_fi" => array("integer", $this->getId()),
1086  "value1" => array("clob", $term),
1087  "value2" => array("clob", $definition),
1088  "pass" => array("integer", $pass),
1089  "tstamp" => array("integer", time())
1090  ));
1091  }
1092 
1093  $this->getProcessLocker()->releaseUserSolutionUpdateLock();
1094 
1095  $saveWorkingDataResult = true;
1096  }
1097  if ($entered_values)
1098  {
1099  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1101  {
1102  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
1103  }
1104  }
1105  else
1106  {
1107  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1109  {
1110  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
1111  }
1112  }
1113 
1114  return $saveWorkingDataResult;
1115  }
1116 
1125  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
1126  {
1127  // nothing to rework!
1128  }
1129 
1130  public function getRandomId()
1131  {
1132  mt_srand((double)microtime()*1000000);
1133  $random_number = mt_rand(1, 100000);
1134  $found = FALSE;
1135  while ($found)
1136  {
1137  $found = FALSE;
1138  foreach ($this->matchingpairs as $key => $pair)
1139  {
1140  if (($pair->term->identifier == $random_number) || ($pair->definition->identifier == $random_number))
1141  {
1142  $found = TRUE;
1143  $random_number++;
1144  }
1145  }
1146  }
1147  return $random_number;
1148  }
1149 
1156  public function setShuffle($shuffle)
1157  {
1158  switch ($shuffle)
1159  {
1160  case 0:
1161  case 1:
1162  case 2:
1163  case 3:
1164  $this->shuffle = $shuffle;
1165  break;
1166  default:
1167  $this->shuffle = 1;
1168  break;
1169  }
1170  }
1171 
1177  public function getQuestionType()
1178  {
1179  return "assMatchingQuestion";
1180  }
1181 
1187  public function getAdditionalTableName()
1188  {
1189  return "qpl_qst_matching";
1190  }
1191 
1197  public function getAnswerTableName()
1198  {
1199  return array("qpl_a_matching", "qpl_a_mterm");
1200  }
1201 
1206  public function getRTETextWithMediaObjects()
1207  {
1209  }
1210 
1214  public function &getMatchingPairs()
1215  {
1216  return $this->matchingpairs;
1217  }
1218 
1224  public function supportsJavascriptOutput()
1225  {
1226  return TRUE;
1227  }
1228 
1241  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
1242  {
1243  include_once ("./Services/Excel/classes/class.ilExcelUtils.php");
1244  $solutions = $this->getSolutionValues($active_id, $pass);
1245  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
1246  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
1247  $imagepath = $this->getImagePath();
1248  $i = 1;
1249  foreach ($solutions as $solution)
1250  {
1251  $matches_written = FALSE;
1252  foreach ($this->getMatchingPairs() as $idx => $pair)
1253  {
1254  if (!$matches_written) $worksheet->writeString($startrow + $i, 1, ilExcelUtils::_convert_text($this->lng->txt("matches")));
1255  $matches_written = TRUE;
1256  if ($pair->definition->identifier == $solution["value2"])
1257  {
1258  if (strlen($pair->definition->text))
1259  {
1260  $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($pair->definition->text));
1261  }
1262  else
1263  {
1264  $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($pair->definition->picture));
1265  }
1266  }
1267  if ($pair->term->identifier == $solution["value1"])
1268  {
1269  if (strlen($pair->term->text))
1270  {
1271  $worksheet->writeString($startrow + $i, 2, ilExcelUtils::_convert_text($pair->term->text));
1272  }
1273  else
1274  {
1275  $worksheet->writeString($startrow + $i, 2, ilExcelUtils::_convert_text($pair->term->picture));
1276  }
1277  }
1278  }
1279  $i++;
1280  }
1281  return $startrow + $i + 1;
1282  }
1283 
1289  public function getThumbGeometry()
1290  {
1291  return $this->thumb_geometry;
1292  }
1293 
1299  public function getThumbSize()
1300  {
1301  return $this->getThumbGeometry();
1302  }
1303 
1309  public function setThumbGeometry($a_geometry)
1310  {
1311  $this->thumb_geometry = ($a_geometry < 1) ? 100 : $a_geometry;
1312  }
1313 
1319  public function getElementHeight()
1320  {
1321  return $this->element_height;
1322  }
1323 
1329  public function setElementHeight($a_height)
1330  {
1331  $this->element_height = ($a_height < 20) ? "" : $a_height;
1332  }
1333 
1337  public function rebuildThumbnails()
1338  {
1339  foreach ($this->terms as $term)
1340  {
1341  if (strlen($term->picture)) $this->generateThumbForFile($this->getImagePath(), $term->picture);
1342  }
1343  foreach ($this->definitions as $definition)
1344  {
1345  if (strlen($definition->picture)) $this->generateThumbForFile($this->getImagePath(), $definition->picture);
1346  }
1347  }
1348 
1349  public function getThumbPrefix()
1350  {
1351  return "thumb.";
1352  }
1353 
1354  protected function generateThumbForFile($path, $file)
1355  {
1356  $filename = $path . $file;
1357  if (@file_exists($filename))
1358  {
1359  $thumbpath = $path . $this->getThumbPrefix() . $file;
1360  $path_info = @pathinfo($filename);
1361  $ext = "";
1362  switch (strtoupper($path_info['extension']))
1363  {
1364  case 'PNG':
1365  $ext = 'PNG';
1366  break;
1367  case 'GIF':
1368  $ext = 'GIF';
1369  break;
1370  default:
1371  $ext = 'JPEG';
1372  break;
1373  }
1374  ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbGeometry());
1375  }
1376  }
1377 
1378  public function getEstimatedElementHeight()
1379  {
1380  $hasImages = false;
1381  foreach ($this->terms as $term)
1382  {
1383  if (strlen($term->picture))
1384  {
1385  $hasImages = true;
1386  }
1387  }
1388  foreach ($this->definitions as $definition)
1389  {
1390  if (strlen($definition->picture))
1391  {
1392  $hasImages = true;
1393  }
1394  }
1395  if ($hasImages)
1396  { // 40 is approx. the height of the preview image
1397  return max($this->getElementHeight(), $this->getThumbSize() + 40);
1398  }
1399  else
1400  {
1401  return ($this->getElementHeight()) ? $this->getElementHeight() : 0;
1402  }
1403  }
1404 
1409  public function toJSON()
1410  {
1411  include_once("./Services/RTE/classes/class.ilRTE.php");
1412  $result = array();
1413  $result['id'] = (int) $this->getId();
1414  $result['type'] = (string) $this->getQuestionType();
1415  $result['title'] = (string) $this->getTitle();
1416  $result['question'] = $this->formatSAQuestion($this->getQuestion());
1417  $result['nr_of_tries'] = (int) $this->getNrOfTries();
1418  $result['shuffle'] = true;
1419  $result['feedback'] = array(
1420  "onenotcorrect" => $this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false),
1421  "allcorrect" => $this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true)
1422  );
1423 
1424  $terms = array("" => array("id"=>"-1",
1425  "term"=>$this->lng->txt("please_select")));
1426  foreach ($this->getTerms() as $term)
1427  {
1428  $terms[(int)$term->identifier] = array(
1429  "term" => $term->text,
1430  "id" =>(int)$term->identifier
1431  );
1432  }
1433  // $terms = $this->pcArrayShuffle($terms);
1434 
1435  // alex 9.9.2010 as a fix for bug 6513 I added the question id
1436  // to the "def_id" in the array. The $pair->definition->identifier is not
1437  // unique, since it gets it value from the morder table field
1438  // this value is not changed, when a question is copied.
1439  // thus copying the same question on a page results in problems
1440  // when the second one (the copy) is answered.
1441 
1442  $pairs = array();
1443  foreach ($this->getDefinitions() as $def)
1444  {
1445  array_push($pairs, array(
1446  "definition" => (string) $def->text,
1447  "def_id" => (int) $this->getId().$def->identifier,
1448  "terms" => $terms
1449  ));
1450  }
1451  $result['pairs'] = $pairs;
1452 
1453  // #10353
1454  $match = $points = array();
1455  foreach ($this->getMatchingPairs() as $pair)
1456  {
1457  $pid = $pair->definition->identifier;
1458 
1459  // we only need pairs with max. points for def-id
1460  if(!isset($match[$pid]) || $match[$pid]["points"] < $pair->points)
1461  {
1462  $match[$pid] = array(
1463  "term_id" => (int) $pair->term->identifier,
1464  "def_id" => (int) $this->getId().$pair->definition->identifier,
1465  "points" => (int) $pair->points
1466  );
1467  }
1468  }
1469  $result['match'] = array_values($match);
1470 
1471  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1472  $result['mobs'] = $mobs;
1473 
1474  return json_encode($result);
1475  }
1476 }