ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilImport.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
27use ILIAS\Export\ImportHandler\Factory as ilImportFactory;
28use ILIAS\Export\ImportStatus\ilFactory as ilImportStatusFactory;
29use ILIAS\Export\ImportStatus\I\ilCollectionInterface as ilImportStatusHandlerCollectionInterface;
31use ILIAS\Export\ImportStatus\Exception\ilException as ilImportStatusException;
33
39{
40 protected ilLogger $log;
42 protected array $entity_types = [];
43 protected ?ilXmlImporter $importer = null;
44 protected string $comp = '';
45 protected string $current_comp = '';
46 protected string $entities = "";
47 protected string $tmp_import_dir = "";
48 protected ?ilImportMapping $mapping = null;
49 protected array $skip_entity = array();
50 protected array $configs = array();
51 protected array $skip_importer = [];
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 {
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
132 ): ilImportStatusHandlerCollectionInterface {
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->getPathname() . 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->getPathname())
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
175 protected function validateXMLFiles(SplFileInfo $manifest_spl): ilImportStatusHandlerCollectionInterface
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->getPathname() . 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->getPathname());
279 $ret = $this->doImportObject($target_dir_info->getPathname(), $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}
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
return true
static ilTempnam(?string $a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
Import configuration class parent class.
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...
Import class.
string $comp
ilLogger $log
array $skip_importer
getTemporaryImportDir()
Get temporary import directory.
ilImportStatusFactory $import_status
string $entities
getConfig(string $a_comp)
Get configuration (note that configurations are optional, null may be returned!)
validateXMLFiles(SplFileInfo $manifest_spl)
__construct(int $a_target_id=0)
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.
setEntityTypes(array $a_val)
array $configs
ilImportFactory $import
addSkipImporter(string $a_component, bool $skip=true)
Filesystems $filesystem
setTemporaryImportDir(string $a_val)
Set temporary import directory.
ilXmlImporter $importer
importFromDirectory(string $dir, string $a_type, string $a_comp)
string $tmp_import_dir
array $skip_entity
ilImportMapping $mapping
addSkipEntity(string $a_component, string $a_entity, bool $skip=true)
Add skip entity.
array $entity_types
Archives $archives
doImportObject(string $dir, string $a_type, string $a_component="", string $a_tmpdir="")
Import repository object export file.
string $current_comp
ilObjectDefinition $objDefinition
checkStatuses(ilImportStatusHandlerCollectionInterface $import_status_collection)
importObject(?object $a_new_obj, string $a_tmp_file, string $a_filename, string $a_type, string $a_comp="", bool $a_copy_file=false)
importEntity(string $a_tmp_file, string $a_filename, string $a_entity, string $a_component, bool $a_copy_file=false)
Import entity.
Component logger with individual log levels by component id.
manifest.xml file not found-exception for import
Manifest parser for ILIAS standard export files.
parses the objects.xml it handles the xml-description of all ilias objects
static _lookupType(int $id, bool $reference=false)
static _writeImportId(int $obj_id, string $import_id)
write import id to db (static)
Xml importer class.
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
The Filesystems interface defines the access methods which can be used to fetch the different filesys...
Definition: Filesystems.php:30
if(!file_exists('../ilias.ini.php'))
global $DIC
Definition: shib_login.php:26