ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
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 $pg_mapping = [];
38  protected array $mobs_with_int_links = [];
39  protected bool $inside_code = false;
40  protected string $content_type = '';
41  public ilTree $tree;
42  public array $cnt = []; // counts open elements
43  public array $current_element = []; // store current element type
44  public ?ilObjLearningModule $learning_module = null; // current learning module
45  public ?ilPageObject $page_object = null; // current page object
47  public array $structure_objects = []; // array of current structure objects
49  public ?object $current_object = null; // at the time a LearningModule, PageObject or StructureObject
50  public ?ilLMTree $lm_tree = null;
51  public array $pg_into_tree = [];
52  public array $st_into_tree = [];
53  public array $container = [];
54  public bool $in_page_object = false; // are we currently within a PageObject? true/false
55  public bool $in_media_object = false;
56  public bool $in_file_item = false;
57  public bool $in_glossary = false;
58  public bool $in_map_area = false;
61  public array $pages_to_parse = [];
62  public array $mob_mapping = [];
63  public array $file_item_mapping = [];
64  public ?ilMediaItem $media_item = null; // current media item
65  public string $loc_type = ''; // current location type
66  public ?ilMapArea $map_area = null; // current map area
67  public array $link_targets = []; // stores all objects by import id
68  public array $qst_mapping = [];
69  protected array $glossary_term_map = [];
70  protected ilLogger $log;
73  protected string $cur_qid = '';
74  protected string $chr_data = '';
75  protected bool $in_media_item = false;
76 
77  public function __construct(
78  private readonly ilObject $content_object,
79  string $xml_file,
80  private readonly string $importdir
81  ) {
82  global $DIC;
83  $lng = $DIC->language();
84  $tree = $DIC->repositoryTree();
85 
86  $this->log = ilLoggerFactory::getLogger('lm');
87 
88  parent::__construct($xml_file);
89  $this->cnt = [];
90  $this->current_element = [];
91  $this->structure_objects = [];
92  $this->st_into_tree = [];
93  $this->pg_into_tree = [];
94  $this->pages_to_parse = [];
95  $this->mobs_with_int_links = [];
96  $this->mob_mapping = [];
97  $this->file_item_mapping = [];
98  $this->pg_mapping = [];
99  $this->link_targets = [];
100  $this->lng = $lng;
101  $this->tree = $tree;
102  $this->inside_code = false;
103  $this->qst_mapping = [];
104  $this->content_type = $this->content_object->getType();
105 
106  if ($this->content_type !== 'tst' && $this->content_type !== 'qpl') {
107  $this->lm_tree = new ilLMTree($this->content_object->getId());
108  }
109  }
110 
114  public function setHandlers($xml_parser): void
115  {
116  xml_set_element_handler($xml_parser, $this->handlerBeginTag(...), $this->handlerEndTag(...));
117  xml_set_character_data_handler($xml_parser, $this->handlerCharacterData(...));
118  }
119 
120  public function setImportMapping(?ilImportMapping $mapping = null): void
121  {
122  $this->mapping = $mapping;
123  }
124 
125  public function startParsing(): void
126  {
127  $this->log->debug('start');
128 
129  parent::startParsing();
130  $this->storeTree();
131  $this->processPagesToParse();
132  $this->copyFileItems();
133  }
134 
138  public function storeTree(): void
139  {
140  $ilLog = $this->log;
141 
142  foreach ($this->st_into_tree as $st) {
143  $this->lm_tree->insertNode($st['id'], $st['parent']);
144  if (is_array($this->pg_into_tree[$st['id']])) {
145  foreach ($this->pg_into_tree[$st['id']] as $pg) {
146  $pg_id = 0;
147  switch ($pg['type']) {
148  case 'pg_alias':
149  if ($this->pg_mapping[$pg['id']] == '') {
150  $ilLog->write('LM Import: No PageObject for PageAlias ' .
151  $pg['id'] . ' found! (Please update export installation to ILIAS 3.3.0)');
152 
153  // Jump two levels up. First level is switch
154  continue 2;
155  }
156  $pg_id = $this->pg_mapping[$pg['id']];
157  break;
158 
159  case 'pg':
160  $pg_id = $pg['id'];
161  break;
162  }
163  if (!$this->lm_tree->isInTree($pg_id)) {
164  $this->lm_tree->insertNode($pg_id, $st['id']);
165  }
166  }
167  }
168  }
169  }
170 
174  public function processPagesToParse(): void
175  {
176  // outgoin internal links
177  foreach ($this->pages_to_parse as $page_id) {
178  $page_arr = explode(':', $page_id);
179  $page_obj = null;
180  switch ($page_arr[0]) {
181  case 'lm':
182  switch ($this->content_object->getType()) {
183  case 'lm':
184  $page_obj = new ilLMPage($page_arr[1]);
185  break;
186 
187  default:
188  die('Unknown content type ' . $this->content_object->getType());
189  }
190 
191  break;
192 
193  case 'term':
194  $page_obj = new ilGlossaryDefPage($page_arr[1]);
195  break;
196 
197  case 'qpl':
198  $page_obj = new ilAssQuestionPage($page_arr[1]);
199  break;
200  }
201  $page_obj->buildDom();
202  $page_obj->resolveIntLinks();
203  $page_obj->resolveIIMMediaAliases($this->mob_mapping);
204  if ($this->content_type == 'lm') {
205  $page_obj->resolveQuestionReferences($this->qst_mapping);
206  }
207  $page_obj->update(false);
208 
209  if ($page_arr[0] == 'term') {
210  $term = new ilGlossaryTerm($page_arr[1]);
211  $term->updateShortText();
212  }
213 
214  unset($page_obj);
215  }
216 
217  foreach ($this->mobs_with_int_links as $mob_id) {
219  }
220 
221  $done = [];
222  foreach ($this->link_targets as $link_target) {
223  $parsed = $this->parseLinkTarget((string) $link_target);
224  if (!isset($parsed)) {
225  continue;
226  }
227  //echo "doin link target:".$link_target.":<br>";
229  $parsed['target_type'],
230  $parsed['target_id'],
231  $parsed['target_inst']
232  );
233  foreach ($sources as $key => $source) {
234  if (in_array($key, $done)) {
235  continue;
236  }
237  $type_arr = explode(':', $source['type']);
238 
239  // content object pages
240  if ($type_arr[1] == 'pg') {
241  if (ilPageObject::_exists($type_arr[0], $source['id'])) {
242  $page_object = ilPageObjectFactory::getInstance($type_arr[0], $source['id']);
243  $page_object->buildDom();
244  $page_object->resolveIntLinks();
245  $page_object->update();
246  unset($page_object);
247  }
248  }
249 
250  // eventually correct links in questions to learning modules
251  if ($type_arr[0] === 'qst') {
252  assQuestion::instantiateQuestion($source['id'])->resolveSuggestedSolutionLinks();
253  }
254  $done[$key] = $key;
255  }
256  }
257  }
258 
262  private function copyFileItems(): void
263  {
264  foreach ($this->file_item_mapping as $origin_id => $file_id) {
265  if (empty($origin_id)) {
266  continue;
267  }
268  $obj_dir = $origin_id;
269  $source_dir = $this->importdir . DIRECTORY_SEPARATOR . 'objects' . DIRECTORY_SEPARATOR . $obj_dir;
270 
271  $file_obj = new ilObjFile($file_id, false);
272  if (is_dir($source_dir)) {
273  $files = scandir($source_dir, SCANDIR_SORT_DESCENDING);
274  if ($files !== false && $files !== [] && is_file($source_dir . '/' . $files[0])) {
275  $file = fopen($source_dir . '/' . $files[0], 'rb');
276  $file_stream = Streams::ofResource($file);
277  $file_obj->appendStream($file_stream, $files[0]);
278  }
279  }
280  $file_obj->update();
281  }
282  }
283 
288  public function setQuestionMapping(array $a_map): void
289  {
290  $this->qst_mapping = $a_map;
291  }
292 
293  public function beginElement(string $a_name): void
294  {
295  if (!isset($this->status['$a_name'])) {
296  $this->cnt[$a_name] = 1;
297  } else {
298  $this->cnt[$a_name]++;
299  }
300  $this->current_element[count($this->current_element)] = $a_name;
301  }
302 
303  public function endElement(string $a_name): void
304  {
305  $this->cnt[$a_name]--;
306  unset($this->current_element[count($this->current_element) - 1]);
307  }
308 
309  public function getCurrentElement(): string
310  {
311  return ($this->current_element[count($this->current_element) - 1] ?? '');
312  }
313 
314  public function getOpenCount(string $a_name): int
315  {
316  if (isset($this->cnt[$a_name])) {
317  return $this->cnt[$a_name];
318  }
319  return 0;
320  }
321 
322  public function buildTag(
323  string $type,
324  string $name,
325  array $attr = []
326  ): string {
327  $tag = '<';
328 
329  if ($type == 'end') {
330  $tag .= '/';
331  }
332 
333  $tag .= $name;
334 
335  if (is_array($attr)) {
336  foreach ($attr as $k => $v) {
337  $tag .= ' ' . $k . "='$v'";
338  }
339  }
340 
341  $tag .= '>';
342 
343  return $tag;
344  }
345 
346  public function handlerBeginTag($a_xml_parser, string $a_name, array $a_attribs): void
347  {
348  switch ($a_name) {
349  case 'ContentObject':
350  $this->current_object = $this->content_object;
351  if ($a_attribs['Type'] == 'Glossary') {
352  $this->glossary_object = $this->content_object;
353  }
354  break;
355 
356  case 'StructureObject':
358  $lm = $this->content_object;
359  $this->structure_objects[count($this->structure_objects)]
360  = new ilStructureObject($lm);
361  $this->current_object = $this->structure_objects[count($this->structure_objects) - 1];
362  $this->current_object->setLMId($this->content_object->getId());
363  // new meta data handling: we create the structure
364  // object already here, this should also create a
365  // md entry
366  $this->current_object->create(true);
367  break;
368 
369  case 'PageObject':
370  $this->in_page_object = true;
371  $this->cur_qid = '';
372  if ($this->content_type !== 'tst' && $this->content_type !== 'qpl') {
374  $lm = $this->content_object;
375  $this->lm_page_object = new ilLMPageObject($lm);
376  $this->page_object = new ilLMPage();
377  $this->lm_page_object->setLMId($this->content_object->getId());
378  $this->lm_page_object->assignPageObject($this->page_object);
379  $this->current_object = $this->lm_page_object;
380  } else {
381  $this->page_object = new ilAssQuestionPage();
382  }
383  break;
384 
385  case 'PageAlias':
386  throw new ilLMException('Page Alias not supported.');
387 
388  case 'MediaObject':
389  case 'InteractiveImage':
390  if ($a_name == 'MediaObject') {
391  $this->in_media_object = true;
392  }
393  $this->media_object = new ilObjMediaObject();
394  $this->media_object->create(false, false);
395  break;
396 
397  case 'MediaAlias':
398  $this->media_object->setAlias(true);
399  $this->media_object->setImportId($a_attribs['OriginId']);
400  $this->mob_mapping[$this->media_object->getImportId()] = $this->media_object->getId();
401  if (is_object($this->page_object)) {
402  $this->page_object->needsImportParsing(true);
403  }
404  break;
405 
406  case 'MediaItem':
407  case 'MediaAliasItem':
408  $this->in_media_item = true;
409  $this->media_item = new ilMediaItem();
410  $this->media_item->setPurpose($a_attribs['Purpose']);
411  break;
412 
413  case 'Layout':
414  if (is_object($this->media_object) && $this->in_media_object) {
415  $this->media_item->setWidth($a_attribs['Width'] ?? '');
416  $this->media_item->setHeight($a_attribs['Height'] ?? '');
417  $this->media_item->setHAlign($a_attribs['HorizontalAlign'] ?? '');
418  }
419  break;
420 
421  case 'Parameter':
422  if (is_object($this->media_object) && $this->in_media_object) {
423  $this->media_item->setParameter($a_attribs['Name'], $a_attribs['Value']);
424  }
425  break;
426 
427  case 'MapArea':
428  $this->in_map_area = true;
429  $this->map_area = new ilMapArea();
430  $this->map_area->setShape($a_attribs['Shape']);
431  $this->map_area->setCoords($a_attribs['Coords']);
432  $this->map_area->setHighlightMode($a_attribs['HighlightMode']);
433  $this->map_area->setHighlightClass($a_attribs['HighlightClass']);
434  break;
435 
436  case 'Glossary':
437  $this->in_glossary = true;
438  if ($this->content_object->getType() != 'glo') {
439  $this->glossary_object = new ilObjGlossary();
440  $this->glossary_object->setTitle('');
441  $this->glossary_object->setDescription('');
442  $this->glossary_object->create(true);
443  $this->glossary_object->createReference();
444  $parent = $this->tree->getParentNodeData($this->content_object->getRefId());
445  $this->glossary_object->putInTree($parent['child']);
446  $this->glossary_object->setPermissions($parent['child']);
447  }
448  $this->current_object = $this->glossary_object;
449  break;
450 
451  case 'GlossaryItem':
452  $this->glossary_term = new ilGlossaryTerm();
453  $this->glossary_term->setGlossaryId($this->glossary_object->getId());
454  $this->glossary_term->setLanguage($a_attribs['Language']);
455  $this->glossary_term->setImportId($a_attribs['Id']);
456  $parsed = $this->parseLinkTarget((string) $a_attribs['Id']);
457  if (isset($parsed)) {
458  $this->link_targets[$a_attribs["Id"]] = $a_attribs['Id'];
459  }
460 
461  // no break
462  case 'Definition':
463  $this->in_glossary_definition = true;
464  $this->page_object = new ilGlossaryDefPage();
465  $this->page_object->setParentId($this->glossary_term->getGlossaryId());
466  $this->glossary_term->assignPageObject($this->page_object);
467  $this->current_object = $this->glossary_term;
468  // see bug #12465, we need to clear xml after creation, since it will be <PageObject></PageObject>
469  // otherwise, and appendXML calls will lead to '<PageObject></PageObject><PageObject>....</PageObject>'
470  $this->page_object->setXMLContent('');
471  break;
472 
473  case 'FileItem':
474  $this->in_file_item = true;
475  $this->file_item = new ilObjFile();
476  $this->file_item->setTitle('dummy');
477  $this->file_item->setMode('filelist');
478  if (is_object($this->page_object)) {
479  $this->page_object->needsImportParsing(true);
480  }
481  break;
482 
483  case 'Paragraph':
484  if ($a_attribs['Characteristic'] == 'Code') {
485  $this->inside_code = true;
486  }
487  break;
488 
489  case 'Properties':
490  $this->in_properties = true;
491  break;
492 
493  case 'Property':
494  if ($this->content_object->getType() == 'lm') {
495  switch ($a_attribs['Name']) {
496  case 'Layout':
497  $this->content_object->setLayout($a_attribs['Value']);
498  break;
499 
500  case 'PageHeader':
501  $this->content_object->setPageHeader($a_attribs['Value']);
502  break;
503 
504  case 'TOCMode':
505  $this->content_object->setTOCMode($a_attribs['Value']);
506  break;
507 
508  case 'ActiveLMMenu':
509  $this->content_object->setActiveLMMenu(
510  ilUtil::yn2tf($a_attribs['Value'])
511  );
512  break;
513 
514  case 'ActiveNumbering':
515  $this->content_object->setActiveNumbering(
516  ilUtil::yn2tf($a_attribs['Value'])
517  );
518  break;
519 
520  case 'ActiveTOC':
521  $this->content_object->setActiveTOC(
522  ilUtil::yn2tf($a_attribs['Value'])
523  );
524  break;
525 
526  case 'ActivePrintView':
527  $this->content_object->setActivePrintView(
528  ilUtil::yn2tf($a_attribs['Value'])
529  );
530  break;
531 
532  case 'CleanFrames':
533  $this->content_object->setCleanFrames(
534  ilUtil::yn2tf($a_attribs['Value'])
535  );
536  break;
537 
538  case 'PublicNotes':
539  $this->content_object->setPublicNotes(
540  ilUtil::yn2tf($a_attribs['Value'])
541  );
542  break;
543 
544  case 'HistoryUserComments':
545  $this->content_object->setHistoryUserComments(
546  ilUtil::yn2tf($a_attribs['Value'])
547  );
548  break;
549 
550  case 'Rating':
551  $this->content_object->setRating(
552  ilUtil::yn2tf($a_attribs['Value'])
553  );
554  break;
555 
556  case 'RatingPages':
557  $this->content_object->setRatingPages(
558  ilUtil::yn2tf($a_attribs['Value'])
559  );
560  break;
561 
562  case 'HeaderPage':
563  if ($a_attribs['Value'] != '') {
564  if ($this->pg_mapping[$a_attribs['Value']] > 0) {
565  $this->content_object->setHeaderPage(
566  $this->pg_mapping[$a_attribs['Value']]
567  );
568  }
569  }
570  break;
571 
572  case 'FooterPage':
573  if ($a_attribs['Value'] != '') {
574  if ($this->pg_mapping[$a_attribs['Value']] > 0) {
575  $this->content_object->setFooterPage(
576  $this->pg_mapping[$a_attribs['Value']]
577  );
578  }
579  }
580  break;
581 
582  case 'LayoutPerPage':
583  $this->content_object->setLayoutPerPage($a_attribs['Value']);
584  break;
585 
586  case 'ProgressIcons':
587  $this->content_object->setProgressIcons($a_attribs['Value']);
588  break;
589 
590  case 'StoreTries':
591  $this->content_object->setStoreTries($a_attribs['Value']);
592  break;
593 
594  case 'RestrictForwardNavigation':
595  $this->content_object->setRestrictForwardNavigation($a_attribs['Value']);
596  break;
597 
598  case 'DisableDefaultFeedback':
599  $this->content_object->setDisableDefaultFeedback($a_attribs['Value']);
600  break;
601  }
602  }
603  break;
604 
605  // Identifier
606  case 'Identifier':
607  if ($this->in_file_item) {
608  if (!isset($this->file_item_mapping[$a_attribs['Entry']])
609  || $this->file_item_mapping[$a_attribs['Entry']] === '') {
610  $this->file_item->create();
611  $this->file_item->setImportId($a_attribs['Entry']);
612  $this->file_item_mapping[$a_attribs['Entry']] = $this->file_item->getId();
613  }
614  }
615  if ($this->in_media_object) {
616  $mob_id = $this->mob_mapping[$a_attribs['Entry']];
617 
618  // within learning module import, usually a media object
619  // has already been created with a media alias tag
620  if ($mob_id > 0) {
621  $this->media_object = new ilObjMediaObject($mob_id);
622  } else { // in glossaries the media objects precede the definitions
623  // so we don't have an object already
624  $this->media_object = new ilObjMediaObject();
625  $this->media_object->create(false, false);
626  $this->mob_mapping[$a_attribs['Entry']]
627  = $this->media_object->getId();
628  }
629  $this->media_object->setImportId($a_attribs['Entry']);
630  }
631  break;
632 
633  // Internal Link
634  case 'IntLink':
635  if (is_object($this->page_object)) {
636  $this->page_object->setContainsIntLink(true);
637  }
638  if ($this->in_map_area) {
639  $this->map_area->setLinkType(IL_INT_LINK);
640  $this->map_area->setTarget($a_attribs['Target']);
641  $this->map_area->setType($a_attribs['Type']);
642  $this->map_area->setTargetFrame($a_attribs['TargetFrame']);
643  if (is_object($this->media_object)) {
644  $this->media_object->setContainsIntLink(true);
645  }
646  }
647  break;
648 
649  // External Link
650  case 'ExtLink':
651  if ($this->in_map_area) {
652  $this->map_area->setLinkType(IL_EXT_LINK);
653  $this->map_area->setHref($a_attribs['Href']);
654  $this->map_area->setExtTitle($a_attribs['Title']);
655  }
656  break;
657 
658  // Question
659  case 'Question':
660  $this->cur_qid = $a_attribs['QRef'];
661  $this->page_object->setContainsQuestion(true);
662  break;
663 
664  case 'Location':
665  $this->loc_type = $a_attribs['Type'];
666  break;
667  }
668  $this->beginElement($a_name);
669 
670  // append content to page xml content
671  if (($this->in_page_object || $this->in_glossary_definition)
672  && !$this->in_media_object) {
673  if ($a_name == 'Definition') {
674  $app_name = 'PageObject';
675  $app_attribs = [];
676  } else {
677  $app_name = $a_name;
678  $app_attribs = $a_attribs;
679  }
680 
681  // change identifier entry of file items to new local file id
682  if ($this->in_file_item && $app_name == 'Identifier') {
683  $app_attribs['Entry'] = 'il__file_' . $this->file_item_mapping[$a_attribs['Entry']];
684  }
685 
686  $this->page_object->appendXMLContent($this->buildTag('start', $app_name, $app_attribs));
687  }
688  }
689 
690  public function processMeta(): bool
691  {
692  // do not process second meta block in (ilias3) glossaries
693  // which comes right after the 'Glossary' tag
694  if ($this->content_object->getType() == 'glo' &&
695  $this->in_glossary && !$this->in_media_object
697  return false;
698  }
699 
700  return true;
701  }
702 
703 
704  public function handlerEndTag($a_xml_parser, string $a_name): void
705  {
706  // append content to page xml content
707  if (($this->in_page_object || $this->in_glossary_definition)
708  && !$this->in_media_object) {
709  $app_name = ($a_name == 'Definition')
710  ? 'PageObject'
711  : $a_name;
712  $this->page_object->appendXMLContent($this->buildTag('end', $app_name));
713  }
714 
715  switch ($a_name) {
716  case 'StructureObject':
717  unset($this->structure_objects[count($this->structure_objects) - 1]);
718  break;
719 
720  case 'PageObject':
721  $this->in_page_object = false;
722  if ($this->content_type != 'tst' && $this->content_type != 'qpl') {
723  //if (!$this->lm_page_object->isAlias()) {
724  $this->page_object->updateFromXML();
725  $this->pg_mapping[$this->lm_page_object->getImportId()]
726  = $this->lm_page_object->getId();
727 
728  if ($this->mapping instanceof ilImportMapping) {
729  $import_id_parsed = ilUtil::parseImportId($this->lm_page_object->getImportId());
730  if ($import_id_parsed['type'] == 'pg') {
731  $this->mapping->addMapping(
732  'components/ILIAS/LearningModule',
733  'pg',
734  $import_id_parsed['id'],
735  $this->lm_page_object->getId()
736  );
737  }
738  }
739 
740  // collect pages with internal links
741  if ($this->page_object->containsIntLink()) {
742  $this->pages_to_parse['lm:' . $this->page_object->getId()] = 'lm:' . $this->page_object->getId();
743  }
744 
745  // collect pages with mobs or files
746  if ($this->page_object->needsImportParsing()) {
747  $this->pages_to_parse['lm:' . $this->page_object->getId()] = 'lm:' . $this->page_object->getId();
748  }
749 
750  // collect pages with questions
751  if ($this->page_object->getContainsQuestion()) {
752  $this->pages_to_parse['lm:' . $this->page_object->getId()] = 'lm:' . $this->page_object->getId();
753  }
754  //}
755  } else {
756  $xml = $this->page_object->getXMLContent();
757  if ($this->cur_qid != '') {
758  $ids = $this->qst_mapping[$this->cur_qid] ?? ['pool' => 0, 'test' => 0];
759  if ($ids['pool'] > 0) {
760  // question pool question
761  $page = new ilAssQuestionPage($ids['pool']);
762  $xmlcontent = str_replace(
763  $this->cur_qid,
764  'il__qst_' . $ids['pool'],
765  $xml
766  );
767  $page->setXMLContent($xmlcontent);
768  $page->updateFromXML();
769  if ($this->page_object->needsImportParsing()) {
770  $this->pages_to_parse['qpl:' . $page->getId()] = 'qpl:' . $page->getId();
771  }
772  unset($page);
773  }
774  if ($ids['test'] > 0) {
775  // test question
776  $page = new ilAssQuestionPage($ids['test']);
777  $xmlcontent = str_replace(
778  $this->cur_qid,
779  'il__qst_' . $ids['test'],
780  $xml
781  );
782  $page->setXMLContent($xmlcontent);
783  $page->updateFromXML();
784  if ($this->page_object->needsImportParsing()) {
785  $this->pages_to_parse['qpl:' . $page->getId()] = 'qpl:' . $page->getId();
786  }
787  unset($page);
788  }
789  }
790  }
791 
792  // if we are within a structure object: put page in tree
793  $cnt = count($this->structure_objects);
794  if ($cnt > 0) {
795  $parent_id = $this->structure_objects[$cnt - 1]->getId();
796  $this->pg_into_tree[$parent_id][] = ['type' => 'pg', 'id' => $this->lm_page_object->getId()];
797  }
798 
799  unset($this->page_object);
800  unset($this->lm_page_object);
801  unset($this->container[count($this->container) - 1]);
802  break;
803 
804  case 'MediaObject':
805  case 'InteractiveImage':
806  if ($a_name == 'MediaObject') {
807  $this->in_media_object = false;
808  }
809 
810  if (empty($this->mob_mapping[$this->media_object->getImportId()])) {
811  // create media object
812  // media items are saves for mobs outside of
813  // pages only
814  $this->media_object->create(false, false);
815 
816  // collect mobs with internal links
817  if ($this->media_object->containsIntLink()) {
818  $this->mobs_with_int_links[] = $this->media_object->getId();
819  }
820 
821  $this->mob_mapping[$this->media_object->getImportId()]
822  = $this->media_object->getId();
823  } else {
824  // get the id from mapping
825  $this->media_object->setId($this->mob_mapping[$this->media_object->getImportId()]);
826 
827  // update 'real' (no alias) media object
828  // (note: we overwrite any data from the media object
829  // created by an MediaAlias, only the data of the real
830  // object is stored in db separately; data of the
831  // MediaAliases are within the page XML
832  if (!$this->media_object->isAlias()) {
833  // now the media items are saved within the db
834  $this->media_object->update();
835 
836  // collect mobs with internal links
837  if ($this->media_object->containsIntLink()) {
838  $this->mobs_with_int_links[] = $this->media_object->getId();
839  }
840  }
841  }
842 
843  // append media alias to page, if we are in a page
844  if ($this->in_page_object || $this->in_glossary_definition) {
845  if ($a_name != 'InteractiveImage') {
846  $this->page_object->appendXMLContent($this->media_object->getXML(IL_MODE_ALIAS));
847  }
848  }
849 
850  break;
851 
852  case 'MediaAliasItem':
853  $this->in_media_item = false;
854  $this->media_object->addMediaItem($this->media_item);
855  break;
856  case 'MediaItem':
857  $this->in_media_item = false;
858  $import_dir = $this->importdir . DIRECTORY_SEPARATOR . 'objects' . DIRECTORY_SEPARATOR . $this->media_object->getImportId();
859  if (!file_exists($import_dir)
860  || !is_dir($import_dir)) {
861  $this->media_object->addMediaItem($this->media_item);
862  break;
863  }
864 
865  $dir_handle = opendir($import_dir);
866  while (($file = readdir($dir_handle)) !== false) {
867  if ($file !== $this->media_item->getLocation()) {
868  continue;
869  }
870 
871  $this->media_object->addMediaItemFromLocalFile(
872  $this->media_item->getPurpose(),
873  $import_dir . DIRECTORY_SEPARATOR . $file,
874  $file
875  );
876  break;
877  }
878  closedir($dir_handle);
879  break;
880 
881  case 'MapArea':
882  $this->in_map_area = false;
883  $this->media_item->addMapArea($this->map_area);
884  break;
885 
886  case 'Properties':
887  $this->in_properties = false;
888  if ($this->content_object->getType() == 'lm') {
889  $this->content_object->update();
890  }
891  break;
892 
893  case 'FileItem':
894  $this->in_file_item = false;
895  // only update new file items
896  if ($this->file_item->getImportId()) {
897  $this->file_item->update();
898  }
899  break;
900 
901 
902  case 'Table':
903  unset($this->container[count($this->container) - 1]);
904  break;
905 
906  case 'Glossary':
907  $this->in_glossary = false;
908  break;
909 
910  case 'GlossaryTerm':
911  $term = trim($this->chr_data);
912  $term = str_replace('&lt;', '<', $term);
913  $term = str_replace('&gt;', '>', $term);
914  $this->glossary_term->setTerm($term);
915  $this->glossary_term->create();
916  $iia = explode('_', $this->glossary_term->getImportId());
917  $this->glossary_term_map[(int) $iia[count($iia) - 1]] = $this->glossary_term->getId();
918  break;
919 
920  case 'Paragraph':
921  $this->inside_code = false;
922  break;
923 
924  case 'Definition':
925  $this->in_glossary_definition = false;
926  $this->page_object->updateFromXML();
927  $this->page_object->buildDom();
928  $this->glossary_term->setShortText($this->page_object->getFirstParagraphText());
929  $this->glossary_term->update();
930  if ($this->page_object->containsIntLink()) {
931  $this->pages_to_parse['term:' . $this->page_object->getId()] = 'term:' . $this->page_object->getId();
932  }
933  if ($this->page_object->needsImportParsing()) {
934  $this->pages_to_parse['term:' . $this->page_object->getId()] = 'term:' . $this->page_object->getId();
935  }
936  break;
937 
938  case 'Format':
939  if ($this->in_media_item) {
940  $this->media_item->setFormat(trim($this->chr_data));
941  }
942  break;
943 
944  case 'Title':
945  if ($this->in_media_object) {
946  $this->media_object->setTitle(trim($this->chr_data));
947  }
948  break;
949 
950  case 'Description':
951  case 'Language':
952  break;
953 
954  case 'Caption':
955  if ($this->in_media_object) {
956  $this->media_item->setCaption(trim($this->chr_data));
957  }
958  break;
959 
960  case 'TextRepresentation':
961  if ($this->in_media_object) {
962  $this->media_item->setTextRepresentation(trim($this->chr_data));
963  }
964  break;
965 
966  // Location
967  case 'Location':
968  // TODO: adapt for files in 'real' subdirectories
969  if ($this->in_media_item) {
970  $this->media_item->setLocationType($this->loc_type);
971  if ($this->loc_type == 'Reference') {
972  $this->media_item->setLocation(str_replace('&', '&amp;', trim($this->chr_data)));
973  } else {
974  $this->media_item->setLocation(trim($this->chr_data));
975  }
976  }
977  if ($this->in_file_item) {
978  // set file name from xml file
979  $this->file_item->setFileName(trim($this->chr_data));
980 
981  // special handling for file names with special characters
982  // (e.g. '&gt;')
983  if ($this->file_item->getType() == 'file' &&
984  is_int(strpos($this->chr_data, '&')) &&
985  is_int(strpos($this->chr_data, ';'))) {
986  $source_dir = $this->importdir . DIRECTORY_SEPARATOR . 'objects' . DIRECTORY_SEPARATOR .
987  $this->file_item->getImportId();
988 
989  // read 'physical' file name from directory
990  if ($dir = opendir($source_dir)) {
991  while (false !== ($file = readdir($dir))) {
992  if ($file != '.' && $file != '..') {
993  $this->file_item->setFileName($file);
994  }
995  }
996  closedir($dir);
997  }
998  }
999 
1000  // set file item title
1001  $this->file_item->setTitle(trim($this->chr_data));
1002  }
1003  break;
1004  }
1005  $this->endElement($a_name);
1006  $this->chr_data = '';
1007  }
1008 
1009  public function handlerCharacterData($a_xml_parser, string $a_data): void
1010  {
1011  // the parser converts '&gt;' to '>' and '&lt;' to '<'
1012  // in character data, but we don't want that, because it's the
1013  // way we mask user html in our content, so we convert back...
1014 
1015  $a_data = str_replace('<', '&lt;', $a_data);
1016  $a_data = str_replace('>', '&gt;', $a_data);
1017 
1018 
1019  // DELETE WHITESPACES AND NEWLINES OF CHARACTER DATA
1020  $a_data = preg_replace('/\n/', '', $a_data);
1021  if (!$this->inside_code) {
1022  $a_data = preg_replace('/\t+/', '', $a_data);
1023  }
1024 
1025  $this->chr_data .= $a_data;
1026 
1027  if (!empty($a_data) || $a_data === '0') {
1028  // append all data to page, if we are within PageObject,
1029  // but not within MetaData or MediaObject
1030  if (($this->in_page_object || $this->in_glossary_definition)
1031  && !$this->in_media_object) {
1032  $this->page_object->appendXMLContent($a_data);
1033  }
1034 
1035  switch ($this->getCurrentElement()) {
1036  case 'IntLink':
1037  case 'ExtLink':
1038  if ($this->in_map_area) {
1039  $this->map_area->appendTitle($a_data);
1040  }
1041  break;
1042  }
1043  }
1044  }
1045 
1049  public function getGlossaryTermMap(): array
1050  {
1051  return $this->glossary_term_map;
1052  }
1053 
1059  private function parseLinkTarget(string $identifier): ?array
1060  {
1061  $link_arr = explode('_', $identifier);
1062 
1063  if (count($link_arr) !== 4
1064  || $link_arr[0] !== 'il'
1065  || !is_numeric($link_arr[1])
1066  || !is_numeric($link_arr[3])
1067  ) {
1068  return null;
1069  }
1070 
1071  return [
1072  'target_inst' => (int) $link_arr[1],
1073  'target_type' => (string) $link_arr[2],
1074  'target_id' => (int) $link_arr[3]
1075  ];
1076  }
1077 }
processPagesToParse()
parse pages that contain files, mobs and/or internal links
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=[])
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...
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...
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...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
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:26
Class ilMediaItem Media Item, component of a media object (file or reference)
Legacy Content Object Parser.
resolveIntLinks(?array $a_link_map=null)
Resolves all internal link targets of the page, if targets are available (after import) ...
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 _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...
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)
setImportMapping(?ilImportMapping $mapping=null)
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