ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
class.ilQuestionPageParser.php
Go to the documentation of this file.
1 <?php
2 
20 
28 {
32  protected bool $in_properties;
36  protected bool $in_glossary_definition = false;
37  protected array $media_meta_cache = [];
38  protected bool $media_meta_start = false;
39  protected array $pg_mapping = [];
40  protected array $mobs_with_int_links = [];
41  protected bool $inside_code = false;
42  protected string $content_type = '';
43  public ilTree $tree;
44  public array $cnt = []; // counts open elements
45  public array $current_element = []; // store current element type
46  public ?ilObjLearningModule $learning_module = null; // current learning module
47  public ?ilPageObject $page_object = null; // current page object
49  public array $structure_objects = []; // array of current structure objects
51  public ?object $current_object = null; // at the time a LearningModule, PageObject or StructureObject
52  public ?ilLMTree $lm_tree = null;
53  public array $pg_into_tree = [];
54  public array $st_into_tree = [];
55  public array $container = [];
56  public bool $in_page_object = false; // are we currently within a PageObject? true/false
57  public bool $in_meta_data = false; // are we currently within MetaData? true/false
58  public bool $in_media_object = false;
59  public bool $in_file_item = false;
60  public bool $in_glossary = false;
61  public bool $in_map_area = false;
63  public ?ilObjFile $file_item = null;
64  public array $pages_to_parse = [];
65  public array $mob_mapping = [];
66  public array $file_item_mapping = [];
67  public ?ilMediaItem $media_item = null; // current media item
68  public string $loc_type = ''; // current location type
69  public ?ilMapArea $map_area = null; // current map area
70  public array $link_targets = []; // stores all objects by import id
71  public array $qst_mapping = [];
72  public bool $metadata_parsing_disabled = false;
73  public bool $in_meta_meta_data = false;
74  protected array $glossary_term_map = [];
75  protected ilLogger $log;
76  protected ?ilGlossaryTerm $glossary_term = null;
77  protected ?ilImportMapping $mapping = null;
78  protected string $cur_qid = '';
79  protected string $chr_data = '';
80  protected bool $in_media_item = false;
81 
82  public function __construct(
83  private readonly ilObject $content_object,
84  string $xml_file,
85  private readonly string $importdir
86  ) {
87  global $DIC;
88  $lng = $DIC->language();
89  $tree = $DIC->repositoryTree();
90 
91  $this->log = ilLoggerFactory::getLogger('lm');
92 
93  parent::__construct($xml_file);
94  $this->cnt = [];
95  $this->current_element = [];
96  $this->structure_objects = [];
97  $this->st_into_tree = [];
98  $this->pg_into_tree = [];
99  $this->pages_to_parse = [];
100  $this->mobs_with_int_links = [];
101  $this->mob_mapping = [];
102  $this->file_item_mapping = [];
103  $this->pg_mapping = [];
104  $this->link_targets = [];
105  $this->lng = $lng;
106  $this->tree = $tree;
107  $this->inside_code = false;
108  $this->qst_mapping = [];
109  $this->content_type = $this->content_object->getType();
110  $this->metadata_parsing_disabled = false;
111 
112  if ($this->content_type !== 'tst' && $this->content_type !== 'qpl') {
113  $this->lm_tree = new ilLMTree($this->content_object->getId());
114  }
115  }
116 
120  public function setHandlers($xml_parser): void
121  {
122  xml_set_object($xml_parser, $this);
123  xml_set_element_handler($xml_parser, 'handlerBeginTag', 'handlerEndTag');
124  xml_set_character_data_handler($xml_parser, 'handlerCharacterData');
125  }
126 
127  public function setImportMapping(ilImportMapping $mapping = null): void
128  {
129  $this->mapping = $mapping;
130  }
131 
132  public function startParsing(): void
133  {
134  $this->log->debug('start');
135 
136  parent::startParsing();
137  $this->storeTree();
138  $this->processPagesToParse();
139  $this->copyFileItems();
140  }
141 
145  public function storeTree(): void
146  {
147  $ilLog = $this->log;
148 
149  foreach ($this->st_into_tree as $st) {
150  $this->lm_tree->insertNode($st['id'], $st['parent']);
151  if (is_array($this->pg_into_tree[$st['id']])) {
152  foreach ($this->pg_into_tree[$st['id']] as $pg) {
153  $pg_id = 0;
154  switch ($pg['type']) {
155  case 'pg_alias':
156  if ($this->pg_mapping[$pg['id']] == '') {
157  $ilLog->write('LM Import: No PageObject for PageAlias ' .
158  $pg['id'] . ' found! (Please update export installation to ILIAS 3.3.0)');
159 
160  // Jump two levels up. First level is switch
161  continue 2;
162  }
163  $pg_id = $this->pg_mapping[$pg['id']];
164  break;
165 
166  case 'pg':
167  $pg_id = $pg['id'];
168  break;
169  }
170  if (!$this->lm_tree->isInTree($pg_id)) {
171  $this->lm_tree->insertNode($pg_id, $st['id']);
172  }
173  }
174  }
175  }
176  }
177 
181  public function processPagesToParse(): void
182  {
183  // outgoin internal links
184  foreach ($this->pages_to_parse as $page_id) {
185  $page_arr = explode(':', $page_id);
186  $page_obj = null;
187  switch ($page_arr[0]) {
188  case 'lm':
189  switch ($this->content_object->getType()) {
190  case 'lm':
191  $page_obj = new ilLMPage($page_arr[1]);
192  break;
193 
194  default:
195  die('Unknown content type ' . $this->content_object->getType());
196  }
197 
198  break;
199 
200  case 'term':
201  $page_obj = new ilGlossaryDefPage($page_arr[1]);
202  break;
203 
204  case 'qpl':
205  $page_obj = new ilAssQuestionPage($page_arr[1]);
206  break;
207  }
208  $page_obj->buildDom();
209  $page_obj->resolveIntLinks();
210  $page_obj->resolveIIMMediaAliases($this->mob_mapping);
211  if ($this->content_type == 'lm') {
212  $page_obj->resolveQuestionReferences($this->qst_mapping);
213  }
214  $page_obj->update(false);
215 
216  if ($page_arr[0] == 'term') {
217  $term = new ilGlossaryTerm($page_arr[1]);
218  $term->updateShortText();
219  }
220 
221  unset($page_obj);
222  }
223 
224  foreach ($this->mobs_with_int_links as $mob_id) {
226  }
227 
228  $done = [];
229  foreach ($this->link_targets as $link_target) {
230  $parsed = $this->parseLinkTarget((string) $link_target);
231  if (!isset($parsed)) {
232  continue;
233  }
234  //echo "doin link target:".$link_target.":<br>";
236  $parsed['target_type'],
237  $parsed['target_id'],
238  $parsed['target_inst']
239  );
240  foreach ($sources as $key => $source) {
241  if (in_array($key, $done)) {
242  continue;
243  }
244  $type_arr = explode(':', $source['type']);
245 
246  // content object pages
247  if ($type_arr[1] == 'pg') {
248  if (ilPageObject::_exists($type_arr[0], $source['id'])) {
249  $page_object = ilPageObjectFactory::getInstance($type_arr[0], $source['id']);
250  $page_object->buildDom();
251  $page_object->resolveIntLinks();
252  $page_object->update();
253  unset($page_object);
254  }
255  }
256 
257  // eventually correct links in questions to learning modules
258  if ($type_arr[0] === 'qst') {
259  assQuestion::instantiateQuestion($source['id'])->resolveSuggestedSolutionLinks();
260  }
261  $done[$key] = $key;
262  }
263  }
264  }
265 
269  private function copyFileItems(): void
270  {
271  foreach ($this->file_item_mapping as $origin_id => $file_id) {
272  if (empty($origin_id)) {
273  continue;
274  }
275  $obj_dir = $origin_id;
276  $source_dir = $this->importdir . DIRECTORY_SEPARATOR . 'objects' . DIRECTORY_SEPARATOR . $obj_dir;
277 
278  $file_obj = new ilObjFile($file_id, false);
279  if (is_dir($source_dir)) {
280  $files = scandir($source_dir, SCANDIR_SORT_DESCENDING);
281  if ($files !== false && $files !== [] && is_file($source_dir . '/' . $files[0])) {
282  $file = fopen($source_dir . '/' . $files[0], 'rb');
283  $file_stream = Streams::ofResource($file);
284  $file_obj->appendStream($file_stream, $files[0]);
285  }
286  }
287  $file_obj->update();
288  }
289  }
290 
295  public function setQuestionMapping(array $a_map): void
296  {
297  $this->qst_mapping = $a_map;
298  }
299 
300  public function beginElement(string $a_name): void
301  {
302  if (!isset($this->status['$a_name'])) {
303  $this->cnt[$a_name] = 1;
304  } else {
305  $this->cnt[$a_name]++;
306  }
307  $this->current_element[count($this->current_element)] = $a_name;
308  }
309 
310  public function endElement(string $a_name): void
311  {
312  $this->cnt[$a_name]--;
313  unset($this->current_element[count($this->current_element) - 1]);
314  }
315 
316  public function getCurrentElement(): string
317  {
318  return ($this->current_element[count($this->current_element) - 1] ?? '');
319  }
320 
321  public function getOpenCount(string $a_name): int
322  {
323  if (isset($this->cnt[$a_name])) {
324  return $this->cnt[$a_name];
325  }
326  return 0;
327  }
328 
329  public function buildTag(
330  string $type,
331  string $name,
332  array $attr = []
333  ): string {
334  $tag = '<';
335 
336  if ($type == 'end') {
337  $tag .= '/';
338  }
339 
340  $tag .= $name;
341 
342  if (is_array($attr)) {
343  foreach ($attr as $k => $v) {
344  $tag .= ' ' . $k . "='$v'";
345  }
346  }
347 
348  $tag .= '>';
349 
350  return $tag;
351  }
352 
353  public function handlerBeginTag($a_xml_parser, string $a_name, array $a_attribs): void
354  {
355  switch ($a_name) {
356  case 'ContentObject':
357  $this->current_object = $this->content_object;
358  if ($a_attribs['Type'] == 'Glossary') {
359  $this->glossary_object = $this->content_object;
360  }
361  break;
362 
363  case 'StructureObject':
365  $lm = $this->content_object;
366  $this->structure_objects[count($this->structure_objects)]
367  = new ilStructureObject($lm);
368  $this->current_object = $this->structure_objects[count($this->structure_objects) - 1];
369  $this->current_object->setLMId($this->content_object->getId());
370  // new meta data handling: we create the structure
371  // object already here, this should also create a
372  // md entry
373  $this->current_object->create(true);
374  break;
375 
376  case 'PageObject':
377  $this->in_page_object = true;
378  $this->cur_qid = '';
379  if ($this->content_type !== 'tst' && $this->content_type !== 'qpl') {
381  $lm = $this->content_object;
382  $this->lm_page_object = new ilLMPageObject($lm);
383  $this->page_object = new ilLMPage();
384  $this->lm_page_object->setLMId($this->content_object->getId());
385  $this->lm_page_object->assignPageObject($this->page_object);
386  $this->current_object = $this->lm_page_object;
387  } else {
388  $this->page_object = new ilAssQuestionPage();
389  }
390  break;
391 
392  case 'PageAlias':
393  throw new ilLMException('Page Alias not supported.');
394 
395  case 'MediaObject':
396  case 'InteractiveImage':
397  if ($a_name == 'MediaObject') {
398  $this->in_media_object = true;
399  }
400  $this->media_meta_start = true;
401  $this->media_meta_cache = [];
402  $this->media_object = new ilObjMediaObject();
403  $this->media_object->create(true, false);
404  break;
405 
406  case 'MediaAlias':
407  $this->media_object->setAlias(true);
408  $this->media_object->setImportId($a_attribs['OriginId']);
409  $this->mob_mapping[$this->media_object->getImportId()] = $this->media_object->getId();
410  if (is_object($this->page_object)) {
411  $this->page_object->needsImportParsing(true);
412  }
413  break;
414 
415  case 'MediaItem':
416  case 'MediaAliasItem':
417  $this->in_media_item = true;
418  $this->media_item = new ilMediaItem();
419  $this->media_item->setPurpose($a_attribs['Purpose']);
420  break;
421 
422  case 'Layout':
423  if (is_object($this->media_object) && $this->in_media_object) {
424  $this->media_item->setWidth($a_attribs['Width'] ?? '');
425  $this->media_item->setHeight($a_attribs['Height'] ?? '');
426  $this->media_item->setHAlign($a_attribs['HorizontalAlign'] ?? '');
427  }
428  break;
429 
430  case 'Parameter':
431  if (is_object($this->media_object) && $this->in_media_object) {
432  $this->media_item->setParameter($a_attribs['Name'], $a_attribs['Value']);
433  }
434  break;
435 
436  case 'MapArea':
437  $this->in_map_area = true;
438  $this->map_area = new ilMapArea();
439  $this->map_area->setShape($a_attribs['Shape']);
440  $this->map_area->setCoords($a_attribs['Coords']);
441  $this->map_area->setHighlightMode($a_attribs['HighlightMode']);
442  $this->map_area->setHighlightClass($a_attribs['HighlightClass']);
443  break;
444 
445  case 'Glossary':
446  $this->in_glossary = true;
447  if ($this->content_object->getType() != 'glo') {
448  $this->glossary_object = new ilObjGlossary();
449  $this->glossary_object->setTitle('');
450  $this->glossary_object->setDescription('');
451  $this->glossary_object->create(true);
452  $this->glossary_object->createReference();
453  $parent = $this->tree->getParentNodeData($this->content_object->getRefId());
454  $this->glossary_object->putInTree($parent['child']);
455  $this->glossary_object->setPermissions($parent['child']);
456  }
457  $this->current_object = $this->glossary_object;
458  break;
459 
460  case 'GlossaryItem':
461  $this->glossary_term = new ilGlossaryTerm();
462  $this->glossary_term->setGlossaryId($this->glossary_object->getId());
463  $this->glossary_term->setLanguage($a_attribs['Language']);
464  $this->glossary_term->setImportId($a_attribs['Id']);
465  $parsed = $this->parseLinkTarget((string) $a_attribs['Id']);
466  if (isset($parsed)) {
467  $this->link_targets[$a_attribs["Id"]] = $a_attribs['Id'];
468  }
469 
470  // no break
471  case 'Definition':
472  $this->in_glossary_definition = true;
473  $this->page_object = new ilGlossaryDefPage();
474  $this->page_object->setParentId($this->glossary_term->getGlossaryId());
475  $this->glossary_term->assignPageObject($this->page_object);
476  $this->current_object = $this->glossary_term;
477  // see bug #12465, we need to clear xml after creation, since it will be <PageObject></PageObject>
478  // otherwise, and appendXML calls will lead to '<PageObject></PageObject><PageObject>....</PageObject>'
479  $this->page_object->setXMLContent('');
480  break;
481 
482  case 'FileItem':
483  $this->in_file_item = true;
484  $this->file_item = new ilObjFile();
485  $this->file_item->setTitle('dummy');
486  $this->file_item->setMode('filelist');
487  if (is_object($this->page_object)) {
488  $this->page_object->needsImportParsing(true);
489  }
490  break;
491 
492  case 'Paragraph':
493  if ($a_attribs['Characteristic'] == 'Code') {
494  $this->inside_code = true;
495  }
496  break;
497 
498  case 'Properties':
499  $this->in_properties = true;
500  break;
501 
502  case 'Property':
503  if ($this->content_object->getType() == 'lm') {
504  switch ($a_attribs['Name']) {
505  case 'Layout':
506  $this->content_object->setLayout($a_attribs['Value']);
507  break;
508 
509  case 'PageHeader':
510  $this->content_object->setPageHeader($a_attribs['Value']);
511  break;
512 
513  case 'TOCMode':
514  $this->content_object->setTOCMode($a_attribs['Value']);
515  break;
516 
517  case 'ActiveLMMenu':
518  $this->content_object->setActiveLMMenu(
519  ilUtil::yn2tf($a_attribs['Value'])
520  );
521  break;
522 
523  case 'ActiveNumbering':
524  $this->content_object->setActiveNumbering(
525  ilUtil::yn2tf($a_attribs['Value'])
526  );
527  break;
528 
529  case 'ActiveTOC':
530  $this->content_object->setActiveTOC(
531  ilUtil::yn2tf($a_attribs['Value'])
532  );
533  break;
534 
535  case 'ActivePrintView':
536  $this->content_object->setActivePrintView(
537  ilUtil::yn2tf($a_attribs['Value'])
538  );
539  break;
540 
541  case 'CleanFrames':
542  $this->content_object->setCleanFrames(
543  ilUtil::yn2tf($a_attribs['Value'])
544  );
545  break;
546 
547  case 'PublicNotes':
548  $this->content_object->setPublicNotes(
549  ilUtil::yn2tf($a_attribs['Value'])
550  );
551  break;
552 
553  case 'HistoryUserComments':
554  $this->content_object->setHistoryUserComments(
555  ilUtil::yn2tf($a_attribs['Value'])
556  );
557  break;
558 
559  case 'Rating':
560  $this->content_object->setRating(
561  ilUtil::yn2tf($a_attribs['Value'])
562  );
563  break;
564 
565  case 'RatingPages':
566  $this->content_object->setRatingPages(
567  ilUtil::yn2tf($a_attribs['Value'])
568  );
569  break;
570 
571  case 'HeaderPage':
572  if ($a_attribs['Value'] != '') {
573  if ($this->pg_mapping[$a_attribs['Value']] > 0) {
574  $this->content_object->setHeaderPage(
575  $this->pg_mapping[$a_attribs['Value']]
576  );
577  }
578  }
579  break;
580 
581  case 'FooterPage':
582  if ($a_attribs['Value'] != '') {
583  if ($this->pg_mapping[$a_attribs['Value']] > 0) {
584  $this->content_object->setFooterPage(
585  $this->pg_mapping[$a_attribs['Value']]
586  );
587  }
588  }
589  break;
590 
591  case 'LayoutPerPage':
592  $this->content_object->setLayoutPerPage($a_attribs['Value']);
593  break;
594 
595  case 'ProgressIcons':
596  $this->content_object->setProgressIcons($a_attribs['Value']);
597  break;
598 
599  case 'StoreTries':
600  $this->content_object->setStoreTries($a_attribs['Value']);
601  break;
602 
603  case 'RestrictForwardNavigation':
604  $this->content_object->setRestrictForwardNavigation($a_attribs['Value']);
605  break;
606 
607  case 'DisableDefaultFeedback':
608  $this->content_object->setDisableDefaultFeedback($a_attribs['Value']);
609  break;
610  }
611  }
612  break;
613 
617  case 'MetaData':
618  $this->in_meta_data = true;
619  // media obejct meta data handling
620  // is done in the 'Identifier' begin tag processing
621  // the rest is done here
622  if (!$this->in_media_object) {
623  if ($this->content_type != 'tst' && $this->content_type != 'qpl') {
624  // type pg/st
625  if ($this->current_object->getType() == 'st'
626  || $this->current_object->getType() == 'pg') {
627  // late creation of page object
628  if ($this->current_object->getType() == 'pg') {
629  $this->lm_page_object->create(true);
630  }
631  $this->md = new ilMD(
632  $this->content_object->getId(),
633  $this->current_object->getId(),
634  $this->current_object->getType()
635  );
636  }
637  // type term
638  elseif ($this->current_object->getType() == 'term') {
639  $this->md = new ilMD(
640  $this->glossary_object->getId(),
641  $this->current_object->getId(),
642  $this->current_object->getType()
643  );
644  }
645  // type lm, dbk, glo
646  else {
647  if ($this->processMeta()) {
648  $this->md = new ilMD(
649  $this->current_object->getId(),
650  0,
651  $this->current_object->getType()
652  );
653  }
654  }
655  } else {
656  // type qpl or tst
657  $this->md = new ilMD(
658  $this->content_object->getId(),
659  0,
660  $this->current_object->getType()
661  );
662  if ($this->md->getGeneral() != false) {
663  $this->metadata_parsing_disabled = true;
664  $this->enableMDParsing(false);
665  }
666  }
667  }
668  break;
669 
670  // Identifier
671  case 'Identifier':
672  // begin-patch optes_lok_export
673  if ($this->in_meta_data && $this->current_object instanceof ilStructureObject) {
674  if ($this->mapping instanceof ilImportMapping) {
675  $import_id_parsed = ilUtil::parseImportId($a_attribs['Entry']);
676  if ($import_id_parsed['type'] == 'st') {
677  $this->mapping->addMapping(
678  'components/ILIAS/LearningModule',
679  'lm_tree',
680  $import_id_parsed['id'],
681  $this->current_object->getId()
682  );
683  }
684  }
685  }
686  // end-patch optes_lok_export
687 
688  // please note: Meta-Metadata and MetaData are different tags!
689  if (!$this->in_meta_meta_data) {
690  if ($this->in_meta_data && !$this->in_glossary_definition) {
691  if (!$this->in_media_object) {
692  $this->current_object->setImportId($a_attribs['Entry']);
693  }
694  // #40680 add a link target only if it is an internal ILIAS link
695  // Export from IMS UCAN sets something like 'IMSm-i1e79762'
696  $parsed = $this->parseLinkTarget($a_attribs["Entry"]);
697  if (isset($parsed)) {
698  $this->link_targets[$a_attribs['Entry']] = $a_attribs['Entry'];
699  }
700  }
701  if ($this->in_file_item) {
702  if (!isset($this->file_item_mapping[$a_attribs['Entry']])
703  || $this->file_item_mapping[$a_attribs['Entry']] === '') {
704  $this->file_item->create();
705  $this->file_item->setImportId($a_attribs['Entry']);
706  $this->file_item_mapping[$a_attribs['Entry']] = $this->file_item->getId();
707  }
708  }
709  if ($this->in_meta_data && $this->in_media_object) {
710  $mob_id = $this->mob_mapping[$a_attribs['Entry']];
711 
712  // within learning module import, usually a media object
713  // has already been created with a media alias tag
714  if ($mob_id > 0) {
715  $this->media_object = new ilObjMediaObject($mob_id);
716  } else { // in glossaries the media objects precede the definitions
717  // so we don't have an object already
718  $this->media_object = new ilObjMediaObject();
719  $this->media_object->create(true, false);
720  $this->mob_mapping[$a_attribs['Entry']]
721  = $this->media_object->getId();
722  }
723  $this->media_object->setImportId($a_attribs['Entry']);
724  $this->md = new ilMD(
725  0,
726  $this->media_object->getId(),
727  'mob'
728  );
729  $this->emptyMediaMetaCache($a_xml_parser);
730  }
731  }
732  break;
733 
734  case 'Meta-Metadata':
735  $this->in_meta_meta_data = true;
736  break;
737 
738  // Internal Link
739  case 'IntLink':
740  if (is_object($this->page_object)) {
741  $this->page_object->setContainsIntLink(true);
742  }
743  if ($this->in_map_area) {
744  $this->map_area->setLinkType(IL_INT_LINK);
745  $this->map_area->setTarget($a_attribs['Target']);
746  $this->map_area->setType($a_attribs['Type']);
747  $this->map_area->setTargetFrame($a_attribs['TargetFrame']);
748  if (is_object($this->media_object)) {
749  $this->media_object->setContainsIntLink(true);
750  }
751  }
752  break;
753 
754  // External Link
755  case 'ExtLink':
756  if ($this->in_map_area) {
757  $this->map_area->setLinkType(IL_EXT_LINK);
758  $this->map_area->setHref($a_attribs['Href']);
759  $this->map_area->setExtTitle($a_attribs['Title']);
760  }
761  break;
762 
763  // Question
764  case 'Question':
765  $this->cur_qid = $a_attribs['QRef'];
766  $this->page_object->setContainsQuestion(true);
767  break;
768 
769  case 'Location':
770  $this->loc_type = $a_attribs['Type'];
771  break;
772  }
773  $this->beginElement($a_name);
774 
775  // append content to page xml content
776  if (($this->in_page_object || $this->in_glossary_definition)
777  && !$this->in_meta_data && !$this->in_media_object) {
778  if ($a_name == 'Definition') {
779  $app_name = 'PageObject';
780  $app_attribs = [];
781  } else {
782  $app_name = $a_name;
783  $app_attribs = $a_attribs;
784  }
785 
786  // change identifier entry of file items to new local file id
787  if ($this->in_file_item && $app_name == 'Identifier') {
788  $app_attribs['Entry'] = 'il__file_' . $this->file_item_mapping[$a_attribs['Entry']];
789  }
790 
791  $this->page_object->appendXMLContent($this->buildTag('start', $app_name, $app_attribs));
792  }
793 
794 
795  // call meta data handler
796  if ($this->in_meta_data && $this->processMeta()) {
797  // cache beginning of meta data within media object tags
798  // (we need to know the id at the begin of meta elements within
799  // media objects, after the 'Identifier' tag has been processed
800  // we send the cached data to the meta xml handler)
801  if ($this->in_media_object && $this->media_meta_start) {
802  $this->media_meta_cache[] =
803  ['type' => 'handlerBeginTag', 'par1' => $a_name, 'par2' => $a_attribs];
804  } else {
805  if ($a_name == 'Identifier') {
806  if (!$this->in_media_object) {
807  $a_attribs['Entry'] = 'il__' . $this->current_object->getType() .
808  '_' . $this->current_object->getId();
809  } else {
810  $a_attribs['Entry'] = 'il__mob' .
811  '_' . $this->media_object->getId();
812  }
813  $a_attribs['Catalog'] = 'ILIAS';
814  }
815 
816  parent::handlerBeginTag($a_xml_parser, $a_name, $a_attribs);
817  }
818  }
819  }
820 
821  public function processMeta(): bool
822  {
823  // do not process second meta block in (ilias3) glossaries
824  // which comes right after the 'Glossary' tag
825  if ($this->content_object->getType() == 'glo' &&
826  $this->in_glossary && !$this->in_media_object
828  return false;
829  }
830 
831  return true;
832  }
833 
834 
835  public function handlerEndTag($a_xml_parser, string $a_name): void
836  {
837  // call meta data handler
838  if ($this->in_meta_data && $this->processMeta()) {
839  // cache beginning of meta data within media object tags
840  // (we need to know the id, after that we send the cached data
841  // to the meta xml handler)
842  if ($this->in_media_object && $this->media_meta_start) {
843  $this->media_meta_cache[] =
844  ['type' => 'handlerEndTag', 'par1' => $a_name];
845  } else {
846  parent::handlerEndTag($a_xml_parser, $a_name);
847  }
848  }
849 
850  // append content to page xml content
851  if (($this->in_page_object || $this->in_glossary_definition)
852  && !$this->in_meta_data && !$this->in_media_object) {
853  $app_name = ($a_name == 'Definition')
854  ? 'PageObject'
855  : $a_name;
856  $this->page_object->appendXMLContent($this->buildTag('end', $app_name));
857  }
858 
859  switch ($a_name) {
860  case 'StructureObject':
861  unset($this->structure_objects[count($this->structure_objects) - 1]);
862  break;
863 
864  case 'PageObject':
865  $this->in_page_object = false;
866  if ($this->content_type != 'tst' && $this->content_type != 'qpl') {
867  //if (!$this->lm_page_object->isAlias()) {
868  $this->page_object->updateFromXML();
869  $this->pg_mapping[$this->lm_page_object->getImportId()]
870  = $this->lm_page_object->getId();
871 
872  if ($this->mapping instanceof ilImportMapping) {
873  $import_id_parsed = ilUtil::parseImportId($this->lm_page_object->getImportId());
874  if ($import_id_parsed['type'] == 'pg') {
875  $this->mapping->addMapping(
876  'components/ILIAS/LearningModule',
877  'pg',
878  $import_id_parsed['id'],
879  $this->lm_page_object->getId()
880  );
881  }
882  }
883 
884  // collect pages with internal links
885  if ($this->page_object->containsIntLink()) {
886  $this->pages_to_parse['lm:' . $this->page_object->getId()] = 'lm:' . $this->page_object->getId();
887  }
888 
889  // collect pages with mobs or files
890  if ($this->page_object->needsImportParsing()) {
891  $this->pages_to_parse['lm:' . $this->page_object->getId()] = 'lm:' . $this->page_object->getId();
892  }
893 
894  // collect pages with questions
895  if ($this->page_object->getContainsQuestion()) {
896  $this->pages_to_parse['lm:' . $this->page_object->getId()] = 'lm:' . $this->page_object->getId();
897  }
898  //}
899  } else {
900  $xml = $this->page_object->getXMLContent();
901  if ($this->cur_qid != '') {
902  $ids = $this->qst_mapping[$this->cur_qid] ?? ['pool' => 0, 'test' => 0];
903  if ($ids['pool'] > 0) {
904  // question pool question
905  $page = new ilAssQuestionPage($ids['pool']);
906  $xmlcontent = str_replace(
907  $this->cur_qid,
908  'il__qst_' . $ids['pool'],
909  $xml
910  );
911  $page->setXMLContent($xmlcontent);
912  $page->updateFromXML();
913  if ($this->page_object->needsImportParsing()) {
914  $this->pages_to_parse['qpl:' . $page->getId()] = 'qpl:' . $page->getId();
915  }
916  unset($page);
917  }
918  if ($ids['test'] > 0) {
919  // test question
920  $page = new ilAssQuestionPage($ids['test']);
921  $xmlcontent = str_replace(
922  $this->cur_qid,
923  'il__qst_' . $ids['test'],
924  $xml
925  );
926  $page->setXMLContent($xmlcontent);
927  $page->updateFromXML();
928  if ($this->page_object->needsImportParsing()) {
929  $this->pages_to_parse['qpl:' . $page->getId()] = 'qpl:' . $page->getId();
930  }
931  unset($page);
932  }
933  }
934  }
935 
936  // if we are within a structure object: put page in tree
937  $cnt = count($this->structure_objects);
938  if ($cnt > 0) {
939  $parent_id = $this->structure_objects[$cnt - 1]->getId();
940  $this->pg_into_tree[$parent_id][] = ['type' => 'pg', 'id' => $this->lm_page_object->getId()];
941  }
942 
943  unset($this->page_object);
944  unset($this->lm_page_object);
945  unset($this->container[count($this->container) - 1]);
946  break;
947 
948  case 'MediaObject':
949  case 'InteractiveImage':
950  if ($a_name == 'MediaObject') {
951  $this->in_media_object = false;
952  }
953 
954  if (empty($this->mob_mapping[$this->media_object->getImportId()])) {
955  // create media object
956  // media items are saves for mobs outside of
957  // pages only
958  $this->media_object->create(true, false);
959 
960  // collect mobs with internal links
961  if ($this->media_object->containsIntLink()) {
962  $this->mobs_with_int_links[] = $this->media_object->getId();
963  }
964 
965  $this->mob_mapping[$this->media_object->getImportId()]
966  = $this->media_object->getId();
967  } else {
968  // get the id from mapping
969  $this->media_object->setId($this->mob_mapping[$this->media_object->getImportId()]);
970 
971  // update 'real' (no alias) media object
972  // (note: we overwrite any data from the media object
973  // created by an MediaAlias, only the data of the real
974  // object is stored in db separately; data of the
975  // MediaAliases are within the page XML
976  if (!$this->media_object->isAlias()) {
977  // now the media items are saved within the db
978  $this->media_object->update();
979 
980  // collect mobs with internal links
981  if ($this->media_object->containsIntLink()) {
982  $this->mobs_with_int_links[] = $this->media_object->getId();
983  }
984  }
985  }
986 
987  // append media alias to page, if we are in a page
988  if ($this->in_page_object || $this->in_glossary_definition) {
989  if ($a_name != 'InteractiveImage') {
990  $this->page_object->appendXMLContent($this->media_object->getXML(IL_MODE_ALIAS));
991  }
992  }
993 
994  break;
995 
996  case 'MediaAliasItem':
997  $this->in_media_item = false;
998  $this->media_object->addMediaItem($this->media_item);
999  break;
1000  case 'MediaItem':
1001  $this->in_media_item = false;
1002  $import_dir = $this->importdir . DIRECTORY_SEPARATOR . 'objects' . DIRECTORY_SEPARATOR . $this->media_object->getImportId();
1003  if (!file_exists($import_dir)
1004  || !is_dir($import_dir)) {
1005  $this->media_object->addMediaItem($this->media_item);
1006  break;
1007  }
1008 
1009  $dir_handle = opendir($import_dir);
1010  while (($file = readdir($dir_handle)) !== false) {
1011  if ($file !== $this->media_item->getLocation()) {
1012  continue;
1013  }
1014 
1015  $this->media_object->addMediaItemFromLocalFile(
1016  $this->media_item->getPurpose(),
1017  $import_dir . DIRECTORY_SEPARATOR . $file,
1018  $file
1019  );
1020  break;
1021  }
1022  closedir($dir_handle);
1023  break;
1024 
1025  case 'MapArea':
1026  $this->in_map_area = false;
1027  $this->media_item->addMapArea($this->map_area);
1028  break;
1029 
1030  case 'Properties':
1031  $this->in_properties = false;
1032  if ($this->content_object->getType() == 'lm') {
1033  $this->content_object->update();
1034  }
1035  break;
1036 
1037  case 'MetaData':
1038  $this->in_meta_data = false;
1039  if (strtolower(get_class($this->current_object)) == 'illmpageobject' && !$this->in_media_object) {
1040  // Metadaten eines PageObjects sichern in NestedSet
1041  if (is_object($this->lm_page_object)) {
1042  // update title/description of page object
1043  $this->current_object->MDUpdateListener('General');
1045  $this->current_object->getId(),
1046  $this->current_object->getImportId()
1047  );
1048  }
1049  } elseif ((strtolower(get_class($this->current_object)) == 'ilobjquestionpool' ||
1050  strtolower(get_class($this->current_object)) == 'ilobjtest') &&
1051  !$this->in_media_object) {
1052  // !$this->in_media_object && !$this->in_page_object)
1053  // changed for imports of ILIAS 2 Tests where PageObjects could have
1054  // Metadata sections (Helmut Schottmüller, 2005-12-02)
1055  if ($this->metadata_parsing_disabled) {
1056  $this->enableMDParsing(true);
1057  } else {
1058  if ($this->in_page_object && !is_null($this->page_object)) {
1059  /*
1060  $this->page_object->MDUpdateListener('General');
1061  ilLMObject::_writeImportId(
1062  $this->page_object->getId(),
1063  $this->page_object->getImportId()
1064  );*/
1065  } else {
1066  $this->current_object->MDUpdateListener('General');
1068  $this->current_object->getId(),
1069  $this->current_object->getImportId()
1070  );
1071  }
1072  }
1073  } elseif (strtolower(get_class($this->current_object)) == 'ilstructureobject') { // save structure object at the end of its meta block
1074  // determine parent
1075  $cnt = count($this->structure_objects);
1076  if ($cnt > 1) {
1077  $parent_id = $this->structure_objects[$cnt - 2]->getId();
1078  } else {
1079  $parent_id = $this->lm_tree->getRootId();
1080  }
1081 
1082  $this->st_into_tree[] = ['id' => $this->current_object->getId(),
1083  'parent' => $parent_id];
1084 
1085  // update title/description of structure object
1086  $this->current_object->MDUpdateListener('General');
1088  $this->current_object->getId(),
1089  $this->current_object->getImportId()
1090  );
1091  } elseif (strtolower(get_class($this->current_object)) == 'ilobjlearningmodule' ||
1092  strtolower(get_class($this->current_object)) == 'ilobjcontentobject' ||
1093  (strtolower(get_class($this->current_object)) == 'ilobjglossary' && $this->in_glossary)) {
1094  // todo: saving of md? getting title/descr and
1095  // set it for current object
1096  } elseif (strtolower(get_class($this->current_object)) == 'ilglossaryterm' && !$this->in_media_object) {
1097  // now on top
1098 
1099  $this->page_object->setId($this->glossary_term->getId());
1100  $this->page_object->updateFromXML();
1101 
1102  // todo: saving of md? getting title/descr and
1103  // set it for current object
1104  }
1105 
1106 
1107  if (strtolower(get_class($this->current_object)) == 'ilobjlearningmodule' ||
1108  strtolower(get_class($this->current_object)) == 'ilobjglossary') {
1109  if (strtolower(get_class($this->current_object)) == 'ilobjglossary' &&
1110  $this->content_object->getType() != 'glo') {
1111  $this->current_object->setTitle($this->content_object->getTitle() . ' - ' .
1112  $this->lng->txt('glossary'));
1113  }
1114 
1115  $this->current_object->MDUpdateListener('General');
1116  }
1117 
1118  if ($this->in_media_object) {
1119  $this->media_object->MDUpdateListener('General');
1120  }
1121 
1122  break;
1123 
1124  case 'Meta-Metadata':
1125  $this->in_meta_meta_data = false;
1126  break;
1127 
1128  case 'FileItem':
1129  $this->in_file_item = false;
1130  // only update new file items
1131  if ($this->file_item->getImportId()) {
1132  $this->file_item->update();
1133  }
1134  break;
1135 
1136 
1137  case 'Table':
1138  unset($this->container[count($this->container) - 1]);
1139  break;
1140 
1141  case 'Glossary':
1142  $this->in_glossary = false;
1143  break;
1144 
1145  case 'GlossaryTerm':
1146  $term = trim($this->chr_data);
1147  $term = str_replace('&lt;', '<', $term);
1148  $term = str_replace('&gt;', '>', $term);
1149  $this->glossary_term->setTerm($term);
1150  $this->glossary_term->create();
1151  $iia = explode('_', $this->glossary_term->getImportId());
1152  $this->glossary_term_map[(int) $iia[count($iia) - 1]] = $this->glossary_term->getId();
1153  break;
1154 
1155  case 'Paragraph':
1156  $this->inside_code = false;
1157  break;
1158 
1159  case 'Definition':
1160  $this->in_glossary_definition = false;
1161  $this->page_object->updateFromXML();
1162  $this->page_object->buildDom();
1163  $this->glossary_term->setShortText($this->page_object->getFirstParagraphText());
1164  $this->glossary_term->update();
1165  if ($this->page_object->containsIntLink()) {
1166  $this->pages_to_parse['term:' . $this->page_object->getId()] = 'term:' . $this->page_object->getId();
1167  }
1168  if ($this->page_object->needsImportParsing()) {
1169  $this->pages_to_parse['term:' . $this->page_object->getId()] = 'term:' . $this->page_object->getId();
1170  }
1171  break;
1172 
1173  case 'Format':
1174  if ($this->in_media_item) {
1175  $this->media_item->setFormat(trim($this->chr_data));
1176  }
1177  break;
1178 
1179  case 'Title':
1180  if ($this->in_meta_data && !$this->in_media_object) {
1181  $this->current_object->setTitle(trim($this->chr_data));
1182  }
1183  if ($this->in_media_object) {
1184  $this->media_object->setTitle(trim($this->chr_data));
1185  }
1186  break;
1187 
1188  case 'Description':
1189  case 'Language':
1190  break;
1191 
1192  case 'Caption':
1193  if ($this->in_media_object) {
1194  $this->media_item->setCaption(trim($this->chr_data));
1195  }
1196  break;
1197 
1198  case 'TextRepresentation':
1199  if ($this->in_media_object) {
1200  $this->media_item->setTextRepresentation(trim($this->chr_data));
1201  }
1202  break;
1203 
1204  // Location
1205  case 'Location':
1206  // TODO: adapt for files in 'real' subdirectories
1207  if ($this->in_media_item) {
1208  $this->media_item->setLocationType($this->loc_type);
1209  if ($this->loc_type == 'Reference') {
1210  $this->media_item->setLocation(str_replace('&', '&amp;', trim($this->chr_data)));
1211  } else {
1212  $this->media_item->setLocation(trim($this->chr_data));
1213  }
1214  }
1215  if ($this->in_file_item) {
1216  // set file name from xml file
1217  $this->file_item->setFileName(trim($this->chr_data));
1218 
1219  // special handling for file names with special characters
1220  // (e.g. '&gt;')
1221  if ($this->file_item->getType() == 'file' &&
1222  is_int(strpos($this->chr_data, '&')) &&
1223  is_int(strpos($this->chr_data, ';'))) {
1224  $source_dir = $this->importdir . DIRECTORY_SEPARATOR . 'objects' . DIRECTORY_SEPARATOR .
1225  $this->file_item->getImportId();
1226 
1227  // read 'physical' file name from directory
1228  if ($dir = opendir($source_dir)) {
1229  while (false !== ($file = readdir($dir))) {
1230  if ($file != '.' && $file != '..') {
1231  $this->file_item->setFileName($file);
1232  }
1233  }
1234  closedir($dir);
1235  }
1236  }
1237 
1238  // set file item title
1239  $this->file_item->setTitle(trim($this->chr_data));
1240  }
1241  break;
1242  }
1243  $this->endElement($a_name);
1244  $this->chr_data = '';
1245  }
1246 
1247  public function handlerCharacterData($a_xml_parser, string $a_data): void
1248  {
1249  // call meta data handler
1250  if ($this->in_meta_data && $this->processMeta()) {
1251  // cache beginning of meta data within media object tags
1252  // (we need to know the id, after that we send the cached data
1253  // to the meta xml handler)
1254  if ($this->in_media_object && $this->media_meta_start) {
1255  $this->media_meta_cache[] =
1256  ['type' => 'handlerCharacterData', 'par1' => $a_data];
1257  } else {
1258  parent::handlerCharacterData($a_xml_parser, $a_data);
1259  }
1260  }
1261 
1262  // the parser converts '&gt;' to '>' and '&lt;' to '<'
1263  // in character data, but we don't want that, because it's the
1264  // way we mask user html in our content, so we convert back...
1265 
1266  $a_data = str_replace('<', '&lt;', $a_data);
1267  $a_data = str_replace('>', '&gt;', $a_data);
1268 
1269 
1270  // DELETE WHITESPACES AND NEWLINES OF CHARACTER DATA
1271  $a_data = preg_replace('/\n/', '', $a_data);
1272  if (!$this->inside_code) {
1273  $a_data = preg_replace('/\t+/', '', $a_data);
1274  }
1275 
1276  $this->chr_data .= $a_data;
1277 
1278  if (!empty($a_data) || $a_data === '0') {
1279  // append all data to page, if we are within PageObject,
1280  // but not within MetaData or MediaObject
1281  if (($this->in_page_object || $this->in_glossary_definition)
1282  && !$this->in_meta_data && !$this->in_media_object) {
1283  $this->page_object->appendXMLContent($a_data);
1284  }
1285 
1286  switch ($this->getCurrentElement()) {
1287  case 'IntLink':
1288  case 'ExtLink':
1289  if ($this->in_map_area) {
1290  $this->map_area->appendTitle($a_data);
1291  }
1292  break;
1293  }
1294  }
1295  }
1296 
1300  public function emptyMediaMetaCache($a_xml_parser): void
1301  {
1302  foreach ($this->media_meta_cache as $cache_entry) {
1303  switch ($cache_entry['type']) {
1304  case 'handlerBeginTag':
1305  parent::handlerBeginTag(
1306  $a_xml_parser,
1307  $cache_entry['par1'],
1308  $cache_entry['par2']
1309  );
1310  break;
1311 
1312  case 'handlerEndTag':
1313  parent::handlerEndTag(
1314  $a_xml_parser,
1315  $cache_entry['par1']
1316  );
1317  break;
1318 
1319  case 'handlerCharacterData':
1320  parent::handlerCharacterData(
1321  $a_xml_parser,
1322  $cache_entry['par1']
1323  );
1324  break;
1325  }
1326  }
1327 
1328  $this->media_meta_start = false;
1329  $this->media_meta_cache[] = [];
1330  }
1331 
1335  public function getGlossaryTermMap(): array
1336  {
1337  return $this->glossary_term_map;
1338  }
1339 
1345  private function parseLinkTarget(string $identifier): ?array
1346  {
1347  $link_arr = explode('_', $identifier);
1348 
1349  if (count($link_arr) !== 4
1350  || $link_arr[0] !== 'il'
1351  || !is_numeric($link_arr[1])
1352  || !is_numeric($link_arr[3])
1353  ) {
1354  return null;
1355  }
1356 
1357  return [
1358  'target_inst' => (int) $link_arr[1],
1359  'target_type' => (string) $link_arr[2],
1360  'target_id' => (int) $link_arr[3]
1361  ];
1362  }
1363 }
processPagesToParse()
parse pages that contain files, mobs and/or internal links
enableMDParsing(bool $a_status)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
buildDom(bool $a_force=false)
static getLogger(string $a_component_id)
Get component logger.
buildTag(string $type, string $name, array $attr=[])
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
handlerEndTag($a_xml_parser, string $a_name)
write(string $message, $level=ilLogLevel::INFO, array $context=[])
write log message
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...
handlerBeginTag($a_xml_parser, string $a_name, array $a_attribs)
resolveIntLinks(array $a_link_map=null)
Resolves all internal link targets of the page, if targets are available (after import) ...
update(bool $a_validate=true, bool $a_no_history=false)
update complete page content in db (dom xml content is used)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setImportMapping(ilImportMapping $mapping=null)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
ilLanguage $lng
parseLinkTarget(string $identifier)
Parse a string the get the elements of a link target Return null if the string is not a link target...
static instantiateQuestion(int $question_id)
setQuestionMapping(array $a_map)
set question import ident to pool/test question id mapping
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const IL_INT_LINK
handlerCharacterData($a_xml_parser, string $a_data)
static _exists(string $a_parent_type, int $a_id, string $a_lang="", bool $a_no_cache=false)
Checks whether page exists.
Class ilPageObject Handles PageObjects of ILIAS Learning Modules (see ILIAS DTD)
Class ilObjFile.
global $DIC
Definition: shib_login.php:25
Class ilMediaItem Media Item, component of a media object (file or reference)
Legacy Content Object Parser.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static parseImportId(string $a_import_id)
Parse an ilias import id Typically of type il_[IL_INST_ID]_[OBJ_TYPE]_[OBJ_ID] returns array( &#39;orig&#39; ...
const IL_MODE_ALIAS
static _writeImportId(int $a_id, string $a_import_id)
static _resolveMapAreaLinks(int $a_mob_id)
resolve internal links of all media items of a media object
ilObjLearningModule $learning_module
copyFileItems()
copy files of file items
const IL_EXT_LINK
Class ilMapArea.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
__construct(Container $dic, ilPlugin $plugin)
static yn2tf(string $a_yn)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
__construct(private readonly ilObject $content_object, string $xml_file, private readonly string $importdir)
static getInstance(string $a_parent_type, int $a_id=0, int $a_old_nr=0, string $a_lang="-")
Get page object instance.
storeTree()
insert StructureObjects and PageObjects into tree