ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilQTIParser.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
30 class ilQTIParser extends ilSaxParser
31 {
32  public const IL_MO_PARSE_QTI = 1;
33  public const IL_MO_VERIFY_QTI = 2;
34 
35  public bool $hasRootElement = false;
36 
40  public array $path = [];
41 
45  public array $items = [];
46 
47  public ?ilQTIItem $item = null;
48 
52  public $depth;
53 
54  public string $qti_element = "";
55 
56  public bool $in_presentation = false;
57 
58  public bool $in_response = false;
59 
63  public $render_type = null;
64 
66 
67  public ?ilQTIMaterial $material = null;
68 
69  public ?ilQTIMatimage $matimage = null;
70 
71  public ?ilQTIResponse $response = null;
72 
74 
75  public ?ilQTIOutcomes $outcomes = null;
76 
77  public ?ilQTIDecvar $decvar = null;
78 
80 
81  public ?ilQTISetvar $setvar = null;
82 
84 
86 
90  public array $flow_mat = [];
91 
92  public int $flow = 0;
93 
95 
96  public ?ilQTIMattext $mattext = null;
97 
98  public bool $sametag = false;
99 
100  public string $characterbuffer = "";
101 
103 
104  public int $parser_mode = 0;
105 
106  protected $solutionhint = null;
107  public $solutionhints = [];
108 
112  public array $import_idents = [];
113 
114  public int $qpl_id = 0;
115 
116  public ?int $tst_id = null;
117 
118  public ?ilObjTest $tst_object = null;
119 
120  public bool $do_nothing = false;
121 
122  public int $gap_index = 0;
123 
127  public array $assessments = [];
128 
129  public ?ilQTIAssessment $assessment = null;
130 
132 
133  public ?ilQTIObjectives $objectives = null;
134 
135  public bool $in_assessment = false;
136 
137  public ?ilQTISection $section = null;
138 
142  public array $import_mapping = [];
143 
144  public int $question_counter = 1;
145 
146  public bool $in_itemmetadata = false;
147 
148  public bool $in_objectives = false;
149 
153  public array $founditems = [];
154 
155  public bool $verifyroot = false;
156 
157  public int $verifyqticomment = 0;
158 
159  public int $verifymetadatafield = 0;
160 
161  public int $verifyfieldlabel = 0;
162 
163  public string $verifyfieldlabeltext = "";
164 
165  public int $verifyfieldentry = 0;
166 
167  public string $verifyfieldentrytext = "";
168 
169  protected int $numImportedItems = 0;
170 
172 
173  protected bool $in_prensentation_material = false;
174 
175  protected bool $ignoreItemsEnabled = false;
176 
177  private ?ilQTIMatapplet $matapplet = null;
178 
182  private array $metadata = ["label" => "", "entry" => ""];
183 
185 
186  protected ?string $questionSetType = null;
187 
188  protected \ILIAS\TestQuestionPool\QuestionFilesService $questionfiles;
189  protected array $mappings;
190 
191  public function __construct(
192  ?string $a_xml_file,
193  int $a_mode = self::IL_MO_PARSE_QTI,
194  int $a_qpl_id = 0,
195  $a_import_idents = '',
196  array $mappings = []
197  ) {
198  global $DIC;
199 
200  $this->parser_mode = $a_mode;
201  $this->questionfiles = $DIC->testQuestionPool()->questionFiles();
202  $this->mappings = $mappings;
203  parent::__construct($a_xml_file);
204 
205  $this->qpl_id = $a_qpl_id;
206  $this->lng = $DIC->language();
207  if (is_array($a_import_idents)) {
208  $this->import_idents = &$a_import_idents;
209  }
210 
211  $this->depth = $this->createParserStorage();
212  }
213 
214  public function isIgnoreItemsEnabled(): bool
215  {
217  }
218 
219  public function setIgnoreItemsEnabled(bool $ignoreItemsEnabled): void
220  {
221  $this->ignoreItemsEnabled = $ignoreItemsEnabled;
222  }
223 
224  public function getQuestionSetType(): ?string
225  {
226  return $this->questionSetType;
227  }
228 
229  public function setQuestionSetType(string $questionSetType): void
230  {
231  $this->questionSetType = $questionSetType;
232  }
233 
234  public function setTestObject(ilObjTest $a_tst_object): void
235  {
236  $this->tst_object = $a_tst_object;
237  $this->tst_id = $this->tst_object->getId();
238  }
239 
240  public function getTestObject(): ilObjTest
241  {
242  return $this->tst_object;
243  }
244 
252  public function setHandlers($a_xml_parser): void
253  {
254  xml_set_object($a_xml_parser, $this);
255  xml_set_element_handler($a_xml_parser, 'handlerBeginTag', 'handlerEndTag');
256  xml_set_character_data_handler($a_xml_parser, 'handlerCharacterData');
257  }
258 
259  public function startParsing(): void
260  {
261  $this->question_counter = 1;
262  parent::startParsing();
263  }
264 
268  public function getParent($a_xml_parser): string
269  {
270  if ($this->depth[$a_xml_parser] > 0) {
271  return $this->path[$this->depth[$a_xml_parser] - 1];
272  }
273 
274  return "";
275  }
276 
281  public function handlerBeginTag($a_xml_parser, string $a_name, array $a_attribs): void
282  {
283  switch ($this->parser_mode) {
284  case self::IL_MO_PARSE_QTI:
285  $this->handlerParseBeginTag($a_xml_parser, $a_name, $a_attribs);
286  break;
287  case self::IL_MO_VERIFY_QTI:
288  $this->handlerVerifyBeginTag($a_xml_parser, $a_name, $a_attribs);
289  break;
290  }
291  }
292 
297  public function handlerParseBeginTag($a_xml_parser, string $a_name, array $a_attribs): void
298  {
299  if ($this->do_nothing) {
300  return;
301  }
302  $this->sametag = false;
303  $this->characterbuffer = "";
304  $this->depth[$a_xml_parser] = ($this->depth[$a_xml_parser] ?? 0) + 1; // Issue with SplObjectStorage: Cannot use ++.
305  $this->path[$this->depth[$a_xml_parser]] = strtolower($a_name);
306  $this->qti_element = $a_name;
307 
308  switch (strtolower($a_name)) {
309  case "assessment":
310  $this->assessmentBeginTag($a_attribs);
311  break;
312  case "assessmentcontrol":
313  $this->assessmentControlBeginTag($a_attribs);
314  break;
315  case "objectives":
316  $this->objectives = new ilQTIObjectives();
317  $this->in_objectives = true;
318  break;
319  case 'presentation_material':
320  $this->prensentation_material = new ilQTIPresentationMaterial();
321  $this->in_prensentation_material = true;
322  break;
323  case "section":
324  $this->section = new ilQTISection();
325  break;
326  case "itemmetadata":
327  $this->in_itemmetadata = true;
328  break;
329  case "qtimetadatafield":
330  $this->metadata = ["label" => "", "entry" => ""];
331  break;
332  case "flow":
333  $this->flow++;
334  break;
335  case "flow_mat":
336  $this->flow_mat[] = new ilQTIFlowMat();
337  break;
338  case "itemfeedback":
339  $this->itemFeedbackBeginTag($a_attribs);
340  break;
341  case "displayfeedback":
342  $this->displayFeedbackBeginTag($a_attribs);
343  break;
344  case "setvar":
345  $this->setVarBeginTag($a_attribs);
346  break;
347  case "conditionvar":
348  $this->conditionvar = new ilQTIConditionvar();
349  break;
350  case "not":
351  if ($this->conditionvar != null) {
352  $this->conditionvar->addNot();
353  }
354  break;
355  case "and":
356  if ($this->conditionvar != null) {
357  $this->conditionvar->addAnd();
358  }
359  break;
360  case "or":
361  if ($this->conditionvar != null) {
362  $this->conditionvar->addOr();
363  }
364  break;
365  case "varequal":
366  $this->varEqualBeginTag($a_attribs);
367  break;
368  case "varlt":
369  $this->responsevar = new ilQTIResponseVar(ilQTIResponseVar::RESPONSEVAR_LT);
370  foreach ($a_attribs as $attribute => $value) {
371  switch (strtolower($attribute)) {
372  case "respident":
373  $this->responsevar->setRespident($value);
374  break;
375  case "index":
376  $this->responsevar->setIndex($value);
377  break;
378  }
379  }
380  break;
381  case "varlte":
382  $this->responsevar = new ilQTIResponseVar(ilQTIResponseVar::RESPONSEVAR_LTE);
383  foreach ($a_attribs as $attribute => $value) {
384  switch (strtolower($attribute)) {
385  case "respident":
386  $this->responsevar->setRespident($value);
387  break;
388  case "index":
389  $this->responsevar->setIndex($value);
390  break;
391  }
392  }
393  break;
394  case "vargt":
395  $this->responsevar = new ilQTIResponseVar(ilQTIResponseVar::RESPONSEVAR_GT);
396  foreach ($a_attribs as $attribute => $value) {
397  switch (strtolower($attribute)) {
398  case "respident":
399  $this->responsevar->setRespident($value);
400  break;
401  case "index":
402  $this->responsevar->setIndex($value);
403  break;
404  }
405  }
406  break;
407  case "vargte":
408  $this->responsevar = new ilQTIResponseVar(ilQTIResponseVar::RESPONSEVAR_GTE);
409  foreach ($a_attribs as $attribute => $value) {
410  switch (strtolower($attribute)) {
411  case "respident":
412  $this->responsevar->setRespident($value);
413  break;
414  case "index":
415  $this->responsevar->setIndex($value);
416  break;
417  }
418  }
419  break;
420  case "varsubset":
421  $this->responsevar = new ilQTIResponseVar(ilQTIResponseVar::RESPONSEVAR_SUBSET);
422  foreach ($a_attribs as $attribute => $value) {
423  switch (strtolower($attribute)) {
424  case "respident":
425  $this->responsevar->setRespident($value);
426  break;
427  case "setmatch":
428  $this->responsevar->setSetmatch($value);
429  break;
430  case "index":
431  $this->responsevar->setIndex($value);
432  break;
433  }
434  }
435  break;
436  case "varinside":
437  $this->responsevar = new ilQTIResponseVar(ilQTIResponseVar::RESPONSEVAR_INSIDE);
438  foreach ($a_attribs as $attribute => $value) {
439  switch (strtolower($attribute)) {
440  case "respident":
441  $this->responsevar->setRespident($value);
442  break;
443  case "areatype":
444  $this->responsevar->setAreatype($value);
445  break;
446  case "index":
447  $this->responsevar->setIndex($value);
448  break;
449  }
450  }
451  break;
452  case "varsubstring":
454  foreach ($a_attribs as $attribute => $value) {
455  switch (strtolower($attribute)) {
456  case "case":
457  $this->responsevar->setCase($value);
458  break;
459  case "respident":
460  $this->responsevar->setRespident($value);
461  break;
462  case "index":
463  $this->responsevar->setIndex($value);
464  break;
465  }
466  }
467  break;
468  case "respcondition":
469  $this->respcondition = new ilQTIRespcondition();
470  foreach ($a_attribs as $attribute => $value) {
471  switch (strtolower($attribute)) {
472  case "continue":
473  $this->respcondition->setContinue($value);
474  break;
475  case "title":
476  $this->respcondition->setTitle($value);
477  break;
478  }
479  }
480  break;
481  case "outcomes":
482  $this->outcomes = new ilQTIOutcomes();
483  break;
484  case "decvar":
485  $this->decVarBeginTag($a_attribs);
486  break;
487  case "matimage":
488  $this->matImageBeginTag($a_attribs);
489  break;
490  case "material":
491  $this->materialBeginTag($a_attribs);
492  break;
493  case "mattext":
494  $this->matTextBeginTag($a_attribs);
495  break;
496  case "matapplet":
497  $this->matAppletBeginTag($a_attribs);
498  break;
499  case "questestinterop":
500  $this->hasRootElement = true;
501  break;
502  case "qticomment":
503  break;
504  case "objectbank":
505  // not implemented yet
506  break;
507  case "presentation":
508  $this->in_presentation = true;
509  $this->presentation = new ilQTIPresentation();
510  break;
511  case "response_label":
512  $this->responseLabelBeginTag($a_attribs);
513  break;
514  case "render_choice":
515  $this->renderChoiceBeginTag($a_attribs);
516  break;
517  case "render_hotspot":
518  $this->renderHotspotBeginTag($a_attribs);
519  break;
520  case "render_fib":
521  $this->renderFibBeginTag($a_attribs);
522  break;
523  case "response_lid":
524  // Ordering Terms and Definitions or
525  // Ordering Terms and Pictures or
526  // Multiple choice single response or
527  // Multiple choice multiple response
528  case "response_xy":
529  // Imagemap question
530  case "response_str":
531  // Close question
532  case "response_num":
533  case "response_grp":
534  // Matching terms and definitions
535  // Matching terms and images
536  $this->termsAndDefinitionsBeginTag($a_name, $a_attribs);
537  break;
538  case "item":
539  $this->itemBeginTag($a_attribs);
540  break;
541  case "resprocessing":
542  $this->resprocessingBeginTag($a_attribs);
543  break;
545  $this->solutionhint['points'] = (float) $a_attribs['points'];
546  break;
547  }
548  }
549 
553  public function handlerEndTag($a_xml_parser, string $a_name): void
554  {
555  switch ($this->parser_mode) {
556  case self::IL_MO_PARSE_QTI:
557  $this->handlerParseEndTag($a_xml_parser, $a_name);
558  break;
559  case self::IL_MO_VERIFY_QTI:
560  $this->handlerVerifyEndTag($a_xml_parser, $a_name);
561  break;
562  }
563  }
564 
569  public function handlerParseEndTag($a_xml_parser, string $a_name): void
570  {
571  if ($this->do_nothing && strtolower($a_name) !== "item") {
572  return;
573  }
574  switch (strtolower($a_name)) {
575  case "assessment":
576  if (is_object($this->tst_object)) {
577  $this->tst_object->fromXML($this->assessment, $this->mappings);
578  }
579  $this->in_assessment = false;
580  break;
581  case "assessmentcontrol":
582  $this->assessment->addAssessmentcontrol($this->assessmentcontrol);
583  $this->assessmentcontrol = null;
584  break;
585  case "objectives":
586  if (strtolower($this->getParent($a_xml_parser)) === "assessment") {
587  $this->assessment->addObjectives($this->objectives);
588  }
589  $this->in_objectives = false;
590  break;
591  case 'presentation_material':
592  $this->assessment->setPresentationMaterial($this->prensentation_material);
593  $this->in_prensentation_material = false;
594  break;
595  case "itemmetadata":
596  $this->in_itemmetadata = false;
597  break;
598  case "qtimetadatafield":
599  // handle only specific ILIAS metadata
600  switch ($this->metadata["label"]) {
601  case "ILIAS_VERSION":
602  if ($this->item !== null) {
603  $this->item->setIliasSourceVersion(
604  $this->fetchNumericVersionFromVersionDateString($this->metadata["entry"])
605  );
606  }
607  break;
608  case "QUESTIONTYPE":
609  if ($this->item !== null) {
610  $this->item->setQuestiontype($this->metadata["entry"]);
611  }
612  break;
613  case "AUTHOR":
614  if ($this->item !== null) {
615  $this->item->setAuthor($this->metadata["entry"]);
616  }
617  // no break
618  default:
619  if ($this->item !== null) {
620  $this->item->addMetadata($this->metadata);
621  }
622  break;
623  }
624  if ($this->in_assessment) {
625  $this->assessment->addQtiMetadata($this->metadata);
626  }
627  $this->metadata = ["label" => "", "entry" => ""];
628  break;
629  case "flow":
630  $this->flow--;
631  break;
632  case "flow_mat":
633  if (count($this->flow_mat)) {
634  $flow_mat = array_pop($this->flow_mat);
635  if (count($this->flow_mat)) {
636  $this->flow_mat[count($this->flow_mat) - 1]->addFlowMat($flow_mat);
637  } elseif ($this->in_prensentation_material) {
638  $this->prensentation_material->addFlowMat($flow_mat);
639  } elseif ($this->itemfeedback != null) {
640  $this->itemfeedback->addFlowMat($flow_mat);
641  } elseif ($this->response_label != null) {
642  $this->response_label->addFlow_mat($flow_mat);
643  }
644  }
645  break;
646  case "itemfeedback":
647  if ($this->item != null) {
648  if ($this->itemfeedback != null) {
649  $this->item->addItemfeedback($this->itemfeedback);
650  }
651  }
652  $this->itemfeedback = null;
653  break;
654  case "displayfeedback":
655  if ($this->respcondition != null) {
656  if ($this->displayfeedback != null) {
657  $this->respcondition->addDisplayfeedback($this->displayfeedback);
658  }
659  }
660  $this->displayfeedback = null;
661  break;
662  case "setvar":
663  if ($this->respcondition !== null) {
664  if ($this->setvar != null) {
665  $this->respcondition->addSetvar($this->setvar);
666  }
667  }
668  $this->setvar = null;
669  break;
670  case "conditionvar":
671  if ($this->respcondition !== null) {
672  $this->respcondition->setConditionvar($this->conditionvar);
673  }
674  $this->conditionvar = null;
675  break;
676  case "varequal":
677  case "varlt":
678  case "varlte":
679  case "vargt":
680  case "vargte":
681  case "varsubset":
682  case "varinside":
683  case "varsubstring":
684  if ($this->conditionvar != null) {
685  if ($this->responsevar != null) {
686  $this->conditionvar->addResponseVar($this->responsevar);
687  }
688  }
689  $this->responsevar = null;
690  break;
691  case "respcondition":
692  if ($this->resprocessing != null) {
693  $this->resprocessing->addRespcondition($this->respcondition);
694  }
695  $this->respcondition = null;
696  break;
697  case "outcomes":
698  if ($this->resprocessing != null) {
699  $this->resprocessing->setOutcomes($this->outcomes);
700  }
701  $this->outcomes = null;
702  break;
703  case "decvar":
704  if ($this->outcomes != null) {
705  $this->outcomes->addDecvar($this->decvar);
706  }
707  $this->decvar = null;
708  break;
709  case "presentation":
710  $this->in_presentation = false;
711  if ($this->presentation != null && $this->item != null) {
712  $this->item->setPresentation($this->presentation);
713  }
714  $this->presentation = null;
715  break;
716  case "response_label":
717  if ($this->render_type != null) {
718  $this->render_type->addResponseLabel($this->response_label);
719  $this->response_label = null;
720  }
721  break;
722  case "render_choice":
723  case "render_hotspot":
724  case "render_fib":
725  if ($this->in_response) {
726  if ($this->response != null) {
727  if ($this->render_type != null) {
728  $this->response->setRenderType($this->render_type);
729  $this->render_type = null;
730  }
731  }
732  }
733  break;
734  case "response_lid":
735  case "response_xy":
736  case "response_str":
737  case "response_num":
738  case "response_grp":
739  $this->gap_index++;
740  if ($this->presentation != null) {
741  if ($this->response != null) {
742  $this->presentation->addResponse($this->response);
743  if ($this->item != null) {
744  $this->item->addPresentationitem($this->response);
745  }
746  }
747  }
748  $this->response = null;
749  $this->in_response = false;
750  break;
751  case "item":
752  if ($this->do_nothing) {
753  $this->do_nothing = false;
754  return;
755  }
756  // save the item directly to save memory
757  // the database id's of the created items are exported. if the import fails
758  // ILIAS can delete the already imported items
759 
760  // problems: the object id of the parent questionpool is not yet known. must be set later
761  // the complete flag must be calculated?
762  $qt = $this->item->determineQuestionType();
763  $presentation = $this->item->getPresentation();
764 
766  return;
767  }
768 
769  $question = new $qt();
770  $fbt = str_replace('ass', 'ilAss', $qt) . 'Feedback';
771  $question->feedbackOBJ = new $fbt(
772  $question,
773  $GLOBALS['ilCtrl'],
774  $GLOBALS['ilDB'],
775  $GLOBALS['lng']
776  );
777  $this->import_mapping = $question->fromXML(
778  $this->item,
779  $this->qpl_id,
780  $this->tst_id,
781  $this->tst_object,
782  $this->question_counter,
783  $this->import_mapping,
784  $this->solutionhints
785  );
786 
787  $this->solutionhints = [];
788 
789  $this->numImportedItems++;
790 
791  break;
792  case "material":
793  if ($this->material) {
794  $mat = $this->material->getMaterial(0);
795  if (!is_array($mat)) {
796  $this->material = null;
797  break;
798  }
799  if ($mat["type"] === "mattext" && $mat["material"]->getLabel() === "suggested_solution") {
800  $this->item->addSuggestedSolution($mat["material"], $this->gap_index);
801  }
802  if ($this->in_objectives) {
803  $this->objectives->addMaterial($this->material);
804  } elseif ($this->render_type != null && strtolower($this->getParent($a_xml_parser)) === "render_hotspot") {
805  $this->render_type->addMaterial($this->material);
806  } elseif (count($this->flow_mat) && strtolower($this->getParent($a_xml_parser)) === "flow_mat") {
807  $this->flow_mat[count($this->flow_mat) - 1]->addMaterial($this->material);
808  } elseif ($this->itemfeedback != null) {
809  $this->itemfeedback->addMaterial($this->material);
810  } elseif ($this->response_label != null) {
811  $this->response_label->addMaterial($this->material);
812  } elseif ($this->response != null) {
813  if ($this->response->hasRendering()) {
814  $this->response->setMaterial2($this->material);
815  } else {
816  $this->response->setMaterial1($this->material);
817  }
818  } elseif (($this->in_presentation) && (!$this->in_response)) {
819  if (!is_object($this->item->getQuestiontext())) {
820  $this->item->setQuestiontext($this->material);
821  }
822  $this->presentation->addMaterial($this->material);
823  } elseif ($this->presentation != null) {
824  $this->presentation->addMaterial($this->material);
825  if ($this->item != null) {
826  $this->item->addPresentationitem($this->material);
827  }
828  }
829  }
830  $this->material = null;
831  break;
832  case "matimage":
833 
834  if (!$this->isMatImageAvailable()) {
835  break;
836  }
837 
838  if ($this->virusDetected($this->matimage->getRawContent())) {
839  break;
840  }
841  try {
842  $matImageSecurity = new ilQtiMatImageSecurity($this->matimage, $this->questionfiles);
843  $matImageSecurity->sanitizeLabel();
844  } catch (Exception $e) {
845  break;
846  }
847  if (!$matImageSecurity->validate()) {
848  break;
849  }
850 
851  $this->material->addMatimage($this->matimage);
852  $this->matimage = null;
853  break;
854 
855  // add support for matbreak element
856  case "matbreak":
857  $this->mattext = new ilQTIMattext();
858  $this->mattext->setContent('<br />');
859  $this->material->addMattext($this->mattext);
860  $this->mattext = null;
861  break;
862  case "resprocessing":
863  if ($this->item != null) {
864  $this->item->addResprocessing($this->resprocessing);
865  }
866  $this->resprocessing = null;
867  break;
868  case "mattext":
869  if ($this->material != null) {
870  $this->material->addMattext($this->mattext);
871  }
872  $this->mattext = null;
873  break;
874  case "matapplet":
875  if ($this->material != null) {
876  $this->material->addMatapplet($this->matapplet);
877  }
878  $this->matapplet = null;
879  break;
880 
882  $this->solutionhint['txt'] = $this->characterbuffer;
883  $this->solutionhints[] = $this->solutionhint;
884  break;
885  }
886  $this->depth[$a_xml_parser] -= 1; // Issue with SplObjectStorage: Cannot use --.
887  }
888 
892  public function handlerCharacterData($a_xml_parser, string $a_data): void
893  {
894  switch ($this->parser_mode) {
895  case self::IL_MO_PARSE_QTI:
896  $this->handlerParseCharacterData($a_xml_parser, $a_data);
897  break;
898  case self::IL_MO_VERIFY_QTI:
899  $this->handlerVerifyCharacterData($a_xml_parser, $a_data);
900  break;
901  }
902  }
903 
907  public function handlerParseCharacterData($a_xml_parser, string $a_data): void
908  {
909  if ($this->do_nothing) {
910  return;
911  }
912  $this->characterbuffer .= $a_data;
913  $a_data = $this->characterbuffer;
914  switch ($this->qti_element) {
915  case "fieldlabel":
916  $this->metadata["label"] = $a_data;
917  break;
918  case "fieldentry":
919  $this->metadata["entry"] = $a_data;
920  break;
921  case "response_label":
922  if ($this->response_label != null) {
923  $this->response_label->setContent($a_data);
924  }
925  break;
926  case "setvar":
927  if ($this->setvar != null) {
928  $this->setvar->setContent($a_data);
929  }
930  break;
931  case "displayfeedback":
932  if ($this->displayfeedback != null) {
933  $this->displayfeedback->setContent($a_data);
934  }
935  break;
936  case "varequal":
937  case "varlt":
938  case "varlte":
939  case "vargt":
940  case "vargte":
941  case "varsubset":
942  case "varinside":
943  case "varsubstring":
944  if ($this->responsevar != null) {
945  $this->responsevar->setContent($a_data);
946  }
947  break;
948  case "decvar":
949  if (strlen($a_data)) {
950  if ($this->decvar != null) {
951  $this->decvar->setContent($a_data);
952  }
953  }
954  break;
955  case "mattext":
956  if ($this->mattext != null) {
957  $this->mattext->setContent($a_data);
958  }
959  break;
960  case "matapplet":
961  if ($this->matapplet != null) {
962  $this->matapplet->setContent($a_data);
963  }
964  break;
965  case "matimage":
966  if ($this->matimage != null) {
967  $this->matimage->setContent($a_data);
968  }
969  break;
970  case "duration":
971  switch ($this->getParent($a_xml_parser)) {
972  case "assessment":
973  // to be done
974  break;
975  case "section":
976  // to be done
977  break;
978  case "item":
979  $this->item->setDuration($a_data);
980  break;
981  }
982  break;
983  case "qticomment":
984  switch ($this->getParent($a_xml_parser)) {
985  case "item":
986  $this->item->setComment($a_data);
987  break;
988  case "assessment":
989  $this->assessment->setComment($a_data);
990  break;
991  default:
992  break;
993  }
994  break;
995  }
996  $this->sametag = true;
997  }
998 
1003  public function handlerVerifyBeginTag($a_xml_parser, string $a_name, array $a_attribs): void
1004  {
1005  $this->qti_element = $a_name;
1006 
1007  switch (strtolower($a_name)) {
1008  case "assessment":
1009  $this->assessment = $this->assessments[] = new ilQTIAssessment();
1010  $this->in_assessment = true;
1011  foreach ($a_attribs as $attribute => $value) {
1012  switch (strtolower($attribute)) {
1013  case "title":
1014  $this->assessment->setTitle($value);
1015  break;
1016  case "ident":
1017  $this->assessment->setIdent($value);
1018  break;
1019  }
1020  }
1021  break;
1022  case "questestinterop":
1023  $this->verifyroot = true;
1024  break;
1025  case "qtimetadatafield":
1026  $this->metadata = ["label" => "", "entry" => ""];
1027  $this->verifymetadatafield = 1;
1028  break;
1029  case "fieldlabel":
1030  $this->verifyfieldlabeltext = "";
1031  if ($this->verifymetadatafield == 1) {
1032  $this->verifyfieldlabel = 1;
1033  }
1034  break;
1035  case "fieldentry":
1036  $this->verifyfieldentrytext = "";
1037  if ($this->verifymetadatafield == 1) {
1038  $this->verifyfieldentry = 1;
1039  }
1040  break;
1041  case "item":
1042  $title = "";
1043  foreach ($a_attribs as $attribute => $value) {
1044  switch (strtolower($attribute)) {
1045  case "title":
1046  $title = $value;
1047  break;
1048  }
1049  }
1050  $this->founditems[] = ["title" => "$title", "type" => "", "ident" => $a_attribs["ident"]];
1051  break;
1052  case "response_lid":
1053  if (strlen($this->founditems[count($this->founditems) - 1]["type"]) == 0) {
1054  // test for non ILIAS generated question types
1055  foreach ($a_attribs as $attribute => $value) {
1056  switch (strtolower($attribute)) {
1057  case "rcardinality":
1058  switch (strtolower($value)) {
1059  case "single":
1060  $this->founditems[count($this->founditems) - 1]["type"] = ilQTIItem::QT_MULTIPLE_CHOICE_SR;
1061  break;
1062  case "multiple":
1063  $this->founditems[count($this->founditems) - 1]["type"] = ilQTIItem::QT_MULTIPLE_CHOICE_MR;
1064  break;
1065  case "ordered":
1066  $this->founditems[count($this->founditems) - 1]["type"] = ilQTIItem::QT_ORDERING;
1067  break;
1068  }
1069  break;
1070  }
1071  }
1072  }
1073  break;
1074 
1076  $this->solutionhint = array_map('intval', $a_attribs);
1077  $this->solutionhint['txt'] = '';
1078  break;
1079  case "response_str":
1080  if (strlen($this->founditems[count($this->founditems) - 1]["type"]) == 0) {
1081  // test for non ILIAS generated question types
1082  foreach ($a_attribs as $attribute => $value) {
1083  switch (strtolower($attribute)) {
1084  case "rcardinality":
1085  switch (strtolower($value)) {
1086  case "single":
1087  $this->founditems[count($this->founditems) - 1]["type"] = ilQTIItem::QT_CLOZE;
1088  break;
1089  case "ordered":
1090  $this->founditems[count($this->founditems) - 1]["type"] = ilQTIItem::QT_TEXT;
1091  break;
1092  }
1093  break;
1094  }
1095  }
1096  }
1097  break;
1098  case "response_xy":
1099  if (strlen($this->founditems[count($this->founditems) - 1]["type"]) == 0) {
1100  $this->founditems[count($this->founditems) - 1]["type"] = ilQTIItem::QT_IMAGEMAP;
1101  }
1102  break;
1103  case "response_num":
1104  if (strlen($this->founditems[count($this->founditems) - 1]["type"]) == 0) {
1105  $this->founditems[count($this->founditems) - 1]["type"] = ilQTIItem::QT_NUMERIC;
1106  }
1107  break;
1108  case "response_grp":
1109  if (strlen($this->founditems[count($this->founditems) - 1]["type"]) == 0) {
1110  $this->founditems[count($this->founditems) - 1]["type"] = ilQTIItem::QT_MATCHING;
1111  }
1112  break;
1113  case "qticomment":
1114  // check for "old" ILIAS qti format (not well formed)
1115  $this->verifyqticomment = 1;
1116  break;
1117  case "presentation":
1118  foreach ($a_attribs as $attribute => $value) {
1119  switch (strtolower($attribute)) {
1120  case "label":
1121  $this->founditems[count($this->founditems) - 1]["title"] = $value;
1122  break;
1123  }
1124  }
1125  break;
1126  }
1127  }
1128 
1132  public function handlerVerifyEndTag($a_xml_parser, string $a_name): void
1133  {
1134  switch (strtolower($a_name)) {
1135  case "assessment":
1136  foreach ($this->assessment->qtimetadata as $metaField) {
1137  if ($metaField['label'] == 'question_set_type') {
1138  $this->setQuestionSetType($metaField['entry']);
1139  break;
1140  }
1141 
1142  if ($metaField['label'] == 'random_test') {
1143  if ($metaField['entry'] == 1) {
1145  } else {
1147  }
1148  break;
1149  }
1150  }
1151  $this->in_assessment = false;
1152  break;
1153  case "qticomment":
1154  // check for "old" ILIAS qti format (not well formed)
1155  $this->verifyqticomment = 0;
1156  break;
1157  case "qtimetadatafield":
1158  $this->verifymetadatafield = 0;
1159  if ($this->verifyfieldlabeltext === "QUESTIONTYPE") {
1160  $this->founditems[count($this->founditems) - 1]["type"] = $this->verifyfieldentrytext;
1161  }
1162  if ($this->in_assessment) {
1163  $this->assessment->addQtiMetadata($this->metadata);
1164  }
1165  $this->metadata = ["label" => "", "entry" => ""];
1166  break;
1167  case "fieldlabel":
1168  $this->verifyfieldlabel = 0;
1169  break;
1170  case "fieldentry":
1171  $this->verifyfieldentry = 0;
1172  break;
1173  }
1174  }
1175 
1179  public function handlerVerifyCharacterData($a_xml_parser, string $a_data): void
1180  {
1181  if ($this->verifyqticomment == 1) {
1182  if (preg_match("/Questiontype\=(.*)/", $a_data, $matches)) {
1183  if (count($this->founditems)) {
1184  $this->founditems[count($this->founditems) - 1]["type"] = $matches[1];
1185  }
1186  }
1187  } elseif ($this->verifyfieldlabel == 1) {
1188  $this->verifyfieldlabeltext = $a_data;
1189  } elseif ($this->verifyfieldentry == 1) {
1190  $this->verifyfieldentrytext = $a_data;
1191  }
1192 
1193  switch ($this->qti_element) {
1194  case "fieldlabel":
1195  $this->metadata["label"] = $a_data;
1196  break;
1197  case "fieldentry":
1198  $this->metadata["entry"] = $a_data;
1199  break;
1200  }
1201  }
1202 
1206  public function &getFoundItems(): array
1207  {
1208  return $this->founditems;
1209  }
1210 
1215  public function getImportMapping(): array
1216  {
1217  return $this->import_mapping;
1218  }
1219 
1223  public function getQuestionIdMapping(): array
1224  {
1225  $questionIdMapping = [];
1226 
1227  foreach ($this->getImportMapping() as $k => $v) {
1228  $oldQuestionId = substr($k, strpos($k, 'qst_') + strlen('qst_'));
1229  $newQuestionId = $v['test']; // yes, this is the new question id ^^
1230 
1231  $questionIdMapping[$oldQuestionId] = $newQuestionId;
1232  }
1233 
1234  return $questionIdMapping;
1235  }
1236 
1237  public function setXMLContent(string $a_xml_content): void
1238  {
1239  $a_xml_content = $this->cleanInvalidXmlChars($a_xml_content);
1240 
1241  parent::setXMLContent($a_xml_content);
1242  }
1243 
1247  protected function openXMLFile()
1248  {
1249  $xmlContent = file_get_contents($this->xml_file);
1250  $xmlContent = $this->cleanInvalidXmlChars($xmlContent);
1251  file_put_contents($this->xml_file, $xmlContent);
1252 
1253  return parent::openXMLFile();
1254  }
1255 
1256  protected function fetchNumericVersionFromVersionDateString(string $versionDateString): ?string
1257  {
1258  $matches = null;
1259 
1260  if (preg_match('/^(\d+\.\d+\.\d+)$/', $versionDateString, $matches)) {
1261  return $matches[1];
1262  }
1263 
1264  return null;
1265  }
1266 
1267  protected function fetchSourceNicFromItemIdent(string $itemIdent): ?string
1268  {
1269  $matches = null;
1270 
1271  if (preg_match('/^il_(\d+?)_qst_\d+$/', $itemIdent, $matches)) {
1272  return $matches[1];
1273  }
1274 
1275  return null;
1276  }
1277 
1278  protected function cleanInvalidXmlChars(string $xmlContent): string
1279  {
1280  // http://www.w3.org/TR/xml/#charsets
1281 
1282  // DOES ACTUALLY KILL CONTENT, SHOULD CLEAN NON ESCAPED ILLEGAL CHARS, DON'T KNOW
1283  //$reg = '/[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]/';
1284  //$xmlContent = preg_replace($reg, '', $xmlContent);
1285 
1286  // remove illegal chars escaped to html entities
1287  $needles = [];
1288  for ($i = 0x00, $max = 0x08; $i <= $max; $i += 0x01) {
1289  $needles[] = "&#{$i};";
1290  }
1291  for ($i = 0x0b, $max = 0x0c; $i <= $max; $i += 0x01) {
1292  $needles[] = "&#{$i};";
1293  }
1294  for ($i = 0x0e, $max = 0x1f; $i <= $max; $i += 0x01) {
1295  $needles[] = "&#{$i};";
1296  }
1297  for ($i = 0xd800, $max = 0xdfff; $i <= $max; $i += 0x0001) {
1298  $needles[] = "&#{$i};";
1299  }
1300  for ($i = 0xfffe, $max = 0xffff; $i <= $max; $i += 0x0001) {
1301  $needles[] = "&#{$i};";
1302  }
1303  $reg = '/(' . implode('|', $needles) . ')/';
1304  $xmlContent = preg_replace($reg, '', $xmlContent);
1305 
1306  return $xmlContent;
1307  }
1308 
1309  public function getNumImportedItems(): int
1310  {
1311  return $this->numImportedItems;
1312  }
1313 
1314  protected function isMatImageAvailable(): bool
1315  {
1316  if (!$this->material) {
1317  return false;
1318  }
1319 
1320  if (!$this->matimage) {
1321  return false;
1322  }
1323 
1324  return true;
1325  }
1326 
1327  protected function virusDetected(string $buffer): bool
1328  {
1330 
1331  if ($vs === null) {
1332  return false; // no virus scan, no virus detected
1333  }
1334 
1335  return $vs->scanBuffer($buffer);
1336  }
1337 
1338  private function assessmentBeginTag(array $a_attribs): void
1339  {
1340  $this->assessment = $this->assessments[] = new ilQTIAssessment();
1341  $this->in_assessment = true;
1342  foreach ($a_attribs as $attribute => $value) {
1343  switch (strtolower($attribute)) {
1344  case "title":
1345  $this->assessment->setTitle($value);
1346  break;
1347  case "ident":
1348  $this->assessment->setIdent($value);
1349  break;
1350  }
1351  }
1352  }
1353 
1354  private function assessmentControlBeginTag(array $a_attribs): void
1355  {
1356  $this->assessmentcontrol = new ilQTIAssessmentcontrol();
1357  foreach ($a_attribs as $attribute => $value) {
1358  switch (strtolower($attribute)) {
1359  case "solutionswitch":
1360  $this->assessmentcontrol->setSolutionswitch($value);
1361  break;
1362  case "hintswitch":
1363  $this->assessmentcontrol->setHintswitch($value);
1364  break;
1365  case "feedbackswitch":
1366  $this->assessmentcontrol->setFeedbackswitch($value);
1367  break;
1368  }
1369  }
1370  }
1371 
1372  private function itemFeedbackBeginTag(array $a_attribs): void
1373  {
1374  $this->itemfeedback = new ilQTIItemfeedback();
1375  foreach ($a_attribs as $attribute => $value) {
1376  switch (strtolower($attribute)) {
1377  case "ident":
1378  $this->itemfeedback->setIdent($value);
1379  break;
1380  case "view":
1381  $this->itemfeedback->setView($value);
1382  break;
1383  }
1384  }
1385  }
1386 
1387  private function displayFeedbackBeginTag(array $a_attribs): void
1388  {
1389  $this->displayfeedback = new ilQTIDisplayfeedback();
1390  foreach ($a_attribs as $attribute => $value) {
1391  switch (strtolower($attribute)) {
1392  case "feedbacktype":
1393  $this->displayfeedback->setFeedbacktype($value);
1394  break;
1395  case "linkrefid":
1396  $this->displayfeedback->setLinkrefid($value);
1397  break;
1398  }
1399  }
1400  }
1401 
1402  private function setVarBeginTag(array $a_attribs): void
1403  {
1404  $this->setvar = new ilQTISetvar();
1405  foreach ($a_attribs as $attribute => $value) {
1406  switch (strtolower($attribute)) {
1407  case "action":
1408  $this->setvar->setAction($value);
1409  break;
1410  case "varname":
1411  $this->setvar->setVarname($value);
1412  break;
1413  }
1414  }
1415  }
1416 
1417  private function varEqualBeginTag(array $a_attribs): void
1418  {
1419  $this->responsevar = new ilQTIResponseVar(ilQTIResponseVar::RESPONSEVAR_EQUAL);
1420  foreach ($a_attribs as $attribute => $value) {
1421  switch (strtolower($attribute)) {
1422  case "case":
1423  $this->responsevar->setCase($value);
1424  break;
1425  case "respident":
1426  $this->responsevar->setRespident($value);
1427  break;
1428  case "index":
1429  $this->responsevar->setIndex($value);
1430  break;
1431  }
1432  }
1433  }
1434 
1435  private function termsAndDefinitionsBeginTag(string $a_name, array $a_attribs): void
1436  {
1437  $response_type = 0;
1438  switch (strtolower($a_name)) {
1439  case "response_lid":
1440  $response_type = ilQTIResponse::RT_RESPONSE_LID;
1441  break;
1442  case "response_xy":
1443  $response_type = ilQTIResponse::RT_RESPONSE_XY;
1444  break;
1445  case "response_str":
1446  $response_type = ilQTIResponse::RT_RESPONSE_STR;
1447  break;
1448  case "response_num":
1449  $response_type = ilQTIResponse::RT_RESPONSE_NUM;
1450  break;
1451  case "response_grp":
1452  $response_type = ilQTIResponse::RT_RESPONSE_GRP;
1453  break;
1454  }
1455  $this->in_response = true;
1456  $this->response = new ilQTIResponse($response_type);
1457  $this->response->setFlow($this->flow);
1458  foreach ($a_attribs as $attribute => $value) {
1459  switch (strtolower($attribute)) {
1460  case "ident":
1461  $this->response->setIdent($value);
1462  break;
1463  case "rtiming":
1464  $this->response->setRTiming($value);
1465  break;
1466  case "rcardinality":
1467  $this->response->setRCardinality($value);
1468  break;
1469  case "numtype":
1470  $this->response->setNumtype($value);
1471  break;
1472  }
1473  }
1474  }
1475 
1476  private function itemBeginTag(array $a_attribs): void
1477  {
1478  $this->gap_index = 0;
1479  $this->item = $this->items[] = new ilQTIItem();
1480  foreach ($a_attribs as $attribute => $value) {
1481  switch (strtolower($attribute)) {
1482  case "ident":
1483  $this->item->setIdent($value);
1484  $this->item->setIliasSourceNic(
1485  $this->fetchSourceNicFromItemIdent($value)
1486  );
1487  if ($this->isIgnoreItemsEnabled()) {
1488  $this->do_nothing = true;
1489  } elseif (count($this->import_idents) > 0) {
1490  if (!in_array($value, $this->import_idents)) {
1491  $this->do_nothing = true;
1492  }
1493  }
1494  break;
1495  case "title":
1496  $this->item->setTitle($value);
1497  break;
1498  case "maxattempts":
1499  $this->item->setMaxattempts($value);
1500  break;
1501  }
1502  }
1503  }
1504 
1505  private function resprocessingBeginTag(array $a_attribs): void
1506  {
1507  $this->resprocessing = new ilQTIResprocessing();
1508  foreach ($a_attribs as $attribute => $value) {
1509  switch (strtolower($attribute)) {
1510  case "scoremodel":
1511  $this->resprocessing->setScoremodel($value);
1512  break;
1513  }
1514  }
1515  }
1516 
1517  private function renderFibBeginTag(array $a_attribs): void
1518  {
1519  if (!$this->in_response) {
1520  return;
1521  }
1522  $this->render_type = new ilQTIRenderFib();
1523  foreach ($a_attribs as $attribute => $value) {
1524  switch (strtolower($attribute)) {
1525  case "encoding":
1526  $this->render_type->setEncoding($value);
1527  break;
1528  case "fibtype":
1529  $this->render_type->setFibtype($value);
1530  break;
1531  case "rows":
1532  $this->render_type->setRows($value);
1533  break;
1534  case "maxchars":
1535  $this->render_type->setMaxchars($value);
1536  break;
1537  case "prompt":
1538  $this->render_type->setPrompt($value);
1539  break;
1540  case "columns":
1541  $this->render_type->setColumns($value);
1542  break;
1543  case "charset":
1544  $this->render_type->setCharset($value);
1545  break;
1546  case "maxnumber":
1547  $this->render_type->setMaxnumber($value);
1548  break;
1549  case "minnumber":
1550  $this->render_type->setMinnumber($value);
1551  break;
1552  }
1553  }
1554  }
1555 
1556  private function renderHotspotBeginTag(array $a_attribs): void
1557  {
1558  if (!$this->in_response) {
1559  return;
1560  }
1561  $this->render_type = new ilQTIRenderHotspot();
1562  foreach ($a_attribs as $attribute => $value) {
1563  switch (strtolower($attribute)) {
1564  case "showdraw":
1565  $this->render_type->setShowdraw($value);
1566  break;
1567  case "minnumber":
1568  $this->render_type->setMinnumber($value);
1569  break;
1570  case "maxnumber":
1571  $this->render_type->setMaxnumber($value);
1572  break;
1573  }
1574  }
1575  }
1576 
1577  private function renderChoiceBeginTag(array $a_attribs): void
1578  {
1579  if (!$this->in_response) {
1580  return;
1581  }
1582  $this->render_type = new ilQTIRenderChoice();
1583  foreach ($a_attribs as $attribute => $value) {
1584  switch (strtolower($attribute)) {
1585  case "shuffle":
1586  $this->render_type->setShuffle($value);
1587  break;
1588  case 'minnumber':
1589  $this->render_type->setMinnumber($value);
1590  break;
1591  case 'maxnumber':
1592  $this->render_type->setMaxnumber($value);
1593  break;
1594  }
1595  }
1596  }
1597 
1598  private function responseLabelBeginTag(array $a_attribs): void
1599  {
1600  if ($this->render_type == null) {
1601  return;
1602  }
1603  $this->response_label = new ilQTIResponseLabel();
1604  foreach ($a_attribs as $attribute => $value) {
1605  switch (strtolower($attribute)) {
1606  case "rshuffle":
1607  $this->response_label->setRshuffle($value);
1608  break;
1609  case "rarea":
1610  $this->response_label->setRarea($value);
1611  break;
1612  case "rrange":
1613  $this->response_label->setRrange($value);
1614  break;
1615  case "labelrefid":
1616  $this->response_label->setLabelrefid($value);
1617  break;
1618  case "ident":
1619  $this->response_label->setIdent($value);
1620  break;
1621  case "match_group":
1622  $this->response_label->setMatchGroup($value);
1623  break;
1624  case "match_max":
1625  $this->response_label->setMatchMax($value);
1626  break;
1627  }
1628  }
1629  }
1630 
1631  private function matAppletBeginTag(array $a_attribs): void
1632  {
1633  $this->matapplet = new ilQTIMatapplet();
1634  foreach ($a_attribs as $attribute => $value) {
1635  switch (strtolower($attribute)) {
1636  case "label":
1637  $this->matapplet->setLabel($value);
1638  break;
1639  case "uri":
1640  $this->matapplet->setUri($value);
1641  break;
1642  case "y0":
1643  $this->matapplet->setY0($value);
1644  break;
1645  case "height":
1646  $this->matapplet->setHeight($value);
1647  break;
1648  case "width":
1649  $this->matapplet->setWidth($value);
1650  break;
1651  case "x0":
1652  $this->matapplet->setX0($value);
1653  break;
1654  case "embedded":
1655  $this->matapplet->setEmbedded($value);
1656  break;
1657  case "entityref":
1658  $this->matapplet->setEntityref($value);
1659  break;
1660  }
1661  }
1662  }
1663 
1664  private function matTextBeginTag(array $a_attribs): void
1665  {
1666  $this->mattext = new ilQTIMattext();
1667  foreach ($a_attribs as $attribute => $value) {
1668  switch (strtolower($attribute)) {
1669  case "texttype":
1670  $this->mattext->setTexttype($value);
1671  break;
1672  case "label":
1673  $this->mattext->setLabel($value);
1674  break;
1675  case "charset":
1676  $this->mattext->setCharset($value);
1677  break;
1678  case "uri":
1679  $this->mattext->setUri($value);
1680  break;
1681  case "xml:space":
1682  $this->mattext->setXmlspace($value);
1683  break;
1684  case "xml:lang":
1685  $this->mattext->setXmllang($value);
1686  break;
1687  case "entityref":
1688  $this->mattext->setEntityref($value);
1689  break;
1690  case "height":
1691  $this->mattext->setHeight($value);
1692  break;
1693  case "width":
1694  $this->mattext->setWidth($value);
1695  break;
1696  case "x0":
1697  $this->mattext->setX0($value);
1698  break;
1699  case "y0":
1700  $this->mattext->setY0($value);
1701  break;
1702  }
1703  }
1704  }
1705 
1706  private function materialBeginTag(array $a_attribs): void
1707  {
1708  $this->material = new ilQTIMaterial();
1709  $this->material->setFlow($this->flow);
1710  foreach ($a_attribs as $attribute => $value) {
1711  switch (strtolower($attribute)) {
1712  case "label":
1713  $this->material->setLabel($value);
1714  break;
1715  }
1716  }
1717  }
1718 
1719  private function matImageBeginTag(array $a_attribs): void
1720  {
1721  $this->matimage = new ilQTIMatimage();
1722  foreach ($a_attribs as $attribute => $value) {
1723  switch (strtolower($attribute)) {
1724  case "imagtype":
1725  $this->matimage->setImagetype($value);
1726  break;
1727  case "label":
1728  $this->matimage->setLabel($value);
1729  break;
1730  case "height":
1731  $this->matimage->setHeight($value);
1732  break;
1733  case "width":
1734  $this->matimage->setWidth($value);
1735  break;
1736  case "uri":
1737  $this->matimage->setUri($value);
1738  break;
1739  case "embedded":
1740  $this->matimage->setEmbedded($value);
1741  break;
1742  case "x0":
1743  $this->matimage->setX0($value);
1744  break;
1745  case "y0":
1746  $this->matimage->setY0($value);
1747  break;
1748  case "entityref":
1749  $this->matimage->setEntityref($value);
1750  break;
1751  }
1752  }
1753  if (!$this->matimage->getEmbedded() && strlen($this->matimage->getUri())) {
1754  $img_string = @file_get_contents(dirname($this->xml_file) . '/' . $this->matimage->getUri());
1755 
1756  if (is_string($img_string)) {
1757  $this->matimage->setContent($img_string);
1758  }
1759  }
1760  }
1761 
1762  private function decVarBeginTag(array $a_attribs): void
1763  {
1764  $this->decvar = new ilQTIDecvar();
1765  foreach ($a_attribs as $attribute => $value) {
1766  switch (strtolower($attribute)) {
1767  case "varname":
1768  $this->decvar->setVarname($value);
1769  break;
1770  case "vartype":
1771  $this->decvar->setVartype($value);
1772  break;
1773  case "defaultval":
1774  $this->decvar->setDefaultval($value);
1775  break;
1776  case "minvalue":
1777  $this->decvar->setMinvalue($value);
1778  break;
1779  case "maxvalue":
1780  $this->decvar->setMaxvalue($value);
1781  break;
1782  case "members":
1783  $this->decvar->setMembers($value);
1784  break;
1785  case "cutvalue":
1786  $this->decvar->setCutvalue($value);
1787  break;
1788  }
1789  }
1790  }
1791 
1795  private function createParserStorage()
1796  {
1797  $parser = xml_parser_create();
1798  $is_resource = is_resource($parser);
1799  xml_parser_free($parser);
1800 
1801  return $is_resource ? [] : new SplObjectStorage();
1802  }
1803 }
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setHandlers($a_xml_parser)
set event handler should be overwritten by inherited class private
ilQTIObjectives $objectives
renderHotspotBeginTag(array $a_attribs)
string $verifyfieldlabeltext
const QT_NUMERIC
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
ilQTIResponse $response
itemBeginTag(array $a_attribs)
matTextBeginTag(array $a_attribs)
ilQTIRespcondition $respcondition
cleanInvalidXmlChars(string $xmlContent)
ilQTIAssessmentcontrol $assessmentcontrol
handlerVerifyCharacterData($a_xml_parser, string $a_data)
const QUESTION_SET_TYPE_RANDOM
assessmentBeginTag(array $a_attribs)
decVarBeginTag(array $a_attribs)
ilQTISetvar $setvar
ilQTIMatimage $matimage
ilQTIPresentationMaterial $prensentation_material
itemFeedbackBeginTag(array $a_attribs)
handlerParseBeginTag($a_xml_parser, string $a_name, array $a_attribs)
ilQTIMaterial $material
const QT_ORDERING
setXMLContent(string $a_xml_content)
renderChoiceBeginTag(array $a_attribs)
ilQTIResprocessing $resprocessing
ilQTISection $section
resprocessingBeginTag(array $a_attribs)
ilQTIAssessment $assessment
termsAndDefinitionsBeginTag(string $a_name, array $a_attribs)
setVarBeginTag(array $a_attribs)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
matImageBeginTag(array $a_attribs)
ilQTIMattext $mattext
ILIAS TestQuestionPool QuestionFilesService $questionfiles
displayFeedbackBeginTag(array $a_attribs)
static isImportable($questionTypeTag)
global $DIC
Definition: feed.php:28
bool $in_prensentation_material
assessmentControlBeginTag(array $a_attribs)
fetchSourceNicFromItemIdent(string $itemIdent)
ilQTIItemfeedback $itemfeedback
handlerParseEndTag($a_xml_parser, string $a_name)
NotOptimalIfConditionsInspection
setIgnoreItemsEnabled(bool $ignoreItemsEnabled)
__construct(VocabulariesInterface $vocabularies)
ilQTIOutcomes $outcomes
handlerBeginTag($a_xml_parser, string $a_name, array $a_attribs)
ilQTIResponseLabel $response_label
responseLabelBeginTag(array $a_attribs)
handlerVerifyEndTag($a_xml_parser, string $a_name)
$GLOBALS["DIC"]
Definition: wac.php:31
ilQTIPresentation $presentation
const QT_MULTIPLE_CHOICE_SR
virusDetected(string $buffer)
fetchNumericVersionFromVersionDateString(string $versionDateString)
matAppletBeginTag(array $a_attribs)
setQuestionSetType(string $questionSetType)
handlerVerifyBeginTag($a_xml_parser, string $a_name, array $a_attribs)
handlerCharacterData($a_xml_parser, string $a_data)
__construct(?string $a_xml_file, int $a_mode=self::IL_MO_PARSE_QTI, int $a_qpl_id=0, $a_import_idents='', array $mappings=[])
const QT_MULTIPLE_CHOICE_MR
ilObjTest $tst_object
ilQTIResponseVar $responsevar
const QT_MATCHING
ilQTIConditionvar $conditionvar
string $verifyfieldentrytext
ilQTIDisplayfeedback $displayfeedback
getImportMapping()
Get array of new created questions for import id.
const QUESTION_SET_TYPE_FIXED
ilQTIDecvar $decvar
varEqualBeginTag(array $a_attribs)
getParent($a_xml_parser)
const QT_IMAGEMAP
renderFibBeginTag(array $a_attribs)
handlerEndTag($a_xml_parser, string $a_name)
ilQTIMatapplet $matapplet
handlerParseCharacterData($a_xml_parser, string $a_data)
materialBeginTag(array $a_attribs)
setTestObject(ilObjTest $a_tst_object)