ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.assMatchingQuestion.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
21 use ILIAS\Refinery\Random\Group as RandomGroup;
23 
24 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
25 
40 {
41  private int $shufflemode = 0;
42 
60 
66  protected array $terms = [];
67 
68  protected $definitions;
74  public $thumb_geometry = 100;
75 
82 
83  public const MATCHING_MODE_1_ON_1 = '1:1';
84  public const MATCHING_MODE_N_ON_N = 'n:n';
85 
86  protected $matchingMode = self::MATCHING_MODE_1_ON_1;
87 
88  private RandomGroup $randomGroup;
89 
102  public function __construct(
103  $title = "",
104  $comment = "",
105  $author = "",
106  $owner = -1,
107  $question = "",
109  ) {
110  global $DIC;
111 
113  $this->matchingpairs = [];
114  $this->matching_type = $matching_type;
115  $this->terms = [];
116  $this->definitions = [];
117  $this->randomGroup = $DIC->refinery()->random();
118  }
119 
120  public function getShuffleMode(): int
121  {
122  return $this->shufflemode;
123  }
124 
125  public function setShuffleMode(int $shuffle)
126  {
127  $this->shufflemode = $shuffle;
128  }
129 
135  public function isComplete(): bool
136  {
137  if (strlen($this->title)
138  && $this->author
139  && $this->question
140  && count($this->matchingpairs)
141  && $this->getMaximumPoints() > 0
142  ) {
143  return true;
144  }
145  return false;
146  }
147 
154  public function saveToDb($original_id = ""): void
155  {
156  if ($original_id == "") {
157  $this->saveQuestionDataToDb();
158  } else {
160  }
161 
164 
165  parent::saveToDb();
166  }
167 
168  public function saveAnswerSpecificDataToDb()
169  {
170  $this->rebuildThumbnails();
171 
172  $this->db->manipulateF(
173  "DELETE FROM qpl_a_mterm WHERE question_fi = %s",
174  [ 'integer' ],
175  [ $this->getId() ]
176  );
177 
178  // delete old definitions
179  $this->db->manipulateF(
180  "DELETE FROM qpl_a_mdef WHERE question_fi = %s",
181  [ 'integer' ],
182  [ $this->getId() ]
183  );
184 
185  $termids = [];
186  // write terms
187  foreach ($this->terms as $key => $term) {
188  $next_id = $this->db->nextId('qpl_a_mterm');
189  $this->db->insert('qpl_a_mterm', [
190  'term_id' => ['integer', $next_id],
191  'question_fi' => ['integer', $this->getId()],
192  'picture' => ['text', $term->getPicture()],
193  'term' => ['text', $term->getText()],
194  'ident' => ['integer', $term->getIdentifier()]
195  ]);
196  $termids[$term->getIdentifier()] = $next_id;
197  }
198 
199  $definitionids = [];
200  // write definitions
201  foreach ($this->definitions as $key => $definition) {
202  $next_id = $this->db->nextId('qpl_a_mdef');
203  $this->db->insert('qpl_a_mdef', [
204  'def_id' => ['integer', $next_id],
205  'question_fi' => ['integer', $this->getId()],
206  'picture' => ['text', $definition->getPicture()],
207  'definition' => ['text', $definition->getText()],
208  'ident' => ['integer', $definition->getIdentifier()]
209  ]);
210  $definitionids[$definition->getIdentifier()] = $next_id;
211  }
212 
213  $this->db->manipulateF(
214  "DELETE FROM qpl_a_matching WHERE question_fi = %s",
215  [ 'integer' ],
216  [ $this->getId() ]
217  );
218  $matchingpairs = $this->getMatchingPairs();
219  foreach ($matchingpairs as $key => $pair) {
220  $next_id = $this->db->nextId('qpl_a_matching');
221  $this->db->manipulateF(
222  "INSERT INTO qpl_a_matching (answer_id, question_fi, points, term_fi, definition_fi) VALUES (%s, %s, %s, %s, %s)",
223  [ 'integer', 'integer', 'float', 'integer', 'integer' ],
224  [
225  $next_id,
226  $this->getId(),
227  $pair->getPoints(),
228  $termids[$pair->getTerm()->getIdentifier()],
229  $definitionids[$pair->getDefinition()->getIdentifier()]
230  ]
231  );
232  }
233  }
234 
236  {
237  $this->db->manipulateF(
238  "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
239  [ "integer" ],
240  [ $this->getId() ]
241  );
242 
243  $this->db->insert($this->getAdditionalTableName(), [
244  'question_fi' => ['integer', $this->getId()],
245  'shuffle' => ['text', $this->getShuffleMode()],
246  'matching_type' => ['text', $this->matching_type],
247  'thumb_geometry' => ['integer', $this->getThumbGeometry()],
248  'matching_mode' => ['text', $this->getMatchingMode()]
249  ]);
250  }
251 
258  public function loadFromDb($question_id): void
259  {
260  $query = "
261  SELECT qpl_questions.*,
262  {$this->getAdditionalTableName()}.*
263  FROM qpl_questions
264  LEFT JOIN {$this->getAdditionalTableName()}
265  ON {$this->getAdditionalTableName()}.question_fi = qpl_questions.question_id
266  WHERE qpl_questions.question_id = %s
267  ";
268 
269  $result = $this->db->queryF(
270  $query,
271  ['integer'],
272  [$question_id]
273  );
274 
275  if ($result->numRows() == 1) {
276  $data = $this->db->fetchAssoc($result);
277  $this->setId((int) $question_id);
278  $this->setObjId((int) $data["obj_fi"]);
279  $this->setTitle((string) $data["title"]);
280  $this->setComment((string) $data["description"]);
281  $this->setOriginalId((int) $data["original_id"]);
282  $this->setNrOfTries((int) $data['nr_of_tries']);
283  $this->setAuthor($data["author"]);
284  $this->setPoints((float) $data["points"]);
285  $this->setOwner((int) $data["owner"]);
286  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc((string) $data["question_text"], 1));
287  $this->setThumbGeometry((int) $data["thumb_geometry"]);
288  $this->setShuffle($data["shuffle"] != '0');
289  $this->setShuffleMode((int) $data['shuffle']);
290  $this->setMatchingMode($data['matching_mode'] === null ? self::MATCHING_MODE_1_ON_1 : $data['matching_mode']);
291 
292  try {
293  $this->setLifecycle(ilAssQuestionLifecycle::getInstance($data['lifecycle']));
296  }
297 
298  try {
299  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
300  } catch (ilTestQuestionPoolException $e) {
301  }
302  }
303 
304  $termids = [];
305  $result = $this->db->queryF(
306  "SELECT * FROM qpl_a_mterm WHERE question_fi = %s ORDER BY term_id ASC",
307  ['integer'],
308  [$question_id]
309  );
310  $this->terms = [];
311  if ($result->numRows() > 0) {
312  while ($data = $this->db->fetchAssoc($result)) {
313  $term = $this->createMatchingTerm($data['term'] ?? '', $data['picture'] ?? '', (int) $data['ident']);
314  $this->terms[] = $term;
315  $termids[$data['term_id']] = $term;
316  }
317  }
318 
319  $definitionids = [];
320  $result = $this->db->queryF(
321  "SELECT * FROM qpl_a_mdef WHERE question_fi = %s ORDER BY def_id ASC",
322  ['integer'],
323  [$question_id]
324  );
325 
326  $this->definitions = [];
327  if ($result->numRows() > 0) {
328  while ($data = $this->db->fetchAssoc($result)) {
329  $definition = $this->createMatchingDefinition($data['definition'] ?? '', $data['picture'] ?? '', (int) $data['ident']);
330  array_push($this->definitions, $definition);
331  $definitionids[$data['def_id']] = $definition;
332  }
333  }
334 
335  $this->matchingpairs = [];
336  $result = $this->db->queryF(
337  "SELECT * FROM qpl_a_matching WHERE question_fi = %s ORDER BY answer_id",
338  ['integer'],
339  [$question_id]
340  );
341  if ($result->numRows() > 0) {
342  while ($data = $this->db->fetchAssoc($result)) {
343  $pair = $this->createMatchingPair(
344  $termids[$data['term_fi']],
345  $definitionids[$data['definition_fi']],
346  (float) $data['points']
347  );
348  array_push($this->matchingpairs, $pair);
349  }
350  }
351  parent::loadFromDb((int) $question_id);
352  }
353 
354 
358  public function duplicate(bool $for_test = true, string $title = "", string $author = "", int $owner = -1, $testObjId = null): int
359  {
360  if ($this->id <= 0) {
361  // The question has not been saved. It cannot be duplicated
362  return -1;
363  }
364  // duplicate the question in database
365  $this_id = $this->getId();
366  $thisObjId = $this->getObjId();
367 
368  $clone = $this;
369 
370  $original_id = $this->questioninfo->getOriginalId($this->id);
371  $clone->id = -1;
372 
373  if ((int) $testObjId > 0) {
374  $clone->setObjId($testObjId);
375  }
376 
377  if ($title) {
378  $clone->setTitle($title);
379  }
380  if ($author) {
381  $clone->setAuthor($author);
382  }
383  if ($owner) {
384  $clone->setOwner((int) $owner);
385  }
386  if ($for_test) {
387  $clone->saveToDb($original_id);
388  } else {
389  $clone->saveToDb();
390  }
391 
392  // copy question page content
393  $clone->copyPageOfQuestion($this_id);
394  // copy XHTML media objects
395  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
396  // duplicate the image
397  $clone->duplicateImages($this_id, $thisObjId, $clone->getId(), $testObjId);
398 
399  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
400 
401  return $clone->id;
402  }
403 
407  public function copyObject($target_questionpool_id, $title = ""): int
408  {
409  if ($this->getId() <= 0) {
410  throw new RuntimeException('The question has not been saved. It cannot be duplicated');
411  }
412  // duplicate the question in database
413  $clone = $this;
414 
415  $original_id = $this->questioninfo->getOriginalId($this->id);
416  $clone->id = -1;
417  $source_questionpool_id = $this->getObjId();
418  $clone->setObjId($target_questionpool_id);
419  if ($title) {
420  $clone->setTitle($title);
421  }
422  $clone->saveToDb();
423  // copy question page content
424  $clone->copyPageOfQuestion($original_id);
425  // copy XHTML media objects
426  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
427  // duplicate the image
428  $clone->copyImages($original_id, $source_questionpool_id);
429 
430  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
431 
432  return $clone->id;
433  }
434 
435  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = ""): int
436  {
437  if ($this->getId() <= 0) {
438  throw new RuntimeException('The question has not been saved. It cannot be duplicated');
439  }
440 
441  $sourceQuestionId = $this->id;
442  $sourceParentId = $this->getObjId();
443 
444  // duplicate the question in database
445  $clone = $this;
446  $clone->id = -1;
447 
448  $clone->setObjId($targetParentId);
449 
450  if ($targetQuestionTitle) {
451  $clone->setTitle($targetQuestionTitle);
452  }
453 
454  $clone->saveToDb();
455  // copy question page content
456  $clone->copyPageOfQuestion($sourceQuestionId);
457  // copy XHTML media objects
458  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
459  // duplicate the image
460  $clone->copyImages($sourceQuestionId, $sourceParentId);
461 
462  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
463 
464  return $clone->id;
465  }
466 
467  public function duplicateImages($question_id, $objectId = null): void
468  {
469  global $DIC;
470  $ilLog = $DIC['ilLog'];
471  $imagepath = $this->getImagePath();
472  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
473 
474  if ((int) $objectId > 0) {
475  $imagepath_original = str_replace("/$this->obj_id/", "/$objectId/", $imagepath_original);
476  }
477 
478  foreach ($this->terms as $term) {
479  if (strlen($term->getPicture())) {
480  $filename = $term->getPicture();
481  if (!file_exists($imagepath)) {
482  ilFileUtils::makeDirParents($imagepath);
483  }
484  if (!@copy($imagepath_original . $filename, $imagepath . $filename)) {
485  $ilLog->write("matching question image could not be duplicated: $imagepath_original$filename");
486  }
487  if (@file_exists($imagepath_original . $this->getThumbPrefix() . $filename)) {
488  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
489  $ilLog->write("matching question image thumbnail could not be duplicated: $imagepath_original" . $this->getThumbPrefix() . $filename);
490  }
491  }
492  }
493  }
494  foreach ($this->definitions as $definition) {
495  if (strlen($definition->getPicture())) {
496  $filename = $definition->getPicture();
497  if (!file_exists($imagepath)) {
498  ilFileUtils::makeDirParents($imagepath);
499  }
500  if (!@copy($imagepath_original . $filename, $imagepath . $filename)) {
501  $ilLog->write("matching question image could not be duplicated: $imagepath_original$filename");
502  }
503  if (@file_exists($imagepath_original . $this->getThumbPrefix() . $filename)) {
504  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
505  $ilLog->write("matching question image thumbnail could not be duplicated: $imagepath_original" . $this->getThumbPrefix() . $filename);
506  }
507  }
508  }
509  }
510  }
511 
512  public function copyImages($question_id, $source_questionpool): void
513  {
514  global $DIC;
515  $ilLog = $DIC['ilLog'];
516 
517  $imagepath = $this->getImagePath();
518  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
519  $imagepath_original = str_replace("/$this->obj_id/", "/$source_questionpool/", $imagepath_original);
520  foreach ($this->terms as $term) {
521  if (strlen($term->getPicture())) {
522  if (!file_exists($imagepath)) {
523  ilFileUtils::makeDirParents($imagepath);
524  }
525  $filename = $term->getPicture();
526  if (!@copy($imagepath_original . $filename, $imagepath . $filename)) {
527  $ilLog->write("matching question image could not be copied: $imagepath_original$filename");
528  }
529  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
530  $ilLog->write("matching question image thumbnail could not be copied: $imagepath_original" . $this->getThumbPrefix() . $filename);
531  }
532  }
533  }
534  foreach ($this->definitions as $definition) {
535  if (strlen($definition->getPicture())) {
536  $filename = $definition->getPicture();
537  if (!file_exists($imagepath)) {
538  ilFileUtils::makeDirParents($imagepath);
539  }
540 
541  if (assQuestion::isFileAvailable($imagepath_original . $filename)) {
542  copy($imagepath_original . $filename, $imagepath . $filename);
543  } else {
544  $ilLog->write("matching question image could not be copied: $imagepath_original$filename");
545  }
546 
547  if (assQuestion::isFileAvailable($imagepath_original . $this->getThumbPrefix() . $filename)) {
548  copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename);
549  } else {
550  $ilLog->write("matching question image thumbnail could not be copied: $imagepath_original" . $this->getThumbPrefix() . $filename);
551  }
552  }
553  }
554  }
555 
566  public function insertMatchingPair($position, $term = null, $definition = null, $points = 0.0): void
567  {
568  $pair = $this->createMatchingPair($term, $definition, $points);
569 
570  if ($position < count($this->matchingpairs)) {
571  $part1 = array_slice($this->matchingpairs, 0, $position);
572  $part2 = array_slice($this->matchingpairs, $position);
573  $this->matchingpairs = array_merge($part1, [$pair], $part2);
574  } else {
575  array_push($this->matchingpairs, $pair);
576  }
577  }
578 
590  public function addMatchingPair(assAnswerMatchingTerm $term = null, assAnswerMatchingDefinition $definition = null, $points = 0.0): void
591  {
592  $pair = $this->createMatchingPair($term, $definition, $points);
593  array_push($this->matchingpairs, $pair);
594  }
595 
599  public function getTermWithIdentifier($a_identifier)
600  {
601  foreach ($this->terms as $term) {
602  if ($term->getIdentifier() == $a_identifier) {
603  return $term;
604  }
605  }
606  return null;
607  }
608 
612  public function getDefinitionWithIdentifier($a_identifier)
613  {
614  foreach ($this->definitions as $definition) {
615  if ($definition->getIdentifier() == $a_identifier) {
616  return $definition;
617  }
618  }
619  return null;
620  }
621 
630  public function getMatchingPair($index = 0): ?object
631  {
632  if ($index < 0) {
633  return null;
634  }
635  if (count($this->matchingpairs) < 1) {
636  return null;
637  }
638  if ($index >= count($this->matchingpairs)) {
639  return null;
640  }
641  return $this->matchingpairs[$index];
642  }
643 
651  public function deleteMatchingPair($index = 0): void
652  {
653  if ($index < 0) {
654  return;
655  }
656  if (count($this->matchingpairs) < 1) {
657  return;
658  }
659  if ($index >= count($this->matchingpairs)) {
660  return;
661  }
662  unset($this->matchingpairs[$index]);
663  $this->matchingpairs = array_values($this->matchingpairs);
664  }
665 
670  public function flushMatchingPairs(): void
671  {
672  $this->matchingpairs = [];
673  }
674 
678  public function withMatchingPairs(array $pairs): self
679  {
680  $clone = clone $this;
681  $clone->matchingpairs = $pairs;
682  return $clone;
683  }
684 
685 
692  public function getMatchingPairCount(): int
693  {
694  return count($this->matchingpairs);
695  }
696 
703  public function getTerms(): array
704  {
705  return $this->terms;
706  }
707 
714  public function getDefinitions(): array
715  {
716  return $this->definitions;
717  }
718 
725  public function getTermCount(): int
726  {
727  return count($this->terms);
728  }
729 
736  public function getDefinitionCount(): int
737  {
738  return count($this->definitions);
739  }
740 
741  public function addTerm(assAnswerMatchingTerm $term): void
742  {
743  $this->terms[] = $term;
744  }
745 
752  public function addDefinition($definition): void
753  {
754  array_push($this->definitions, $definition);
755  }
756 
763  public function insertTerm($position, assAnswerMatchingTerm $term = null): void
764  {
765  if (is_null($term)) {
766  $term = $this->createMatchingTerm();
767  }
768  if ($position < count($this->terms)) {
769  $part1 = array_slice($this->terms, 0, $position);
770  $part2 = array_slice($this->terms, $position);
771  $this->terms = array_merge($part1, [$term], $part2);
772  } else {
773  array_push($this->terms, $term);
774  }
775  }
776 
783  public function insertDefinition($position, assAnswerMatchingDefinition $definition = null): void
784  {
785  if (is_null($definition)) {
786  $definition = $this->createMatchingDefinition();
787  }
788  if ($position < count($this->definitions)) {
789  $part1 = array_slice($this->definitions, 0, $position);
790  $part2 = array_slice($this->definitions, $position);
791  $this->definitions = array_merge($part1, [$definition], $part2);
792  } else {
793  array_push($this->definitions, $definition);
794  }
795  }
796 
801  public function flushTerms(): void
802  {
803  $this->terms = [];
804  }
805 
810  public function flushDefinitions(): void
811  {
812  $this->definitions = [];
813  }
814 
821  public function deleteTerm($position): void
822  {
823  unset($this->terms[$position]);
824  $this->terms = array_values($this->terms);
825  }
826 
833  public function deleteDefinition($position): void
834  {
835  unset($this->definitions[$position]);
836  $this->definitions = array_values($this->definitions);
837  }
838 
846  public function setTerm($term, $index): void
847  {
848  $this->terms[$index] = $term;
849  }
850 
861  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false): float
862  {
863  if ($returndetails) {
864  throw new ilTestException('return details not implemented for ' . __METHOD__);
865  }
866 
867  $found_values = [];
868  if (is_null($pass)) {
869  $pass = $this->getSolutionMaxPass($active_id);
870  }
871  $result = $this->getCurrentSolutionResultSet($active_id, (int) $pass, $authorizedSolution);
872  while ($data = $this->db->fetchAssoc($result)) {
873  if (strcmp($data["value1"], "") != 0) {
874  if (!isset($found_values[$data['value2']])) {
875  $found_values[$data['value2']] = [];
876  }
877 
878  $found_values[$data['value2']][] = $data['value1'];
879  }
880  }
881 
882  $points = $this->calculateReachedPointsForSolution($found_values);
883 
884  return $points;
885  }
886 
890  public function getMaximumPoints(): float
891  {
892  $points = 0;
893 
894  foreach ($this->getMaximumScoringMatchingPairs() as $pair) {
895  $points += $pair->getPoints();
896  }
897 
898  return $points;
899  }
900 
901  public function getMaximumScoringMatchingPairs(): array
902  {
903  if ($this->getMatchingMode() == self::MATCHING_MODE_N_ON_N) {
904  return $this->getPositiveScoredMatchingPairs();
905  } elseif ($this->getMatchingMode() == self::MATCHING_MODE_1_ON_1) {
907  }
908 
909  return [];
910  }
911 
912  private function getPositiveScoredMatchingPairs(): array
913  {
914  $matchingPairs = [];
915 
916  foreach ($this->matchingpairs as $pair) {
917  if ($pair->getPoints() <= 0) {
918  continue;
919  }
920 
921  $matchingPairs[] = $pair;
922  }
923 
924  return $matchingPairs;
925  }
926 
928  {
929  $matchingPairsByDefinition = [];
930 
931  foreach ($this->matchingpairs as $pair) {
932  if ($pair->getPoints() <= 0) {
933  continue;
934  }
935 
936  $defId = $pair->getDefinition()->getIdentifier();
937 
938  if (!isset($matchingPairsByDefinition[$defId])) {
939  $matchingPairsByDefinition[$defId] = $pair;
940  } elseif ($pair->getPoints() > $matchingPairsByDefinition[$defId]->getPoints()) {
941  $matchingPairsByDefinition[$defId] = $pair;
942  }
943  }
944 
945  return $matchingPairsByDefinition;
946  }
947 
952  public function fetchIndexedValuesFromValuePairs(array $valuePairs): array
953  {
954  $indexedValues = [];
955 
956  foreach ($valuePairs as $valuePair) {
957  if (!isset($indexedValues[$valuePair['value2']])) {
958  $indexedValues[$valuePair['value2']] = [];
959  }
960 
961  $indexedValues[$valuePair['value2']][] = $valuePair['value1'];
962  }
963 
964  return $indexedValues;
965  }
966 
975  public function getEncryptedFilename($filename): string
976  {
977  $extension = "";
978  if (preg_match("/.*\\.(\\w+)$/", $filename, $matches)) {
979  $extension = $matches[1];
980  }
981  return md5($filename) . "." . $extension;
982  }
983 
984  public function removeTermImage($index): void
985  {
986  $term = $this->terms[$index] ?? null;
987  if (is_object($term)) {
988  $this->deleteImagefile($term->getPicture());
989  $term = $term->withPicture('');
990  }
991  }
992 
993  public function removeDefinitionImage($index): void
994  {
995  $definition = $this->definitions[$index] ?? null;
996  if (is_object($definition)) {
997  $this->deleteImagefile($definition->getPicture());
998  $definition = $definition->withPicture('');
999  }
1000  }
1001 
1002 
1009  public function deleteImagefile(string $filename): bool
1010  {
1011  $deletename = $filename;
1012  try {
1013  $result = unlink($this->getImagePath() . $deletename)
1014  && unlink($this->getImagePath() . $this->getThumbPrefix() . $deletename);
1015  } catch (Throwable $e) {
1016  $result = false;
1017  }
1018  return $result;
1019  }
1020 
1029  public function setImageFile($image_tempfilename, $image_filename, $previous_filename = '')
1030  {
1031  $result = true;
1032  if (strlen($image_tempfilename)) {
1033  $image_filename = str_replace(" ", "_", $image_filename);
1034  $imagepath = $this->getImagePath();
1035  if (!file_exists($imagepath)) {
1036  ilFileUtils::makeDirParents($imagepath);
1037  }
1038  $savename = $image_filename;
1039  if (!ilFileUtils::moveUploadedFile($image_tempfilename, $savename, $imagepath . $savename)) {
1040  $result = false;
1041  } else {
1042  // create thumbnail file
1043  $thumbpath = $imagepath . $this->getThumbPrefix() . $savename;
1044  ilShellUtil::convertImage($imagepath . $savename, $thumbpath, "JPEG", (string) $this->getThumbGeometry());
1045  }
1046  if ($result && (strcmp($image_filename, $previous_filename) != 0) && (strlen($previous_filename))) {
1047  $this->deleteImagefile($previous_filename);
1048  }
1049  }
1050  return $result;
1051  }
1052 
1053  private function fetchSubmittedMatchingsFromPost(): array
1054  {
1055  $request = $this->dic->testQuestionPool()->internal()->request();
1056  $post = $request->getParsedBody();
1057 
1058  $matchings = [];
1059  if (array_key_exists('matching', $post)) {
1060  $postData = $post['matching'][$this->getId()];
1061  foreach ($this->getDefinitions() as $definition) {
1062  if (isset($postData[$definition->getIdentifier()])) {
1063  foreach ($this->getTerms() as $term) {
1064  if (isset($postData[$definition->getIdentifier()][$term->getIdentifier()])) {
1065  if (!is_array($postData[$definition->getIdentifier()])) {
1066  $postData[$definition->getIdentifier()] = [];
1067  }
1068  $matchings[$definition->getIdentifier()][] = $term->getIdentifier();
1069  }
1070  }
1071  }
1072  }
1073  }
1074 
1075  return $matchings;
1076  }
1077 
1078  private function checkSubmittedMatchings($submittedMatchings): bool
1079  {
1080  if ($this->getMatchingMode() == self::MATCHING_MODE_N_ON_N) {
1081  return true;
1082  }
1083 
1084  $handledTerms = [];
1085 
1086  foreach ($submittedMatchings as $definition => $terms) {
1087  if (count($terms) > 1) {
1088  $this->tpl->setOnScreenMessage('failure', $this->lng->txt("multiple_matching_values_selected"), true);
1089  return false;
1090  }
1091 
1092  foreach ($terms as $i => $term) {
1093  if (isset($handledTerms[$term])) {
1094  $this->tpl->setOnScreenMessage('failure', $this->lng->txt("duplicate_matching_values_selected"), true);
1095  return false;
1096  }
1097 
1098  $handledTerms[$term] = $term;
1099  }
1100  }
1101 
1102  return true;
1103  }
1104 
1113  public function saveWorkingData($active_id, $pass = null, $authorized = true): bool
1114  {
1115  $submittedMatchings = $this->fetchSubmittedMatchingsFromPost();
1116  $submittedMatchingsValid = $this->checkSubmittedMatchings($submittedMatchings);
1117 
1118  $matchingsExist = false;
1119 
1120  if ($submittedMatchingsValid) {
1121  if (is_null($pass)) {
1122  $pass = ilObjTest::_getPass($active_id);
1123  }
1124 
1125  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$matchingsExist, $submittedMatchings, $active_id, $pass, $authorized) {
1126  $this->removeCurrentSolution($active_id, $pass, $authorized);
1127 
1128  foreach ($submittedMatchings as $definition => $terms) {
1129  foreach ($terms as $i => $term) {
1130  $this->saveCurrentSolution($active_id, $pass, $term, $definition, $authorized);
1131  $matchingsExist = true;
1132  }
1133  }
1134  });
1135 
1136  $saveWorkingDataResult = true;
1137  } else {
1138  $saveWorkingDataResult = false;
1139  }
1140 
1142  if ($matchingsExist) {
1143  assQuestion::logAction($this->lng->txtlng(
1144  "assessment",
1145  "log_user_entered_values",
1147  ), $active_id, $this->getId());
1148  } else {
1149  assQuestion::logAction($this->lng->txtlng(
1150  "assessment",
1151  "log_user_not_entered_values",
1153  ), $active_id, $this->getId());
1154  }
1155  }
1156 
1157  return $saveWorkingDataResult;
1158  }
1159 
1160  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession): void
1161  {
1162  $submittedMatchings = $this->fetchSubmittedMatchingsFromPost();
1163 
1164  if ($this->checkSubmittedMatchings($submittedMatchings)) {
1165  $previewSession->setParticipantsSolution($submittedMatchings);
1166  }
1167  }
1168 
1169  public function getRandomId(): int
1170  {
1171  mt_srand((float) microtime() * 1000000);
1172  $random_number = mt_rand(1, 100000);
1173  $found = false;
1174  while ($found) {
1175  $found = false;
1176  foreach ($this->matchingpairs as $key => $pair) {
1177  if (($pair->getTerm()->getIdentifier() == $random_number) || ($pair->getDefinition()->getIdentifier() == $random_number)) {
1178  $found = true;
1179  $random_number++;
1180  }
1181  }
1182  }
1183  return $random_number;
1184  }
1185 
1186  public function setShuffle($shuffle = true): void
1187  {
1188  $this->shuffle = (bool) $shuffle;
1189  }
1190 
1196  public function getQuestionType(): string
1197  {
1198  return "assMatchingQuestion";
1199  }
1200 
1201  public function getAdditionalTableName(): string
1202  {
1203  return "qpl_qst_matching";
1204  }
1205 
1206  public function getAnswerTableName(): array
1207  {
1208  return ["qpl_a_matching", "qpl_a_mterm"];
1209  }
1210 
1215  public function getRTETextWithMediaObjects(): string
1216  {
1217  return parent::getRTETextWithMediaObjects();
1218  }
1219 
1223  public function &getMatchingPairs(): array
1224  {
1225  return $this->matchingpairs;
1226  }
1227 
1231  public function setExportDetailsXLSX(ilAssExcelFormatHelper $worksheet, int $startrow, int $col, int $active_id, int $pass): int
1232  {
1233  parent::setExportDetailsXLSX($worksheet, $startrow, $col, $active_id, $pass);
1234 
1235  $solutions = $this->getSolutionValues($active_id, $pass);
1236 
1237  $imagepath = $this->getImagePath();
1238  $i = 1;
1239  foreach ($solutions as $solution) {
1240  $matches_written = false;
1241  foreach ($this->getMatchingPairs() as $idx => $pair) {
1242  if (!$matches_written) {
1243  $worksheet->setCell($startrow + $i, $col + 1, $this->lng->txt("matches"));
1244  }
1245  $matches_written = true;
1246  if ($pair->getDefinition()->getIdentifier() == $solution["value2"]) {
1247  if (strlen($pair->getDefinition()->getText())) {
1248  $worksheet->setCell($startrow + $i, $col, $pair->getDefinition()->getText());
1249  } else {
1250  $worksheet->setCell($startrow + $i, $col, $pair->getDefinition()->getPicture());
1251  }
1252  }
1253  if ($pair->getTerm()->getIdentifier() == $solution["value1"]) {
1254  if (strlen($pair->getTerm()->getText())) {
1255  $worksheet->setCell($startrow + $i, $col + 2, $pair->getTerm()->getText());
1256  } else {
1257  $worksheet->setCell($startrow + $i, $col + 2, $pair->getTerm()->getPicture());
1258  }
1259  }
1260  }
1261  $i++;
1262  }
1263 
1264  return $startrow + $i + 1;
1265  }
1266 
1272  public function getThumbGeometry(): int
1273  {
1274  return $this->thumb_geometry;
1275  }
1276 
1282  public function getThumbSize(): int
1283  {
1284  return $this->getThumbGeometry();
1285  }
1286 
1292  public function setThumbGeometry(int $a_geometry): void
1293  {
1294  $this->thumb_geometry = ($a_geometry < 1) ? 100 : $a_geometry;
1295  }
1296 
1300  public function rebuildThumbnails(): void
1301  {
1302  $new_terms = [];
1303  foreach ($this->terms as $term) {
1304  if ($term->getPicture() !== '') {
1305  $current_file_path = $this->getImagePath() . $term->getPicture();
1306  if (!file_exists($current_file_path)) {
1307  $new_terms[] = $term;
1308  continue;
1309  }
1310  $new_file_name = $this->buildHashedImageFilename($term->getPicture(), true);
1311  $new_file_path = $this->getImagePath() . $new_file_name;
1312  rename($current_file_path, $new_file_path);
1313  $term = $term->withPicture($new_file_name);
1314  $this->generateThumbForFile($this->getImagePath(), $term->getPicture());
1315  }
1316  $new_terms[] = $term;
1317  }
1318  $this->terms = $new_terms;
1319 
1320  $new_definitions = [];
1321  foreach ($this->definitions as $definition) {
1322  if ($definition->getPicture() !== '') {
1323  $current_file_path = $this->getImagePath() . $definition->getPicture();
1324  if (!file_exists($current_file_path)) {
1325  $new_definitions[] = $definition;
1326  continue;
1327  }
1328  $new_file_name = $this->buildHashedImageFilename($definition->getPicture(), true);
1329  $new_file_path = $this->getImagePath() . $new_file_name;
1330  rename($current_file_path, $new_file_path);
1331  $definition = $definition->withPicture($new_file_name);
1332  $this->generateThumbForFile($this->getImagePath(), $definition->getPicture());
1333  }
1334  $new_definitions[] = $definition;
1335  }
1336  $this->definitions = $new_definitions;
1337  }
1338 
1339  public function getThumbPrefix(): string
1340  {
1341  return "thumb.";
1342  }
1343 
1344  protected function generateThumbForFile($path, $file): void
1345  {
1346  $filename = $path . $file;
1347  if (file_exists($filename)) {
1348  $thumbpath = $path . $this->getThumbPrefix() . $file;
1349  $path_info = pathinfo($filename);
1350  $ext = "";
1351  switch (strtoupper($path_info['extension'])) {
1352  case 'PNG':
1353  $ext = 'PNG';
1354  break;
1355  case 'GIF':
1356  $ext = 'GIF';
1357  break;
1358  default:
1359  $ext = 'JPEG';
1360  break;
1361  }
1362  ilShellUtil::convertImage($filename, $thumbpath, $ext, (string) $this->getThumbGeometry());
1363  }
1364  }
1365 
1369  public function toJSON(): string
1370  {
1371  $result = [];
1372 
1373  $result['id'] = $this->getId();
1374  $result['type'] = (string) $this->getQuestionType();
1375  $result['title'] = $this->getTitleForHTMLOutput();
1376  $result['question'] = $this->formatSAQuestion($this->getQuestion());
1377  $result['nr_of_tries'] = $this->getNrOfTries();
1378  $result['matching_mode'] = $this->getMatchingMode();
1379  $result['shuffle'] = true;
1380  $result['feedback'] = [
1381  'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
1382  'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
1383  ];
1384 
1385  $this->setShuffler($this->randomGroup->shuffleArray(new RandomSeed()));
1386 
1387  $terms = [];
1388  foreach ($this->getShuffler()->transform($this->getTerms()) as $term) {
1389  $terms[] = [
1390  "text" => $this->formatSAQuestion($term->getText()),
1391  "id" => $this->getId() . $term->getIdentifier()
1392  ];
1393  }
1394  $result['terms'] = $terms;
1395 
1396  $this->setShuffler($this->randomGroup->shuffleArray(new RandomSeed()));
1397 
1398  $definitions = [];
1399  foreach ($this->getShuffler()->transform($this->getDefinitions()) as $def) {
1400  $definitions[] = [
1401  "text" => $this->formatSAQuestion((string) $def->getText()),
1402  "id" => $this->getId() . $def->getIdentifier()
1403  ];
1404  }
1405  $result['definitions'] = $definitions;
1406 
1407  // #10353
1408  $matchings = [];
1409  foreach ($this->getMatchingPairs() as $pair) {
1410  // fau: fixLmMatchingPoints - ignore matching pairs with 0 or negative points
1411  if ($pair->getPoints() <= 0) {
1412  continue;
1413  }
1414  // fau.
1415 
1416  $pid = $pair->getDefinition()->getIdentifier();
1417  if ($this->getMatchingMode() == self::MATCHING_MODE_N_ON_N) {
1418  $pid .= '::' . $pair->getTerm()->getIdentifier();
1419  }
1420 
1421  if (!isset($matchings[$pid]) || $matchings[$pid]["points"] < $pair->getPoints()) {
1422  $matchings[$pid] = [
1423  "term_id" => $this->getId() . $pair->getTerm()->getIdentifier(),
1424  "def_id" => $this->getId() . $pair->getDefinition()->getIdentifier(),
1425  "points" => (int) $pair->getPoints()
1426  ];
1427  }
1428  }
1429 
1430  $result['matchingPairs'] = array_values($matchings);
1431 
1432  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1433  $result['mobs'] = $mobs;
1434 
1435  $this->lng->loadLanguageModule('assessment');
1436  $result['reset_button_label'] = $this->lng->txt("reset_terms");
1437 
1438  return json_encode($result);
1439  }
1440 
1441  public function setMatchingMode($matchingMode): void
1442  {
1443  $this->matchingMode = $matchingMode;
1444  }
1445 
1446  public function getMatchingMode(): string
1447  {
1448  return $this->matchingMode;
1449  }
1450 
1455  protected function calculateReachedPointsForSolution($found_values): float
1456  {
1457  $points = 0;
1458  if (! is_array($found_values)) {
1459  return $points;
1460  }
1461  foreach ($found_values as $definition => $terms) {
1462  if (!is_array($terms)) {
1463  continue;
1464  }
1465  foreach ($terms as $term) {
1466  foreach ($this->matchingpairs as $pair) {
1467  if ($pair->getDefinition()->getIdentifier() == $definition && $pair->getTerm()->getIdentifier() == $term) {
1468  $points += $pair->getPoints();
1469  }
1470  }
1471  }
1472  }
1473  return $points;
1474  }
1475 
1484  public function getOperators($expression): array
1485  {
1487  }
1488 
1493  public function getExpressionTypes(): array
1494  {
1495  return [
1500  ];
1501  }
1510  public function getUserQuestionResult($active_id, $pass): ilUserQuestionResult
1511  {
1512  $result = new ilUserQuestionResult($this, $active_id, $pass);
1513 
1514  $data = $this->db->queryF(
1515  "SELECT ident FROM qpl_a_mdef WHERE question_fi = %s ORDER BY def_id",
1516  ["integer"],
1517  [$this->getId()]
1518  );
1519 
1520  $definitions = [];
1521  for ($index = 1; $index <= $this->db->numRows($data); ++$index) {
1522  $row = $this->db->fetchAssoc($data);
1523  $definitions[$row["ident"]] = $index;
1524  }
1525 
1526  $data = $this->db->queryF(
1527  "SELECT ident FROM qpl_a_mterm WHERE question_fi = %s ORDER BY term_id",
1528  ["integer"],
1529  [$this->getId()]
1530  );
1531 
1532  $terms = [];
1533  for ($index = 1; $index <= $this->db->numRows($data); ++$index) {
1534  $row = $this->db->fetchAssoc($data);
1535  $terms[$row["ident"]] = $index;
1536  }
1537 
1538  $maxStep = $this->lookupMaxStep($active_id, $pass);
1539 
1540  if ($maxStep > 0) {
1541  $data = $this->db->queryF(
1542  "SELECT value1, value2 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
1543  ["integer", "integer", "integer","integer"],
1544  [$active_id, $pass, $this->getId(), $maxStep]
1545  );
1546  } else {
1547  $data = $this->db->queryF(
1548  "SELECT value1, value2 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
1549  ["integer", "integer", "integer"],
1550  [$active_id, $pass, $this->getId()]
1551  );
1552  }
1553 
1554  while ($row = $this->db->fetchAssoc($data)) {
1555  if ($row["value1"] > 0) {
1556  $result->addKeyValue($definitions[$row["value2"]], $terms[$row["value1"]]);
1557  }
1558  }
1559 
1560  $points = $this->calculateReachedPoints($active_id, $pass);
1561  $max_points = $this->getMaximumPoints();
1562 
1563  $result->setReachedPercentage(($points / $max_points) * 100);
1564 
1565  return $result;
1566  }
1567 
1574  public function getAvailableAnswerOptions($index = null)
1575  {
1576  if ($index !== null) {
1577  return $this->getMatchingPair($index);
1578  } else {
1579  return $this->getMatchingPairs();
1580  }
1581  }
1582 
1586  protected function afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId): void
1587  {
1588  parent::afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
1589 
1590  $origImagePath = $this->questionFilesService->buildImagePath($origQuestionId, $origParentObjId);
1591  $dupImagePath = $this->questionFilesService->buildImagePath($dupQuestionId, $dupParentObjId);
1592 
1593  ilFileUtils::delDir($origImagePath);
1594  if (is_dir($dupImagePath)) {
1595  ilFileUtils::makeDirParents($origImagePath);
1596  ilFileUtils::rCopy($dupImagePath, $origImagePath);
1597  }
1598  }
1599 
1600  protected function createMatchingTerm(string $term = '', string $picture = '', int $identifier = 0): assAnswerMatchingTerm
1601  {
1602  return new assAnswerMatchingTerm($term, $picture, $identifier);
1603  }
1604  protected function createMatchingDefinition(string $term = '', string $picture = '', int $identifier = 0): assAnswerMatchingDefinition
1605  {
1606  return new assAnswerMatchingDefinition($term, $picture, $identifier);
1607  }
1608  protected function createMatchingPair(
1609  assAnswerMatchingTerm $term = null,
1610  assAnswerMatchingDefinition $definition = null,
1611  float $points = 0.0
1613  $term = $term ?? $this->createMatchingTerm();
1614  $definition = $definition ?? $this->createMatchingDefinition();
1615  return new assAnswerMatchingPair($term, $definition, $points);
1616  }
1617 }
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
getThumbGeometry()
Get the thumbnail geometry.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getSolutionValues($active_id, $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
setNrOfTries(int $a_nr_of_tries)
addTerm(assAnswerMatchingTerm $term)
getMaximumPoints()
Calculates and Returns the maximum points, a learner can reach answering the question.
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
getTermCount()
Returns the number of terms.
copyObject($target_questionpool_id, $title="")
Copies an assMatchingQuestion.
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
deleteMatchingPair($index=0)
Deletes a matching pair with a given index.
Abstract basic class which is to be extended by the concrete assessment question type classes...
static isFileAvailable(string $file)
setOwner(int $owner=-1)
afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
{}
getDefinitionCount()
Returns the number of definitions.
getQuestionType()
Returns the question type of the question.
bool $shuffle
Indicates whether the answers will be shuffled or not.
flushMatchingPairs()
Deletes all matching pairs.
setImageFile($image_tempfilename, $image_filename, $previous_filename='')
Sets the image file and uploads the image to the object&#39;s image directory.
getEncryptedFilename($filename)
Returns the encrypted save filename of a matching picture Images are saved with an encrypted filename...
flushTerms()
Deletes all terms.
const MT_TERMS_DEFINITIONS
static rCopy(string $a_sdir, string $a_tdir, bool $preserveTimeAttributes=false)
Copies content of a directory $a_sdir recursively to a directory $a_tdir.
fetchIndexedValuesFromValuePairs(array $valuePairs)
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
setCell($a_row, $a_col, $a_value, $datatype=null)
deleteTerm($position)
Deletes a term.
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
setComment(string $comment="")
setThumbGeometry(int $a_geometry)
Set the thumbnail geometry.
rebuildThumbnails()
Rebuild the thumbnail images with a new thumbnail size.
calculateReachedPointsForSolution($found_values)
getDefinitions()
Returns the definitions of the matching question.
float $points
The maximum available points for the question.
addMatchingPair(assAnswerMatchingTerm $term=null, assAnswerMatchingDefinition $definition=null, $points=0.0)
Adds an matching pair for an matching choice question.
loadFromDb($question_id)
Loads a assMatchingQuestion object from a database.
getMatchingPair($index=0)
Returns a matching pair with a given index.
Base Exception for all Exceptions relating to Modules/Test.
$path
Definition: ltiservices.php:32
Class for matching questions.
duplicateImages($question_id, $objectId=null)
global $DIC
Definition: feed.php:28
getTermWithIdentifier($a_identifier)
Returns a term with a given identifier.
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
insertMatchingPair($position, $term=null, $definition=null, $points=0.0)
Inserts a matching pair for an matching choice question.
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
__construct(VocabulariesInterface $vocabularies)
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
duplicate(bool $for_test=true, string $title="", string $author="", int $owner=-1, $testObjId=null)
Duplicates an assMatchingQuestion.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
buildHashedImageFilename(string $plain_image_filename, bool $unique=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setTerm($term, $index)
Sets a specific term.
static logAction(string $logtext, int $active_id, int $question_id)
getMatchingPairCount()
Returns the number of matching pairs.
getExpressionTypes()
Get all available expression types for a specific question.
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
getOperators($expression)
Get all available operations for a specific question.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
checkSubmittedMatchings($submittedMatchings)
string $key
Consumer key/client ID value.
Definition: System.php:193
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
deleteImagefile(string $filename)
Deletes an imagefile from the system if the file is deleted manually.
addDefinition($definition)
Adds a definition.
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
setPoints(float $points)
setObjId(int $obj_id=0)
string $question
The question text.
static convertImage(string $a_from, string $a_to, string $a_target_format="", string $a_geometry="", string $a_background_color="")
static _getMobsOfObject(string $a_type, int $a_id, int $a_usage_hist_nr=0, string $a_lang="-")
flushDefinitions()
Deletes all definitions.
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
$filename
Definition: buildRTE.php:78
createMatchingTerm(string $term='', string $picture='', int $identifier=0)
__construct( $title="", $comment="", $author="", $owner=-1, $question="", $matching_type=MT_TERMS_DEFINITIONS)
assMatchingQuestion constructor
toJSON()
Returns a JSON representation of the question.
insertTerm($position, assAnswerMatchingTerm $term=null)
Inserts a term.
deleteDefinition($position)
Deletes a definition.
setExportDetailsXLSX(ilAssExcelFormatHelper $worksheet, int $startrow, int $col, int $active_id, int $pass)
{}
saveQuestionDataToDb(int $original_id=-1)
getSolutionMaxPass(int $active_id)
getTerms()
Returns the terms of the matching question.
removeCurrentSolution(int $active_id, int $pass, bool $authorized=true)
getDefinitionWithIdentifier($a_identifier)
Returns a definition with a given identifier.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
createMatchingPair(assAnswerMatchingTerm $term=null, assAnswerMatchingDefinition $definition=null, float $points=0.0)
setId(int $id=-1)
setOriginalId(?int $original_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setShuffler(Transformation $shuffler)
setTitle(string $title="")
createMatchingDefinition(string $term='', string $picture='', int $identifier=0)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setLifecycle(ilAssQuestionLifecycle $lifecycle)
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
saveToDb($original_id="")
Saves a assMatchingQuestion object to a database.
& getMatchingPairs()
Returns the matchingpairs array.
copyImages($question_id, $source_questionpool)
ILIAS DI LoggingServices $ilLog
lookupMaxStep(int $active_id, int $pass)
setAuthor(string $author="")
$post
Definition: ltitoken.php:49
savePreviewData(ilAssQuestionPreviewSession $previewSession)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
isComplete()
Returns true, if a matching question is complete for use.
insertDefinition($position, assAnswerMatchingDefinition $definition=null)
Inserts a definition.
getThumbSize()
Get the thumbnail geometry.
setQuestion(string $question="")