ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilExport.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
25 class ilExport
26 {
30  public string $export_run_dir = '';
31 
32  protected ilLogger $log;
33 
34  private static array $new_file_structure = array('cat',
35  'exc',
36  'crs',
37  'sess',
38  'file',
39  'grp',
40  'frm',
41  'usr',
42  'catr',
43  'crsr',
44  'grpr'
45  );
46  // @todo this should be part of module.xml and be parsed in the future
47  private static array $export_implementer = array("tst", "lm", "glo", "sahs");
48  private array $configs = [];
49 
50  private array $cnt = [];
51  private ?ilXmlWriter $manifest_writer = null;
52 
53  public function __construct()
54  {
55  global $DIC;
56  $this->log = $DIC->logger()->exp();
57  }
58 
65  public function getConfig(string $a_comp): ilExportConfig
66  {
67  // if created, return existing config object
68  if (isset($this->configs[$a_comp])) {
69  return $this->configs[$a_comp];
70  }
71 
72  // create instance of export config object
73  $comp_arr = explode("/", $a_comp);
74  $a_class = "il" . $comp_arr[1] . "ExportConfig";
75  $exp_config = new $a_class();
76  $this->configs[$a_comp] = $exp_config;
77  return $exp_config;
78  }
79 
85  public static function _getValidExportSubItems(int $a_ref_id): array
86  {
87  global $DIC;
88 
89  $tree = $DIC->repositoryTree();
90 
91  $valid_items = array();
92  $sub_items = $tree->getSubTree($tree->getNodeData($a_ref_id));
93  foreach ($sub_items as $sub_item) {
94  if (in_array($sub_item["type"], self::$export_implementer)) {
95  $valid_items[] = [
96  "type" => (string) $sub_item["type"],
97  "title" => (string) $sub_item["title"],
98  "ref_id" => (int) $sub_item["child"],
99  "obj_id" => (int) $sub_item["obj_id"],
100  "timestamp" => ilExport::_getLastExportFileDate($sub_item["obj_id"], "xml", $sub_item["type"])
101  ];
102  }
103  }
104  return $valid_items;
105  }
106 
113  public static function _getLastExportFileDate(int $a_obj_id, string $a_type = "", string $a_obj_type = ""): int
114  {
115  $files = ilExport::_getExportFiles($a_obj_id, $a_type, $a_obj_type);
116  if (is_array($files)) {
117  $files = ilArrayUtil::sortArray($files, "timestamp", "desc");
118  return (int) $files[0]["timestamp"];
119  }
120  return 0;
121  }
122 
130  public static function _getLastExportFileInformation(
131  int $a_obj_id,
132  string $a_type = "",
133  string $a_obj_type = ""
134  ): ?array {
135  $files = ilExport::_getExportFiles($a_obj_id, $a_type, $a_obj_type);
136  if (is_array($files)) {
137  $files = ilArrayUtil::sortArray($files, "timestamp", "desc");
138  return $files[0];
139  }
140  return null;
141  }
142 
151  public static function _getExportDirectory(
152  int $a_obj_id,
153  string $a_type = "xml",
154  string $a_obj_type = "",
155  string $a_entity = ""
156  ): string {
157  global $DIC;
158 
159  $logger = $DIC->logger()->exp();
160  $objDefinition = $DIC['objDefinition'];
161 
162  $ent = ($a_entity == "")
163  ? ""
164  : "_" . $a_entity;
165 
166  if ($a_obj_type == "") {
167  $a_obj_type = ilObject::_lookupType($a_obj_id);
168  }
169 
170  if (in_array($a_obj_type, self::$new_file_structure)) {
171  $dir = ilFileUtils::getDataDir() . DIRECTORY_SEPARATOR;
172  $dir .= 'il' . $objDefinition->getClassName($a_obj_type) . $ent . DIRECTORY_SEPARATOR;
173  $dir .= self::createPathFromId($a_obj_id, $a_obj_type) . DIRECTORY_SEPARATOR;
174  $dir .= ($a_type == 'xml' ? 'export' : 'export_' . $a_type);
175  return $dir;
176  }
177 
178  $exporter_class = ilImportExportFactory::getExporterClass($a_obj_type);
179  $export_dir = call_user_func(
180  array($exporter_class, 'lookupExportDirectory'),
181  $a_obj_type,
182  $a_obj_id,
183  $a_type,
184  $a_entity
185  );
186 
187  $logger->debug('Export dir is ' . $export_dir);
188  return $export_dir;
189  }
190 
197  public static function _getExportFiles(int $a_obj_id, $a_export_types = "", string $a_obj_type = ""): array
198  {
199  if ($a_obj_type == "") {
200  $a_obj_type = ilObject::_lookupType($a_obj_id);
201  }
202 
203  if ($a_export_types == "") {
204  $a_export_types = array("xml");
205  }
206  if (!is_array($a_export_types)) {
207  $a_export_types = array($a_export_types);
208  }
209 
210  // initialize array
211  $file = array();
212 
213  $types = $a_export_types;
214 
215  foreach ($types as $type) {
216  $dir = ilExport::_getExportDirectory($a_obj_id, $type, $a_obj_type);
217 
218  // quit if import dir not available
219  if (!is_dir($dir) || !is_writable($dir)) {
220  continue;
221  }
222 
223  // open directory
224  $h_dir = dir($dir);
225 
226  // get files and save the in the array
227  while ($entry = $h_dir->read()) {
228  if ($entry !== "." &&
229  $entry !== ".." &&
230  substr($entry, -4) === ".zip" &&
231  preg_match("/^[0-9]{10}_{2}[0-9]+_{2}(" . $a_obj_type . "_)*[0-9]+\.zip\$/", $entry)) {
232  $ts = substr($entry, 0, strpos($entry, "__"));
233  $file[$entry . $type] = [
234  "type" => (string) $type,
235  "file" => (string) $entry,
236  "size" => (int) filesize($dir . "/" . $entry),
237  "timestamp" => (int) $ts
238  ];
239  }
240  }
241 
242  // close import directory
243  $h_dir->close();
244  }
245 
246  // sort files
247  ksort($file);
248  reset($file);
249  return $file;
250  }
251 
252  public static function _createExportDirectory(
253  int $a_obj_id,
254  string $a_export_type = "xml",
255  string $a_obj_type = ""
256  ): bool {
257  global $DIC;
258 
259  $ilErr = $DIC['ilErr'];
260 
261  if ($a_obj_type == "") {
262  $a_obj_type = ilObject::_lookupType($a_obj_id);
263  }
264 
265  $edir = ilExport::_getExportDirectory($a_obj_id, $a_export_type, $a_obj_type);
267  return true;
268  }
269 
274  public static function _generateIndexFile(
275  string $a_filename,
276  int $a_obj_id,
277  array $a_files,
278  string $a_type = ""
279  ): void {
280  global $DIC;
281 
282  $lng = $DIC->language();
283  $lng->loadLanguageModule("export");
284 
285  if ($a_type == "") {
286  $a_type = ilObject::_lookupType($a_obj_id);
287  }
288  $a_tpl = new ilGlobalTemplate("tpl.main.html", true, true);
289  $location_stylesheet = ilUtil::getStyleSheetLocation();
290  $a_tpl->setVariable("LOCATION_STYLESHEET", $location_stylesheet);
291  $a_tpl->loadStandardTemplate();
292  $a_tpl->setTitle(ilObject::_lookupTitle($a_obj_id));
293  $a_tpl->setDescription($lng->txt("export_export_date") . ": " .
294  date('Y-m-d H:i:s', time()) . " (" . date_default_timezone_get() . ")");
295  $f_tpl = new ilTemplate("tpl.export_list.html", true, true, "Services/Export");
296  foreach ($a_files as $file) {
297  $f_tpl->setCurrentBlock("file_row");
298  $f_tpl->setVariable("TITLE", $file["title"]);
299  $f_tpl->setVariable("TYPE", $lng->txt("obj_" . $file["type"]));
300  $f_tpl->setVariable("FILE", $file["file"]);
301  $f_tpl->parseCurrentBlock();
302  }
303  $a_tpl->setContent($f_tpl->get());
304  $index_content = $a_tpl->getSpecial("DEFAULT", false, false, false, true, false, false);
305 
306  $f = fopen($a_filename, "w");
307  fwrite($f, $index_content);
308  fclose($f);
309  }
310 
311  /***
312  * - Walk through sequence
313  * - Each step in sequence creates one xml file,
314  * e.g. Services/Mediapool/set_1.xml
315  * - manifest.xml lists all files
316  * <manifest>
317  * <xmlfile path="Services/Mediapool/set_1.xml"/>
318  * ...
319  * </manifest
320  */
321 
329  public function exportObject(
330  string $a_type,
331  int $a_id,
332  string $a_target_release = ""
333  ): array {
334  $this->log->debug("export type: $a_type, id: $a_id, target_release: " . $a_target_release);
335 
336  // if no target release specified, use latest major release number
337  if ($a_target_release == "") {
338  $v = explode(".", ILIAS_VERSION_NUMERIC);
339  $a_target_release = $v[0] . "." . $v[1] . ".0";
340  $this->log->debug("target_release set to: " . $a_target_release);
341  }
342 
343  // manifest writer
344  $this->manifest_writer = new ilXmlWriter();
345  $this->manifest_writer->xmlHeader();
346  $this->manifest_writer->xmlStartTag(
347  'Manifest',
348  array(
349  "MainEntity" => $a_type,
350  "Title" => ilObject::_lookupTitle($a_id),
351  /* "TargetRelease" => $a_target_release, */
352  "InstallationId" => IL_INST_ID,
353  "InstallationUrl" => ILIAS_HTTP_PATH
354  )
355  );
356 
357  // get export class
358  ilExport::_createExportDirectory($a_id, "xml", $a_type);
359  $export_dir = ilExport::_getExportDirectory($a_id, "xml", $a_type);
360  $ts = time();
361 
362  // Workaround for test assessment
363  $sub_dir = $ts . '__' . IL_INST_ID . '__' . $a_type . '_' . $a_id;
364  $new_file = $sub_dir . '.zip';
365 
366  $this->export_run_dir = $export_dir . "/" . $sub_dir;
367  ilFileUtils::makeDirParents($this->export_run_dir);
368  $this->log->debug("export dir: " . $this->export_run_dir);
369 
370  $this->cnt = array();
371 
372  $class = ilImportExportFactory::getExporterClass($a_type);
373  $comp = ilImportExportFactory::getComponentForExport($a_type);
374 
375  $success = $this->processExporter($comp, $class, $a_type, $a_target_release, [$a_id]);
376 
377  $this->manifest_writer->xmlEndTag('Manifest');
378  $this->manifest_writer->xmlDumpFile($this->export_run_dir . "/manifest.xml", false);
379 
380  // zip the file
381  $this->log->debug("zip: " . $export_dir . "/" . $new_file);
382  $this->log->debug("run dir: " . $this->export_run_dir);
383  ilFileUtils::zip($this->export_run_dir, $export_dir . "/" . $new_file);
384  ilFileUtils::delDir($this->export_run_dir);
385 
386  // Store info about export
387  if ($success) {
388  $exp = new ilExportFileInfo($a_id);
389  $exp->setVersion($a_target_release);
390  $exp->setCreationDate(new ilDateTime($ts, IL_CAL_UNIX));
391  $exp->setExportType('xml');
392  $exp->setFilename($new_file);
393  $exp->create();
394  }
395 
396  return array(
397  "success" => $success,
398  "file" => $new_file,
399  "directory" => $export_dir
400  );
401  }
402 
411  public function exportEntity(
412  string $a_entity,
413  string $a_id,
414  string $a_target_release,
415  string $a_component,
416  string $a_title,
417  string $a_export_dir,
418  string $a_type_for_file = ""
419  ): array {
420  global $DIC;
421 
422  $objDefinition = $DIC['objDefinition'];
423  $tpl = $DIC['tpl'];
424 
425  // if no target release specified, use latest major release number
426  if ($a_target_release == "") {
427  $v = explode(".", ILIAS_VERSION_NUMERIC);
428  $a_target_release = $v[0] . "." . $v[1] . ".0";
429  }
430 
431  if ($a_type_for_file == "") {
432  $a_type_for_file = $a_entity;
433  }
434 
435  $comp = $a_component;
436  $c = explode("/", $comp);
437  $class = "il" . $c[1] . "Exporter";
438 
439  // manifest writer
440  $this->manifest_writer = new ilXmlWriter();
441  $this->manifest_writer->xmlHeader();
442  $this->manifest_writer->xmlStartTag(
443  'Manifest',
444  array(
445  "MainEntity" => $a_entity,
446  "Title" => $a_title,
447  /* "TargetRelease" => $a_target_release, */
448  "InstallationId" => IL_INST_ID,
449  "InstallationUrl" => ILIAS_HTTP_PATH
450  )
451  );
452 
453  $export_dir = $a_export_dir;
454  $ts = time();
455 
456  // determine file name and subdirectory
457  $sub_dir = $ts . '__' . IL_INST_ID . '__' . $a_type_for_file . '_' . $a_id;
458  $new_file = $sub_dir . '.zip';
459 
460  $this->export_run_dir = $export_dir . "/" . $sub_dir;
461  ilFileUtils::makeDirParents($this->export_run_dir);
462 
463  $this->cnt = array();
464  $success = $this->processExporter($comp, $class, $a_entity, $a_target_release, [$a_id]);
465  $this->manifest_writer->xmlEndTag('Manifest');
466  $this->manifest_writer->xmlDumpFile($this->export_run_dir . "/manifest.xml", false);
467 
468  // zip the file
469  ilFileUtils::zip($this->export_run_dir, $export_dir . "/" . $new_file);
470  ilFileUtils::delDir($this->export_run_dir);
471 
472  return array(
473  "success" => $success,
474  "file" => $new_file,
475  "directory" => $export_dir
476  );
477  }
478 
489  public function processExporter(
490  string $a_comp,
491  string $a_class,
492  string $a_entity,
493  string $a_target_release,
494  array $a_id = null
495  ): bool {
496  $success = true;
497  $this->log->debug("process exporter, comp: " . $a_comp . ", class: " . $a_class . ", entity: " . $a_entity .
498  ", target release " . $a_target_release . ", id: " . print_r($a_id, true));
499 
500  if (!is_array($a_id)) {
501  if ($a_id == "") {
502  return true;
503  }
504  $a_id = array($a_id);
505  }
506 
507  // get exporter object
508  if (!class_exists($a_class)) {
509  $export_class_file = "./" . $a_comp . "/classes/class." . $a_class . ".php";
510  if (!is_file($export_class_file)) {
511  throw new ilExportException('Export class file "' . $export_class_file . '" not found.');
512  }
513  }
514 
515  $exp = new $a_class();
516  $exp->setExport($this);
517  if (!isset($this->cnt[$a_comp])) {
518  $this->cnt[$a_comp] = 1;
519  } else {
520  $this->cnt[$a_comp]++;
521  }
522  $set_dir_relative = $a_comp . "/set_" . $this->cnt[$a_comp];
523  $set_dir_absolute = $this->export_run_dir . "/" . $set_dir_relative;
524  ilFileUtils::makeDirParents($set_dir_absolute);
525  $this->log->debug("dir: " . $set_dir_absolute);
526 
527  $this->log->debug("init exporter");
528  $exp->init();
529 
530  // process head dependencies
531  $this->log->debug("process head dependencies for " . $a_entity);
532  $sequence = $exp->getXmlExportHeadDependencies($a_entity, $a_target_release, $a_id);
533  foreach ($sequence as $s) {
534  $comp = explode("/", $s["component"]);
535  $exp_class = "il" . $comp[1] . "Exporter";
536  $s = $this->processExporter(
537  $s["component"],
538  $exp_class,
539  $s["entity"],
540  $a_target_release,
541  $s["ids"]
542  );
543  if (!$s) {
544  $success = false;
545  }
546  }
547 
548  // write export.xml file
549  $export_writer = new ilXmlWriter();
550  $export_writer->xmlHeader();
551 
552  $sv = $exp->determineSchemaVersion($a_entity, $a_target_release);
553  $sv["uses_dataset"] ??= false;
554  $sv['xsd_file'] ??= '';
555  $this->log->debug("schema version for entity: $a_entity, target release: $a_target_release");
556  $this->log->debug("...is: " . $sv["schema_version"] . ", namespace: " . $sv["namespace"] .
557  ", xsd file: " . $sv["xsd_file"] . ", uses_dataset: " . ((int) $sv["uses_dataset"]));
558 
559  $attribs = array("InstallationId" => IL_INST_ID,
560  "InstallationUrl" => ILIAS_HTTP_PATH,
561  "Entity" => $a_entity,
562  "SchemaVersion" => $sv["schema_version"],
563  /* "TargetRelease" => $a_target_release, */
564  "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
565  "xmlns:exp" => "http://www.ilias.de/Services/Export/exp/4_1",
566  "xsi:schemaLocation" => "http://www.ilias.de/Services/Export/exp/4_1 " . ILIAS_HTTP_PATH . "/xml/ilias_export_4_1.xsd"
567  );
568  if ($sv["namespace"] != "" && $sv["xsd_file"] != "") {
569  $attribs["xsi:schemaLocation"] .= " " . $sv["namespace"] . " " .
570  ILIAS_HTTP_PATH . "/xml/" . $sv["xsd_file"];
571  $attribs["xmlns"] = $sv["namespace"];
572  }
573  if ($sv["uses_dataset"]) {
574  $attribs["xsi:schemaLocation"] .= " " .
575  "http://www.ilias.de/Services/DataSet/ds/4_3 " . ILIAS_HTTP_PATH . "/xml/ilias_ds_4_3.xsd";
576  $attribs["xmlns:ds"] = "http://www.ilias.de/Services/DataSet/ds/4_3";
577  }
578  $export_writer->xmlStartTag('exp:Export', $attribs);
579 
580  $dir_cnt = 1;
581  foreach ($a_id as $id) {
582  $exp->setExportDirectories(
583  $set_dir_relative . "/expDir_" . $dir_cnt,
584  $set_dir_absolute . "/expDir_" . $dir_cnt
585  );
586  $export_writer->xmlStartTag('exp:ExportItem', array("Id" => $id));
587  //$xml = $exp->getXmlRepresentation($a_entity, $a_target_release, $id);
588  $xml = $exp->getXmlRepresentation($a_entity, $sv["schema_version"], (string) $id);
589  $export_writer->appendXML($xml);
590  $export_writer->xmlEndTag('exp:ExportItem');
591  $dir_cnt++;
592  }
593 
594  $export_writer->xmlEndTag('exp:Export');
595  $export_writer->xmlDumpFile($set_dir_absolute . "/export.xml", false);
596 
597  $this->manifest_writer->xmlElement(
598  "ExportFile",
599  array("Component" => $a_comp, "Path" => $set_dir_relative . "/export.xml")
600  );
601 
602  // process tail dependencies
603  $this->log->debug("process tail dependencies of " . $a_entity);
604  $sequence = $exp->getXmlExportTailDependencies($a_entity, $a_target_release, $a_id);
605  foreach ($sequence as $s) {
606  if (empty((array) ($s["ids"] ?? []))) {
607  continue;
608  }
609 
610  $comp = explode("/", $s["component"]);
611  $exp_class = "il" . $comp[1] . "Exporter";
612  $s = $this->processExporter(
613  $s["component"],
614  $exp_class,
615  $s["entity"],
616  $a_target_release,
617  (array) $s["ids"]
618  );
619  if (!$s) {
620  $success = false;
621  }
622  }
623 
624  $this->log->debug("returning " . ((int) $success) . " for " . $a_entity);
625  return $success;
626  }
627 
628  protected static function createPathFromId(int $a_container_id, string $a_name): string
629  {
630  $max_exponent = 3;
631  $factor = 100;
632 
633  $path = [];
634  $found = false;
635  $num = $a_container_id;
636  $path_string = '';
637  for ($i = $max_exponent; $i > 0; $i--) {
638  $factor = pow($factor, $i);
639  if (($tmp = (int) ($num / $factor)) or $found) {
640  $path[] = $tmp;
641  $num = $num % $factor;
642  $found = true;
643  }
644  }
645  if (count($path)) {
646  $path_string = (implode('/', $path) . '/');
647  }
648  return $path_string . $a_name . '_' . $a_container_id;
649  }
650 }
static _getLastExportFileDate(int $a_obj_id, string $a_type="", string $a_obj_type="")
Get date of last export file.
const IL_INST_ID
Definition: constants.php:40
const ILIAS_VERSION_NUMERIC
special template class to simplify handling of ITX/PEAR
getConfig(string $a_comp)
Get configuration (note that configurations are optional, null may be returned!)
static array $new_file_structure
exportEntity(string $a_entity, string $a_id, string $a_target_release, string $a_component, string $a_title, string $a_export_dir, string $a_type_for_file="")
Export an ILIAS entity.
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...
Export.
const IL_CAL_UNIX
static getStyleSheetLocation(string $mode="output", string $a_css_name="", string $a_css_location="")
get full style sheet file name (path inclusive) of current user
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
static _getExportDirectory(int $a_obj_id, string $a_type="xml", string $a_obj_type="", string $a_entity="")
Get export directory for an repository object.
$ilErr
Definition: raiseError.php:17
static _getValidExportSubItems(int $a_ref_id)
Get a list of subitems of a repository resource, that implement the export.
$path
Definition: ltiservices.php:32
global $DIC
Definition: feed.php:28
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
processExporter(string $a_comp, string $a_class, string $a_entity, string $a_target_release, array $a_id=null)
Process exporter.
static _lookupTitle(int $obj_id)
$lng
static _generateIndexFile(string $a_filename, int $a_obj_id, array $a_files, string $a_type="")
Generates an index.html file including links to all xml files included (for container exports) ...
static _createExportDirectory(int $a_obj_id, string $a_export_type="xml", string $a_obj_type="")
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
string $export_run_dir
static _getExportFiles(int $a_obj_id, $a_export_types="", string $a_obj_type="")
ilXmlWriter $manifest_writer
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
exportObject(string $a_type, int $a_id, string $a_target_release="")
Export an ILIAS object (the object type must be known by objDefinition)
static getDataDir()
get data directory (outside webspace)
static _getLastExportFileInformation(int $a_obj_id, string $a_type="", string $a_obj_type="")
Get last export file information.
static array $export_implementer
static createPathFromId(int $a_container_id, string $a_name)
array $configs
static zip(string $a_dir, string $a_file, bool $compress_content=false)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
ilLogger $log
static _lookupType(int $id, bool $reference=false)
static sortArray(array $array, string $a_array_sortby_key, string $a_array_sortorder="asc", bool $a_numeric=false, bool $a_keep_keys=false)