ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilFileXMLParser.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
18 
19 include_once './Services/Xml/classes/class.ilSaxParser.php';
20 include_once 'Modules/File/classes/class.ilFileException.php';
21 include_once 'Services/Utilities/classes/class.ilFileUtils.php';
22 
24 {
25  public static $CONTENT_NOT_COMPRESSED = 0;
26  public static $CONTENT_GZ_COMPRESSED = 1;
27  public static $CONTENT_ZLIB_COMPRESSED = 2;
28  public static $CONTENT_COPY = 4;
29  // begin-patch fm
30  public static $CONTENT_REST = 5;
31  // end-patch fm
37  public $file;
44  public $obj_id;
50  public $result;
56  public $mode;
62  //var $content;
63 
69  public $tmpFilename;
75  //var $content;
76 
80  protected $version = null;
84  protected $action = null;
88  protected $rollback_version = null;
92  protected $rollback_user_id = null;
96  protected $max_version = null;
100  protected $date = null;
104  protected $usr_id = null;
108  protected $versions = [];
109 
110 
120  public function __construct($file, $a_xml_data, $obj_id = -1, $mode = 0)
121  {
122  parent::__construct();
123  $this->file = $file;
124  $this->setXMLContent($a_xml_data);
125  $this->obj_id = $obj_id;
126  $this->result = false;
127  $this->mode = $mode;
128  }
129 
130 
136  public function setImportDirectory($a_val)
137  {
138  $this->importDirectory = $a_val;
139  }
140 
141 
147  public function getImportDirectory()
148  {
149  return $this->importDirectory;
150  }
151 
152 
160  public function setHandlers($a_xml_parser)
161  {
162  xml_set_object($a_xml_parser, $this);
163  xml_set_element_handler($a_xml_parser, 'handlerBeginTag', 'handlerEndTag');
164  xml_set_character_data_handler($a_xml_parser, 'handlerCharacterData');
165  }
166 
167 
178  public function handlerBeginTag($a_xml_parser, $a_name, $a_attribs)
179  {
180  global $DIC;
181  $ilErr = $DIC['ilErr'];
182 
183  global $DIC;
184  $ilLog = $DIC['ilLog'];
185 
186  switch ($a_name) {
187  case 'File':
188  if (isset($a_attribs["obj_id"])) {
189  $read_obj_id = ilUtil::__extractId($a_attribs["obj_id"], IL_INST_ID);
190  if ($this->obj_id != -1 && (int) $read_obj_id != -1 && (int) $this->obj_id != (int) $read_obj_id) {
191  throw new ilFileException(
192  "Object IDs (xml $read_obj_id and argument " . $this->obj_id . ") do not match!",
194  );
195  }
196  }
197  if (isset($a_attribs["type"])) {
198  $this->file->setFileType($a_attribs["type"]);
199  }
200  $this->file->setVersion($a_attribs["version"]); // Selected version
201  $this->file->setMaxVersion($a_attribs["max_version"]);
202  $this->file->setAction($a_attribs["action"]);
203  $this->file->setRollbackVersion($a_attribs["rollback_version"]);
204  $this->file->setRollbackUserId($a_attribs["rollback_user_id"]);
205  break;
206  case 'Content': // Old import files
207  case 'Version':
208  if ($a_name === "Version" && !isset($a_attribs["mode"])) {
209  // Old import files
210  $this->version = null;
211  if ($this->date === null) {
212  // Version tag comes after Content tag. Take only first (= Should be latest)
213  $this->date = $a_attribs["date"];
214  $this->usr_id = $a_attribs["usr_id"];
215  $this->versions[0]["date"] = $this->date;
216  $this->versions[0]["usr_id"] = $this->usr_id;
217  }
218  break;
219  }
220 
222  $this->isReadingFile = true;
223  $this->tmpFilename = ilUtil::ilTempnam();
224  #echo $a_attribs["mode"];
225  if (isset($a_attribs["mode"])) {
226  if ($a_attribs["mode"] == "GZIP") {
227  if (!function_exists("gzread")) {
228  throw new ilFileException("Deflating with gzip is not supported", ilFileException::$ID_DEFLATE_METHOD_MISMATCH);
229  }
230 
232  } elseif ($a_attribs["mode"] == "ZLIB") {
233  if (!function_exists("gzuncompress")) {
234  throw new ilFileException("Deflating with zlib (compress/uncompress) is not supported", ilFileException::$ID_DEFLATE_METHOD_MISMATCH);
235  }
236 
238  } elseif ($a_attribs["mode"] == "COPY") {
239  $this->mode = ilFileXMLParser::$CONTENT_COPY;
240  } // begin-patch fm
241  elseif ($a_attribs['mode'] == 'REST') {
242  $this->mode = ilFileXMLParser::$CONTENT_REST;
243  }
244  // end-patch fm
245  }
246 
247  if ($a_name === "Version") {
248  $this->version = $a_attribs["version"];
249  $this->max_version = $a_attribs["max_version"];
250  $this->date = $a_attribs["date"];
251  $this->usr_id = $a_attribs["usr_id"];
252  $this->action = $a_attribs["action"];
253  $this->rollback_version = $a_attribs["rollback_version"];
254  $this->rollback_user_id = $a_attribs["rollback_user_id"];
255  } else {
256  // Old import files
257  //$this->version = $this->file->getVersion();
258  $this->version = 1;
259  $this->file->setVersion($this->version);
260  }
261  }
262  }
263 
264 
271  public function handlerEndTag($a_xml_parser, $a_name)
272  {
273  $this->cdata = trim($this->cdata);
274 
275  $GLOBALS['DIC']['ilLog']->write(__METHOD__ . ': ' . $this->cdata);
276 
277  switch ($a_name) {
278  case 'File':
279  $this->result = true;
280  break;
281  case 'Filename':
282  if (strlen($this->cdata) == 0) {
283  throw new ilFileException("Filename ist missing!");
284  }
285 
286  $this->file->setFilename(basename(self::normalizeRelativePath($this->cdata)));
287  $this->file->setTitle($this->cdata);
288 
289  break;
290  case 'Title':
291  $this->file->setTitle(trim($this->cdata));
292  break;
293  case 'Description':
294  $this->file->setDescription(trim($this->cdata));
295  break;
296  case 'Rating':
297  $this->file->setRating((bool) $this->cdata);
298  break;
299  case 'Content': // Old import files
300  case 'Version':
301  if ($a_name === "Version" && $this->version === null) {
302  // Old import files
303  break;
304  }
305 
306  $GLOBALS['DIC']['ilLog']->write($this->mode);
307  $this->isReadingFile = false;
308  $baseDecodedFilename = ilUtil::ilTempnam();
309  if ($this->mode == ilFileXMLParser::$CONTENT_COPY) {
310  $this->tmpFilename = $this->getImportDirectory() . "/" . self::normalizeRelativePath($this->cdata);
311  } // begin-patch fm
312  elseif ($this->mode == ilFileXMLParser::$CONTENT_REST) {
313  include_once './Services/WebServices/Rest/classes/class.ilRestFileStorage.php';
314  $storage = new ilRestFileStorage();
315  $this->tmpFilename = $storage->getStoredFilePath(self::normalizeRelativePath($this->cdata));
316  if (!ilFileUtils::fastBase64Decode($this->tmpFilename, $baseDecodedFilename)) {
317  throw new ilFileException("Base64-Decoding failed", ilFileException::$DECOMPRESSION_FAILED);
318  }
319  $this->tmpFilename = $baseDecodedFilename;
320  } // end-patch fm
321  else {
322  if (!ilFileUtils::fastBase64Decode($this->tmpFilename, $baseDecodedFilename)) {
323  throw new ilFileException("Base64-Decoding failed", ilFileException::$DECOMPRESSION_FAILED);
324  }
325  if ($this->mode == ilFileXMLParser::$CONTENT_GZ_COMPRESSED) {
326  if (!ilFileUtils::fastGunzip($baseDecodedFilename, $this->tmpFilename)) {
327  throw new ilFileException("Deflating with fastzunzip failed", ilFileException::$DECOMPRESSION_FAILED);
328  }
329  unlink($baseDecodedFilename);
330  } elseif ($this->mode == ilFileXMLParser::$CONTENT_ZLIB_COMPRESSED) {
331  if (!ilFileUtils::fastGunzip($baseDecodedFilename, $this->tmpFilename)) {
332  throw new ilFileException("Deflating with fastDecompress failed", ilFileException::$DECOMPRESSION_FAILED);
333  }
334  unlink($baseDecodedFilename);
335  } else {
336  $this->tmpFilename = $baseDecodedFilename;
337  }
338  }
339 
340  if ($this->version == $this->file->getVersion()) {
341  global $DIC;
342  $rel = LegacyPathHelper::createRelativePath($this->tmpFilename);
343  if ($DIC->filesystem()->temp()->has($rel)) {
344  $this->file->setFileSize($DIC->filesystem()->temp()->getSize($rel, DataSize::Byte)->getSize());
345  } else {
346  $array = $DIC->filesystem()->temp()->listContents(dirname($rel));
347  $first_file = reset($array);
348 
349  if ($first_file instanceof \ILIAS\Filesystem\DTO\Metadata) {
350  $this->file->setFileSize($DIC->filesystem()->temp()->getSize($first_file->getPath(),
351  DataSize::Byte)->getSize());
352  }
353  }
354 
355  // if no file type is given => lookup mime type
356  if (!$this->file->getFileType()) {
357  global $DIC;
358  $ilLog = $DIC['ilLog'];
359 
360  #$ilLog->write(__METHOD__.': Trying to detect mime type...');
361  include_once('./Services/Utilities/classes/class.ilFileUtils.php');
362  $this->file->setFileType(ilFileUtils::_lookupMimeType($this->tmpFilename));
363  }
364  }
365 
366  $this->versions[] = [
367  "version" => $this->version,
368  "max_version" => $this->max_version,
369  "tmpFilename" => $this->tmpFilename,
370  "date" => $this->date,
371  "usr_id" => $this->usr_id,
372  "action" => $this->action,
373  "rollback_version" => $this->rollback_version,
374  "rollback_user_id" => $this->rollback_user_id,
375  ];
376  $this->version = null;
377  $this->date = null;
378  $this->usr_id = null;
379  break;
380  }
381 
382  $this->cdata = '';
383 
384  return;
385  }
386 
387 
394  public function handlerCharacterData($a_xml_parser, $a_data)
395  {
396  if ($a_data != "\n") {
397  // begin-patch fm
398  if ($this->isReadingFile && $this->mode != ilFileXMLParser::$CONTENT_COPY
399  && $this->mode != ilFileXMLParser::$CONTENT_REST
400  ) { // begin-patch fm
401  $handle = fopen($this->tmpFilename, "a");
402  fwrite($handle, $a_data);
403  fclose($handle);
404  } else {
405  $this->cdata .= $a_data;
406  }
407  }
408  }
409 
410 
416  public function setFileContents()
417  {
418  // Delete exists version 1 history
419  ilHistory::_removeEntriesForObject($this->file->getId());
420 
421  foreach ($this->versions as $version) {
422  if (!file_exists($version["tmpFilename"])) {
423  // try to get first file of dir
424  $files = scandir(dirname($version["tmpFilename"]));
425  $version["tmpFilename"] = rtrim(dirname($version["tmpFilename"]),
426  "/") . "/" . $files[2];// because [0] = "." [1] = ".."
427  if (!file_exists($version["tmpFilename"])) {
428  ilLoggerFactory::getLogger('file')->error(__METHOD__ . ' "' . ($version["tmpFilename"]) . '" file not found.');
429 
430  continue;
431  }
432  }
433 
434  if (filesize($version["tmpFilename"]) == 0) {
435  continue;
436  }
437 
438  $filedir = $this->file->getDirectory($version["version"]);
439 
440  if (!is_dir($filedir)) {
441  $this->file->createDirectory();
442  ilUtil::makeDir($filedir);
443  }
444 
445  $filename = $filedir . "/" . $this->file->getFileName();
446 
447  if (file_exists($filename)) {
448  unlink($filename);
449  }
450 
451  ilFileUtils::rename($version["tmpFilename"], $filename);
452 
453  // Add version history
454  // bugfix mantis 26236: add rollback info to version instead of max_version to ensure compatibility with older ilias versions
455  if ($version["rollback_version"] != "" and $version["rollback_version"] != null
456  and $version["rollback_user_id"] != "" and $version["rollback_user_id"] != null
457  ) {
458  ilHistory::_createEntry($this->file->getId(), $version["action"], basename($filename) . ","
459  . $version["version"] . "|"
460  . $version["rollback_version"] . "|"
461  . $version["rollback_user_id"] . ","
462  . $version["max_version"]);
463  } else {
464  if ($version["action"] != "" and $version["action"] != null) {
465  ilHistory::_createEntry($this->file->getId(), $version["action"], basename($filename) . ","
466  . $version["version"] . ","
467  . $version["max_version"]);
468  } else {
469  ilHistory::_createEntry($this->file->getId(), "new_version", basename($filename) . ","
470  . $version["version"] . ","
471  . $version["max_version"]);
472  }
473  }
474  }
475  }
476 
477 
483  public function updateFileContents()
484  {
485  if ($this->setFileContents()) {
486  require_once("./Services/History/classes/class.ilHistory.php");
487  if ($this->file->getRollbackVersion() != "" and $this->file->getRollbackVersion() != null
488  and $this->file->getRollbackUserId() != "" and $this->file->getRollbackUserId() != null
489  ) {
490  ilHistory::_createEntry($this->file->getId(), $this->file->getAction(), $this->file->getFilename() . "," . $this->file->getVersion() . "," . $this->file->getMaxVersion()
491  . "|" . $this->file->getRollbackVersion() . "|" . $this->file->getRollbackUserId());
492  } else {
493  if ($this->file->getAction() != "" and $this->file->getAction() != null) {
494  ilHistory::_createEntry($this->file->getId(), $this->file->getAction(), $this->file->getFilename() . "," . $this->file->getVersion() . "," . $this->file->getMaxVersion());
495  } else {
496  ilHistory::_createEntry($this->file->getId(), "replace", $this->file->getFilename() . "," . $this->file->getVersion() . "," . $this->file->getMaxVersion());
497  }
498  }
499  $this->file->addNewsNotification("file_updated");
500  }
501  }
502 
503 
511  public function start()
512  {
513  $this->startParsing();
514 
515  return $this->result > 0;
516  }
517 
518 
531  public static function normalizeRelativePath($path)
532  {
533  $path = str_replace('\\', '/', $path);
534 
535  while (preg_match('#\p{C}+|^\./#u', $path)) {
536  $path = preg_replace('#\p{C}+|^\./#u', '', $path);
537  }
538 
539  $parts = [];
540  foreach (explode('/', $path) as $part) {
541  switch ($part) {
542  case '':
543  case '.':
544  break;
545  case '..':
546  array_pop($parts);
547  break;
548  default:
549  $parts[] = $part;
550  break;
551  }
552  }
553 
554  return implode('/', $parts);
555  }
556 }
setImportDirectory($a_val)
Set import directory.
$path
Definition: aliased.php:25
static normalizeRelativePath($path)
Normalize relative directories in a path.
$files
Definition: metarefresh.php:49
global $DIC
Definition: saml.php:7
startParsing()
stores xml data in array
Exercise XML Parser which completes/updates a given file by an xml string.
Class BaseForm.
static _createEntry( $a_obj_id, $a_action, $a_info_params="", $a_obj_type="", $a_user_comment="", $a_update_last=false)
Creates a new history entry for an object.
$ilErr
Definition: raiseError.php:18
handlerEndTag($a_xml_parser, $a_name)
handler for end of element
Base class for sax-based expat parsing extended classes need to overwrite the method setHandlers and ...
start()
starts parsing an changes object by side effect.
static _lookupMimeType($a_file)
static rename($a_source, $a_target)
Rename a file.
handlerCharacterData($a_xml_parser, $a_data)
handler for character data
Class to report exception.
$filename
Definition: buildRTE.php:89
static makeDir($a_dir)
creates a new directory and inherits all filesystem permissions of the parent directory You may pass ...
__construct($file, $a_xml_data, $obj_id=-1, $mode=0)
Constructor.
setHandlers($a_xml_parser)
set event handlers
static ilTempnam($a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
File storage handling.
static fastBase64Decode($filein, $fileout)
decodes base encoded file row by row to prevent memory exhaust
updateFileContents()
update file according to filename and version and create history entry has to be called after (!) fil...
static _removeEntriesForObject($a_obj_id)
remove all history entries for an object
static getLogger($a_component_id)
Get component logger.
fastGunzip($in, $out)
fast uncompressing the file with the zlib-extension without memory consumption
getImportDirectory()
Get import directory.
setXMLContent($a_xml_content)
static __extractId($ilias_id, $inst_id)
extract ref id from role title, e.g.
Class FlySystemFileAccessTest.
$GLOBALS['JPEG_Segment_Names']
Global Variable: XMP_tag_captions.
handlerBeginTag($a_xml_parser, $a_name, $a_attribs)
handler for begin of element
setFileContents()
update file according to filename and version, does not update history has to be called after (!) fil...