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