ILIAS  trunk Revision v11.0_alpha-1731-gff9cd7e2bd3
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
class.ilImport.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
27 use ILIAS\Export\ImportHandler\Factory as ilImportFactory;
28 use ILIAS\Export\ImportStatus\ilFactory as ilImportStatusFactory;
33 
38 class ilImport
39 {
40  protected ilLogger $log;
42  protected array $entity_types = [];
44  protected string $comp = '';
45  protected string $current_comp = '';
46  protected string $entities = "";
47  protected string $tmp_import_dir = "";
49  protected array $skip_entity = array();
50  protected array $configs = array();
51  protected array $skip_importer = [];
52  protected Archives $archives;
54  protected ilImportFactory $import;
55  protected ilImportStatusFactory $import_status;
56 
57  public function __construct(int $a_target_id = 0)
58  {
59  global $DIC;
60  $this->objDefinition = $DIC['objDefinition'];
61  $this->mapping = new ilImportMapping();
62  $this->mapping->setTargetId($a_target_id);
63  $this->log = $DIC->logger()->exp();
64  $this->archives = $DIC->archives();
65  $this->filesystem = $DIC->filesystem();
66  $this->import = new ilImportFactory();
67  $this->import_status = new ilImportStatusFactory();
68  }
69 
73  public function getConfig(string $a_comp): ilImportConfig
74  {
75  // if created, return existing config object
76  if (isset($this->configs[$a_comp])) {
77  return $this->configs[$a_comp];
78  }
79  // create instance of export config object
80  $comp_arr = explode("/", $a_comp);
81  $a_class = "il" . ($comp_arr[2] ?? $comp_arr[1]) . "ImportConfig";
82  $imp_config = new $a_class();
83  $this->configs[$a_comp] = $imp_config;
84  return $imp_config;
85  }
86 
87  public function getMapping(): ilImportMapping
88  {
89  return $this->mapping;
90  }
91 
92  final public function setEntityTypes(array $a_val): void
93  {
94  $this->entity_types = $a_val;
95  }
96 
97  final public function getEntityTypes(): array
98  {
99  return $this->entity_types;
100  }
101 
105  public function addSkipEntity(string $a_component, string $a_entity, bool $skip = true): void
106  {
107  $this->skip_entity[$a_component][$a_entity] = $skip;
108  }
109 
110  public function addSkipImporter(string $a_component, bool $skip = true): void
111  {
112  $this->skip_importer[$a_component] = $skip;
113  }
114 
118  final public function importEntity(
119  string $a_tmp_file,
120  string $a_filename,
121  string $a_entity,
122  string $a_component,
123  bool $a_copy_file = false
124  ): int {
125  return $this->importObject(null, $a_tmp_file, $a_filename, $a_entity, $a_component, $a_copy_file);
126  }
127 
128  protected function unzipFile(
129  string $zip_file_name,
130  string $path_to_tmp_upload,
131  bool $file_is_on_server
133  $import_status_collection = $this->import_status->collection()->withNumberingEnabled(true);
134  $tmp_dir_info = new SplFileInfo(ilFileUtils::ilTempnam());
135  $this->filesystem->temp()->createDir($tmp_dir_info->getFilename());
136  $target_file_path_str = $tmp_dir_info->getRealPath() . DIRECTORY_SEPARATOR . $zip_file_name;
137  $target_dir_path_str = substr($target_file_path_str, 0, -4);
138  // Copy/move zip to tmp out directory
139  // File is not uploaded with the ilias storage system, therefore the php copy functions are used.
140  if (
141  (
142  $file_is_on_server &&
143  !copy($path_to_tmp_upload, $target_file_path_str)
144  ) || (
145  !$file_is_on_server &&
146  !ilFileUtils::moveUploadedFile($path_to_tmp_upload, $zip_file_name, $target_file_path_str)
147  )
148  ) {
149  return $import_status_collection->withAddedStatus(
150  $this->import_status->handler()
151  ->withType(StatusType::FAILED)
152  ->withContent($this->import_status->content()->builder()->string()->withString(
153  'Could not move file.'
154  ))
155  );
156  }
158  $unzip = $this->archives->unzip(
159  Streams::ofResource(fopen($target_file_path_str, 'rb')),
160  $this->archives->unzipOptions()
161  ->withZipOutputPath($tmp_dir_info->getRealPath())
162  ->withDirectoryHandling(ZipDirectoryHandling::ENSURE_SINGLE_TOP_DIR)
163  );
164  return $unzip->extract()
165  ? $import_status_collection->withAddedStatus(
166  $this->import_status->handler()->withType(StatusType::SUCCESS)
167  ->withContent($this->import_status->content()->builder()->string()->withString($target_dir_path_str))
168  )
169  : $import_status_collection->withAddedStatus(
170  $this->import_status->handler()->withType(StatusType::FAILED)
171  ->withContent($this->import_status->content()->builder()->string()->withString('Unzip failed.'))
172  );
173  }
174 
176  {
177  $export_files = $this->import->file()->xml()->export()->collection();
178  $manifest_handlers = $this->import->file()->xml()->manifest()->collection();
179  $statuses = $this->import_status->collection();
180  // Find export xmls
181  try {
182  $manifest_handlers = $manifest_handlers->withElement(
183  $this->import->file()->xml()->manifest()->handler()->withFileInfo($manifest_spl)
184  );
185  // VALIDATE 1st manifest file, can be either export-set or export-file
186  $statuses = $manifest_handlers->validateElements();
187  if ($statuses->hasStatusType(StatusType::FAILED)) {
188  return $statuses;
189  }
190  // If export set look for the export file manifests + VALIDATE
191  if ($manifest_handlers->containsExportObjectType(ExportObjectType::EXPORT_SET)) {
192  $manifest_handlers = $manifest_handlers->findNextFiles();
193  $statuses = $manifest_handlers->validateElements();
194  }
195  if ($statuses->hasStatusType(StatusType::FAILED)) {
196  return $statuses;
197  }
198  // If export file look for the export xmls
199  if ($manifest_handlers->containsExportObjectType(ExportObjectType::EXPORT_FILE)) {
200  foreach ($manifest_handlers as $manfiest_file_handler) {
201  $export_files = $export_files->withMerged($manfiest_file_handler->findXMLFileHandlers());
202  }
203  }
204  } catch (ilImportStatusException $e) {
205  $this->checkStatuses($e->getStatuses());
206  }
207  // VALIDATE export xmls
208  $path_to_export_item_child = $this->import->path()->handler()
209  ->withStartAtRoot(true)
210  ->withNode($this->import->path()->node()->simple()->withName('exp:Export'))
211  ->withNode($this->import->path()->node()->simple()->withName('exp:ExportItem'))
212  ->withNode($this->import->path()->node()->anyNode());
213  $component_tree = $this->import->parser()->nodeInfo()->tree()->handler();
214  foreach ($export_files as $export_file) {
215  if ($export_file->isContainerExportXML()) {
216  $component_tree = $component_tree->withRootInFile($export_file, $path_to_export_item_child);
217  break;
218  }
219  }
220  foreach ($export_files as $export_file) {
221  $found_statuses = $export_file->buildValidationSets();
222  if (!$found_statuses->hasStatusType(StatusType::FAILED)) {
223  $found_statuses = $this->import->validation()->handler()->validateSets(
224  $export_file->getValidationSets()
225  );
226  }
227  if (!$found_statuses->hasStatusType(StatusType::FAILED)) {
228  $statuses = $statuses->getMergedCollectionWith($found_statuses);
229  continue;
230  }
231  $info_str = "<br> Location: " . $export_file->getILIASPath($component_tree) . "<br>";
232  $found_statuses = $found_statuses->mergeContentToElements(
233  $this->import_status->content()->builder()->string()->withString($info_str)
234  );
235  $statuses = $statuses->getMergedCollectionWith($found_statuses);
236  }
237  return $statuses;
238  }
239 
240  protected function checkStatuses(ilImportStatusHandlerCollectionInterface $import_status_collection): void
241  {
242  if ($import_status_collection->hasStatusType(StatusType::FAILED)) {
243  throw new ilImportException($import_status_collection
244  ->withNumberingEnabled(true)
246  }
247  }
248 
249  final public function importObject(
250  ?object $a_new_obj,
251  string $a_tmp_file,
252  string $a_filename, // Verwerfen (sollte zip sein)
253  string $a_type,
254  string $a_comp = "",
255  bool $a_copy_file = false
256  ): ?int {
257  // Unzip
258  $status_collection = $this->unzipFile(
259  $a_filename,
260  $a_tmp_file,
261  $a_copy_file
262  );
263  $this->checkStatuses($status_collection);
264  $success_status = $status_collection->getCollectionOfAllByType(StatusType::SUCCESS)->current();
265  $target_dir_info = new SplFileInfo($success_status->getContent()->toString());
266  $delete_dir_info = new SplFileInfo($target_dir_info->getPath());
267  $manifest_spl = new SplFileInfo($target_dir_info->getRealPath() . DIRECTORY_SEPARATOR . 'manifest.xml');
268  // Validate manifest files
269  try {
270  $status_collection = $this->validateXMLFiles($manifest_spl);
271  $this->checkStatuses($status_collection);
272  } catch (Exception $e) {
273  $this->filesystem->temp()->deleteDir($delete_dir_info->getFilename());
274  throw $e;
275  }
276  // Import
277  try {
278  $this->setTemporaryImportDir($target_dir_info->getRealPath());
279  $ret = $this->doImportObject($target_dir_info->getRealPath(), $a_type, $a_comp, $target_dir_info->getPath());
280  $new_id = null;
281  if (is_array($ret) && array_key_exists('new_id', $ret)) {
282  $new_id = $ret['new_id'];
283  }
284  } catch (Exception $e) {
285  $this->filesystem->temp()->deleteDir($delete_dir_info->getFilename());
286  throw $e;
287  }
288  // Delete tmp files
289  $this->filesystem->temp()->deleteDir($delete_dir_info->getFilename());
290  return $new_id;
291  }
292 
293  public function importFromDirectory(string $dir, string $a_type, string $a_comp): ?int
294  {
295  $ret = $this->doImportObject($dir, $a_type, $a_comp);
296  if (is_array($ret)) {
297  return $ret['new_id'];
298  }
299  return null;
300  }
301 
306  protected function setTemporaryImportDir(string $a_val)
307  {
308  $this->tmp_import_dir = $a_val;
309  }
310 
315  public function getTemporaryImportDir(): string
316  {
317  return $this->tmp_import_dir;
318  }
319 
323  protected function doImportObject(
324  string $dir,
325  string $a_type,
326  string $a_component = "",
327  string $a_tmpdir = ""
328  ): array {
329  if ($a_component == "") {
330  $a_component = ilImportExportFactory::getComponentForExport($a_type);
331  }
332  $this->comp = $a_component;
333 
334  // get import class
335  $success = true;
336 
337  // process manifest file
338  if (!is_file($dir . "/manifest.xml")) {
339  $mess = (DEVMODE)
340  ? 'Manifest file not found: "' . $dir . "/manifest.xml" . '".'
341  : 'Manifest file not found: "manifest.xml."';
343  $e->setManifestDir($dir);
344  $e->setTmpDir($a_tmpdir);
345  throw $e;
346  }
347  $parser = new ilManifestParser($dir . "/manifest.xml");
348  $this->mapping->setInstallUrl($parser->getInstallUrl());
349  $this->mapping->setInstallId($parser->getInstallId());
350 
351  // check for correct type
352  if ($parser->getMainEntity() != $a_type) {
354  "Object type does not match. Import file has type '" .
355  $parser->getMainEntity() . "' but import being processed for '" . $a_type . "'."
356  );
357  }
358 
359  // process export files
360  $expfiles = $parser->getExportFiles();
361 
362  $all_importers = array();
363  foreach ($expfiles as $expfile) {
364  $comp = $expfile["component"];
365 
366  if (isset($this->skip_importer[$comp]) && $this->skip_importer[$comp] === true) {
367  continue;
368  }
369 
370  $class = ilImportExportFactory::getImporterClass($comp);
371 
372  // log a warning for inactive page component plugins, but continue import
373  // page content will be imported, but not its additional data
374  // (other plugins throw an exception in ilImportExportFactory)
375  if ($class == '') {
376  $this->log->warning("no class found for component: $comp");
377  continue;
378  }
379 
380  $this->log->debug("create new class = $class");
381 
382  $this->importer = new $class();
383  $this->importer->setImport($this);
384  $all_importers[] = $this->importer;
385  $this->importer->setImportDirectory($dir);
386  $this->importer->init();
387  $this->current_comp = $comp;
388  try {
389  $this->log->debug("Process file: " . $dir . "/" . $expfile["path"]);
390  $parser = new ilExportFileParser($dir . "/" . $expfile["path"], $this, "processItemXml");
391  } catch (Exception $e) {
392  $this->log->error("Import failed: " . $e->getMessage());
393  $this->log->error('XML failed: ' . file_get_contents($dir . '/' . $expfile['path']));
394  throw $e;
395  }
396  }
397 
398  // write import ids before(!) final processing
399  $obj_map = $this->getMapping()->getMappingsOfEntity('components/ILIAS/Container', 'objs');
400  if (is_array($obj_map)) {
401  foreach ($obj_map as $obj_id_old => $obj_id_new) {
403  (int) $obj_id_new,
404  "il_" . $this->mapping->getInstallId() . "_" . ilObject::_lookupType((int) $obj_id_new) . "_" . $obj_id_old
405  );
406  }
407  }
408 
409  // final processing
410  foreach ($all_importers as $imp) {
411  $this->log->debug("Call finalProcessing for: " . get_class($imp));
412  $imp->finalProcessing($this->mapping);
413  }
414 
415  // we should only get on mapping here
416  $top_mapping = $this->mapping->getMappingsOfEntity($this->comp, $a_type);
417 
418  $new_id = (int) current($top_mapping);
419  return array(
420  'new_id' => $new_id,
421  'importers' => $all_importers
422  );
423  }
424 
428  public function processItemXml(
429  string $a_entity,
430  string $a_schema_version,
431  string $a_id,
432  string $a_xml,
433  string $a_install_id,
434  string $a_install_url
435  ): void {
436  // skip
437  if (isset($this->skip_entity[$this->current_comp][$a_entity]) &&
438  $this->skip_entity[$this->current_comp][$a_entity]) {
439  return;
440  }
441 
442  if ($this->objDefinition->isRBACObject($a_entity) &&
443  $this->getMapping()->getMapping('components/ILIAS/Container', 'imported', $a_id)) {
444  $this->log->info('Ignoring referenced ' . $a_entity . ' with id ' . $a_id);
445  return;
446  }
447  $this->importer->setInstallId($a_install_id);
448  $this->importer->setInstallUrl($a_install_url);
449  $this->importer->setSchemaVersion($a_schema_version);
450  $this->importer->setSkipEntities($this->skip_entity);
451  $this->importer->importXmlRepresentation($a_entity, $a_id, $a_xml, $this->mapping);
452 
453  // Store information about imported obj_ids in mapping to avoid double imports of references
454  if ($this->objDefinition->isRBACObject($a_entity)) {
455  $this->getMapping()->addMapping('components/ILIAS/Container', 'imported', $a_id, '1');
456  }
457  }
458 }
doImportObject(string $dir, string $a_type, string $a_component="", string $a_tmpdir="")
Import repository object export file.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
array $skip_entity
checkStatuses(ilImportStatusHandlerCollectionInterface $import_status_collection)
static _writeImportId(int $obj_id, string $import_id)
write import id to db (static)
setEntityTypes(array $a_val)
ilLogger $log
ilImportStatusFactory $import_status
validateXMLFiles(SplFileInfo $manifest_spl)
string $comp
string $entities
importEntity(string $a_tmp_file, string $a_filename, string $a_entity, string $a_component, bool $a_copy_file=false)
Import entity.
Import class.
string $tmp_import_dir
array $skip_importer
__construct(int $a_target_id=0)
addSkipEntity(string $a_component, string $a_entity, bool $skip=true)
Add skip entity.
importObject(?object $a_new_obj, string $a_tmp_file, string $a_filename, string $a_type, string $a_comp="", bool $a_copy_file=false)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
string $current_comp
ilXmlImporter $importer
getTemporaryImportDir()
Get temporary import directory.
ilImportFactory $import
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
ilImportMapping $mapping
global $DIC
Definition: shib_login.php:22
getConfig(string $a_comp)
Get configuration (note that configurations are optional, null may be returned!)
addSkipImporter(string $a_component, bool $skip=true)
Import configuration class parent class.
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
manifest.xml file not found-exception for import
array $entity_types
Archives $archives
Manifest parser for ILIAS standard export files.
static ilTempnam(?string $a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
setImportDirectory(string $a_val)
processItemXml(string $a_entity, string $a_schema_version, string $a_id, string $a_xml, string $a_install_id, string $a_install_url)
Process item xml.
importFromDirectory(string $dir, string $a_type, string $a_comp)
setTemporaryImportDir(string $a_val)
Set temporary import directory.
static _lookupType(int $id, bool $reference=false)
Filesystems $filesystem
array $configs
ilObjectDefinition $objDefinition
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Xml importer class.
The Filesystems interface defines the access methods which can be used to fetch the different filesys...
Definition: Filesystems.php:29