ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.assClozeTestImport.php
Go to the documentation of this file.
1 <?php
2 
29 {
43  public function fromXML(&$item, $questionpool_id, &$tst_id, &$tst_object, &$question_counter, $import_mapping): array
44  {
45  global $DIC;
46  $ilUser = $DIC['ilUser'];
47 
48  // empty session variable for imported xhtml mobs
49  ilSession::clear('import_mob_xhtml');
50  $presentation = $item->getPresentation();
51 
52  $questiontext = $this->processNonAbstractedImageReferences(
53  $item->getMetadataEntry("question") ?? '&nbsp;',
54  $item->getIliasSourceNic()
55  );
56 
57  $clozetext_array = [];
58  $gaps = [];
59  foreach ($presentation->order as $entry) {
60  switch ($entry["type"]) {
61  case "material":
62 
63  $material_string = $this->object->QTIMaterialToString(
64  $presentation->material[$entry["index"]]
65  );
66 
67  if ($questiontext === '&nbsp;') {
68  $questiontext = $material_string;
69  } else {
70  array_push($clozetext_array, $material_string);
71  }
72 
73  break;
74  case "response":
75  $response = $presentation->response[$entry["index"]];
76  $rendertype = $response->getRenderType();
77  array_push($clozetext_array, "<<" . $response->getIdent() . ">>");
78 
79  switch (strtolower(get_class($response->getRenderType()))) {
80  case "ilqtirenderfib":
81  switch ($response->getRenderType()->getFibtype()) {
84  array_push(
85  $gaps,
86  array(
87  "ident" => $response->getIdent(),
88  "type" => CLOZE_NUMERIC,
89  "answers" => array(),
90  "minnumber" => $response->getRenderType()->getMinnumber(),
91  "maxnumber" => $response->getRenderType()->getMaxnumber(),
92  'gap_size' => $response->getRenderType()->getMaxchars()
93  )
94  );
95  break;
96  default:
98  array_push(
99  $gaps,
100  array("ident" => $response->getIdent(),
101  "type" => CLOZE_TEXT,
102  "answers" => array(),
103  'gap_size' => $response->getRenderType()->getMaxchars()
104  )
105  );
106  break;
107  }
108  break;
109  case "ilqtirenderchoice":
110  $answers = array();
111  $shuffle = $rendertype->getShuffle();
112  $answerorder = 0;
113  foreach ($rendertype->response_labels as $response_label) {
114  $ident = $response_label->getIdent();
115  $answertext = "";
116  foreach ($response_label->material as $mat) {
117  $answertext .= $this->object->QTIMaterialToString($mat);
118  }
119  $answers[$ident] = array(
120  "answertext" => $answertext,
121  "points" => 0,
122  "answerorder" => $answerorder++,
123  "action" => "",
124  "shuffle" => $rendertype->getShuffle()
125  );
126  }
127  array_push($gaps, array("ident" => $response->getIdent(), "type" => CLOZE_SELECT, "shuffle" => $rendertype->getShuffle(), "answers" => $answers));
128  break;
129  }
130  break;
131  }
132  }
133  $responses = array();
134  $feedbacks = array();
135  $feedbacksgeneric = array();
136  foreach ($item->resprocessing as $resprocessing) {
137  foreach ($resprocessing->respcondition as $respcondition) {
138  $ident = "";
139  $correctness = 1;
140  $conditionvar = $respcondition->getConditionvar();
141  foreach ($conditionvar->order as $order) {
142  switch ($order["field"]) {
143  case "varequal":
144  $equals = $conditionvar->varequal[$order["index"]]->getContent();
145  $gapident = $conditionvar->varequal[$order["index"]]->getRespident();
146  break;
147  }
148  }
149  foreach ($respcondition->setvar as $setvar) {
150  if (strcmp($gapident, "") != 0) {
151  foreach ($gaps as $gi => $g) {
152  if (strcmp($g["ident"], $gapident) == 0) {
153  if ($g["type"] == CLOZE_SELECT) {
154  foreach ($gaps[$gi]["answers"] as $ai => $answer) {
155  if (strcmp($answer["answertext"], $equals) == 0) {
156  $gaps[$gi]["answers"][$ai]["action"] = $setvar->getAction();
157  $gaps[$gi]["answers"][$ai]["points"] = $setvar->getContent();
158  }
159  }
160  } elseif ($g["type"] == CLOZE_TEXT) {
161  array_push($gaps[$gi]["answers"], array(
162  "answertext" => $equals,
163  "points" => $setvar->getContent(),
164  "answerorder" => count($gaps[$gi]["answers"]),
165  "action" => $setvar->getAction()
166 
167  ));
168  } elseif ($g["type"] == CLOZE_NUMERIC) {
169  array_push($gaps[$gi]["answers"], array(
170  "answertext" => $equals,
171  "points" => $setvar->getContent(),
172  "answerorder" => count($gaps[$gi]["answers"]),
173  "action" => $setvar->getAction()
174  ));
175  }
176  }
177  }
178  }
179  }
180 
181  if (count($respcondition->displayfeedback)) {
182  foreach ($respcondition->displayfeedback as $feedbackpointer) {
183  if (strlen($feedbackpointer->getLinkrefid())) {
184  foreach ($item->itemfeedback as $ifb) {
185  if (strcmp($ifb->getIdent(), "response_allcorrect") == 0) {
186  // found a feedback for the identifier
187  if (count($ifb->material)) {
188  foreach ($ifb->material as $material) {
189  $feedbacksgeneric[1] = $material;
190  }
191  }
192  if ((count($ifb->flow_mat) > 0)) {
193  foreach ($ifb->flow_mat as $fmat) {
194  if (count($fmat->material)) {
195  foreach ($fmat->material as $material) {
196  $feedbacksgeneric[1] = $material;
197  }
198  }
199  }
200  }
201  } elseif (strcmp($ifb->getIdent(), "response_onenotcorrect") == 0) {
202  // found a feedback for the identifier
203  if (count($ifb->material)) {
204  foreach ($ifb->material as $material) {
205  $feedbacksgeneric[0] = $material;
206  }
207  }
208  if ((count($ifb->flow_mat) > 0)) {
209  foreach ($ifb->flow_mat as $fmat) {
210  if (count($fmat->material)) {
211  foreach ($fmat->material as $material) {
212  $feedbacksgeneric[0] = $material;
213  }
214  }
215  }
216  }
217  } else {
218  // found a feedback for the identifier
219  if (count($ifb->material)) {
220  foreach ($ifb->material as $material) {
221  $feedbacks[$ifb->getIdent()] = $material;
222  }
223  }
224  if ((count($ifb->flow_mat) > 0)) {
225  foreach ($ifb->flow_mat as $fmat) {
226  if (count($fmat->material)) {
227  foreach ($fmat->material as $material) {
228  $feedbacks[$ifb->getIdent()] = $material;
229  }
230  }
231  }
232  }
233  }
234  }
235  }
236  }
237  }
238  }
239  }
240 
241  $this->addGeneralMetadata($item);
242  $this->object->setTitle($item->getTitle());
243  $this->object->setNrOfTries((int) $item->getMaxattempts());
244  $this->object->setComment($item->getComment());
245  $this->object->setAuthor($item->getAuthor());
246  $this->object->setOwner($ilUser->getId());
247  $this->object->setObjId($questionpool_id);
248  $textgap_rating = $item->getMetadataEntry("textgaprating");
249  $this->object->setFixedTextLength($item->getMetadataEntry("fixedTextLength"));
250  $this->object->setIdenticalScoring($item->getMetadataEntry("identicalScoring"));
251  $this->object->setFeedbackMode(
252  strlen($item->getMetadataEntry("feedback_mode")) ?
253  $item->getMetadataEntry("feedback_mode") : ilAssClozeTestFeedback::FB_MODE_GAP_QUESTION
254  );
255  $combinations = json_decode(base64_decode($item->getMetadataEntry("combinations")));
256  if (strlen($textgap_rating) == 0) {
257  $textgap_rating = "ci";
258  }
259  $this->object->setTextgapRating($textgap_rating);
260  $gaptext = array();
261  foreach ($gaps as $gapidx => $gap) {
262  $gapcontent = array();
263  $clozegap = new assClozeGap($gap["type"]);
264  foreach ($gap["answers"] as $index => $answer) {
265  $gapanswer = new assAnswerCloze($answer["answertext"], $answer["points"], $answer["answerorder"]);
266  $gapanswer->setGapSize((int) ($gap["gap_size"] ?? 0));
267  switch ($clozegap->getType()) {
268  case CLOZE_SELECT:
269  $clozegap->setShuffle($answer["shuffle"]);
270  break;
271  case CLOZE_NUMERIC:
272  $gapanswer->setLowerBound($gap["minnumber"]);
273  $gapanswer->setUpperBound($gap["maxnumber"]);
274  break;
275  }
276  $clozegap->setGapSize((int) ($gap["gap_size"] ?? 0));
277  $clozegap->addItem($gapanswer);
278  array_push($gapcontent, $answer["answertext"]);
279  }
280  $this->object->addGapAtIndex($clozegap, $gapidx);
281  $gaptext[$gap["ident"]] = "[gap]" . join(",", $gapcontent) . "[/gap]";
282  }
283 
284  $this->object->setQuestion($questiontext);
285  $clozetext = join("", $clozetext_array);
286 
287  foreach ($gaptext as $idx => $val) {
288  $clozetext = str_replace("<<" . $idx . ">>", $val, $clozetext);
289  }
290  $this->object->setClozeTextValue($clozetext);
291 
292  // additional content editing mode information
293  $this->object->setAdditionalContentEditingMode(
295  );
296  $this->object->saveToDb();
297 
298  if (is_array($combinations) && count($combinations) > 0) {
300  assClozeGapCombination::importGapCombinationToDb($this->object->getId(), $combinations);
301  $gap_combinations = new assClozeGapCombination();
302  $gap_combinations->loadFromDb($this->object->getId());
303  $this->object->setGapCombinations($gap_combinations);
304  $this->object->setGapCombinationsExists(true);
305  }
306 
307  // handle the import of media objects in XHTML code
308  foreach ($feedbacks as $ident => $material) {
309  $m = $this->object->QTIMaterialToString($material);
310  $feedbacks[$ident] = $m;
311  }
312  foreach ($feedbacksgeneric as $correctness => $material) {
313  $m = $this->object->QTIMaterialToString($material);
314  $feedbacksgeneric[$correctness] = $m;
315  }
316 
317  if (is_array(ilSession::get("import_mob_xhtml"))) {
318  foreach (ilSession::get("import_mob_xhtml") as $mob) {
319  if ($tst_id > 0) {
320  $importfile = $this->getTstImportArchivDirectory() . '/' . $mob["uri"];
321  } else {
322  $importfile = $this->getQplImportArchivDirectory() . '/' . $mob["uri"];
323  }
324  global $DIC; /* @var ILIAS\DI\Container $DIC */
325  $DIC['ilLog']->write(__METHOD__ . ': import mob from dir: ' . $importfile);
326 
327  $media_object = ilObjMediaObject::_saveTempFileAsMediaObject(basename($importfile), $importfile, false);
328  $questiontext = str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $questiontext);
329  $clozetext = str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $clozetext);
330  foreach ($feedbacks as $ident => $material) {
331  $feedbacks[$ident] = str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $material);
332  }
333  foreach ($feedbacksgeneric as $correctness => $material) {
334  $feedbacksgeneric[$correctness] = str_replace("src=\"" . $mob["mob"] . "\"", "src=\"" . "il_" . IL_INST_ID . "_mob_" . $media_object->getId() . "\"", $material);
335  }
336  }
337  }
338  $this->object->setQuestion(ilRTE::_replaceMediaObjectImageSrc($questiontext, 1));
339  $this->object->setClozeTextValue(ilRTE::_replaceMediaObjectImageSrc($clozetext, 1));
340  foreach ($feedbacks as $ident => $material) {
341  $fbIdentifier = $this->buildFeedbackIdentifier($ident);
342  $this->object->feedbackOBJ->importSpecificAnswerFeedback(
343  $this->object->getId(),
344  $fbIdentifier->getQuestionIndex(),
345  $fbIdentifier->getAnswerIndex(),
347  );
348  }
349  foreach ($feedbacksgeneric as $correctness => $material) {
350  $this->object->feedbackOBJ->importGenericFeedback(
351  $this->object->getId(),
352  $correctness,
354  );
355  }
356  $this->object->saveToDb();
357 
358  if (count($item->suggested_solutions)) {
359  foreach ($item->suggested_solutions as $suggested_solution) {
360  $this->object->setSuggestedSolution($suggested_solution["solution"]->getContent(), $suggested_solution["gap_index"], true);
361  }
362  $this->object->saveToDb();
363  }
364  if (isset($tst_id) && $tst_id !== $questionpool_id) {
365  $qpl_qid = $this->object->getId();
366  $tst_qid = $this->object->duplicate(true, "", "", "", $tst_id);
367  $tst_object->questions[$question_counter++] = $tst_qid;
368  $import_mapping[$item->getIdent()] = array("pool" => $qpl_qid, "test" => $tst_qid);
369  return $import_mapping;
370  }
371 
372  if (isset($tst_id)) {
373  $tst_object->questions[$question_counter++] = $this->object->getId();
374  $import_mapping[$item->getIdent()] = ["pool" => 0, "test" => $this->object->getId()];
375  return $import_mapping;
376  }
377 
378  $import_mapping[$item->getIdent()] = ["pool" => $this->object->getId(), "test" => 0];
379  return $import_mapping;
380  }
381 
387  {
388  $fbIdentifier = new ilAssSpecificFeedbackIdentifier();
389 
390  $ident = explode('_', $ident);
391 
392  if (count($ident) > 1) {
393  $fbIdentifier->setQuestionIndex($ident[0]);
394  $fbIdentifier->setAnswerIndex($ident[1]);
395  } else {
396  $fbIdentifier->setQuestionIndex($ident[0]);
397  $fbIdentifier->setAnswerIndex(0);
398  }
399 
400  return $fbIdentifier;
401  }
402 }
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...
static get(string $a_var)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
addGeneralMetadata(ilQTIItem $item)
const IL_INST_ID
Definition: constants.php:40
static importGapCombinationToDb($question_id, $gap_combinations)
const CLOZE_TEXT
Cloze question constants.
processNonAbstractedImageReferences($text, $sourceNic)
fetchAdditionalContentEditingModeInformation($qtiItem)
fetches the "additional content editing mode" information from qti item and falls back to ADDITIONAL_...
getQplImportArchivDirectory()
returns the full path to extracted qpl import archiv (qpl import dir + qpl archiv subdir) ...
Class for cloze question gaps.
$index
Definition: metadata.php:145
global $DIC
Definition: feed.php:28
const CLOZE_SELECT
fromXML(&$item, $questionpool_id, &$tst_id, &$tst_object, &$question_counter, $import_mapping)
Creates a question from a QTI file.
static clearGapCombinationsFromDb($question_id)
static _saveTempFileAsMediaObject(string $name, string $tmp_name, bool $upload=true)
Create new media object and update page in db and return new media object.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$ilUser
Definition: imgupload.php:34
getTstImportArchivDirectory()
returns the full path to extracted tst import archiv (tst import dir + tst archiv subdir) ...
const CLOZE_NUMERIC
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...
$response
static clear(string $a_var)
const FB_MODE_GAP_QUESTION
constants for different feedback modes (per gap or per gap-answers/options)