‪TYPO3CMS  11.5
Import.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
18 namespace ‪TYPO3\CMS\Impexp;
19 
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
43 
50 {
51  public const ‪IMPORT_MODE_FORCE_UID = 'force_uid';
52  public const ‪IMPORT_MODE_AS_NEW = 'as_new';
53  public const ‪IMPORT_MODE_EXCLUDE = 'exclude';
54  public const ‪IMPORT_MODE_IGNORE_PID = 'ignore_pid';
55  public const ‪IMPORT_MODE_RESPECT_PID = 'respect_pid';
56 
57  public const ‪SOFTREF_IMPORT_MODE_EXCLUDE = 'exclude';
58  public const ‪SOFTREF_IMPORT_MODE_EDITABLE = 'editable';
59 
63  protected ‪$mode = 'import';
64 
71  protected ‪$suggestedInsertUids = [];
72 
78  protected ‪$enableLogging = false;
79 
87  protected ‪$importNewId = [];
88 
94  protected ‪$importNewIdPids = [];
95 
99  protected ‪$decompressionAvailable = false;
100 
104  private ‪$supportedFileExtensions = [];
105 
109  protected ‪$isFilesSavedOutsideImportFile = false;
110 
114  public function ‪__construct()
115  {
116  parent::__construct();
117  $this->decompressionAvailable = function_exists('gzuncompress');
118  }
119 
120  /**************************
121  * File Input
122  *************************/
123 
131  public function ‪loadFile(string $fileName, bool $all = false): void
132  {
133  $filePath = GeneralUtility::getFileAbsFileName($fileName);
134 
135  if (empty($filePath)) {
136  $this->‪addError('File path is not valid: ' . $fileName);
137  } elseif (!@is_file($filePath)) {
138  $this->‪addError('File not found: ' . $filePath);
139  }
140 
141  if ($this->‪hasErrors()) {
143  sprintf('Loading of the import file "%s" failed.', $fileName),
144  1484484619
145  );
146  }
147 
148  $pathInfo = pathinfo($filePath);
149  $fileExtension = strtolower($pathInfo['extension']);
150 
151  if (!in_array($fileExtension, $this->‪getSupportedFileExtensions(), true)) {
152  $this->‪addError(
153  sprintf(
154  'File extension "%s" is not valid. Supported file extensions are %s.',
155  $fileExtension,
156  implode(', ', array_map(static function ($supportedFileExtension) {
157  return '"' . $supportedFileExtension . '"';
158  }, $this->‪getSupportedFileExtensions()))
159  )
160  );
161  }
162 
163  if ($this->‪hasErrors() === false) {
164  if (@is_dir($filePath . '.files')) {
165  if (GeneralUtility::isAllowedAbsPath($filePath . '.files')) {
166  // copy the folder lowlevel to typo3temp, because the files would be deleted after import
167  GeneralUtility::copyDirectory($filePath . '.files', $this->‪getOrCreateTemporaryFolderName());
168  } else {
169  $this->‪addError('External import files for the given import source is currently not supported.');
170  }
171  $this->isFilesSavedOutsideImportFile = true;
172  } else {
173  $this->isFilesSavedOutsideImportFile = false;
174  }
175  if ($fileExtension === 'xml') {
176  $xmlContent = (string)file_get_contents($filePath);
177  if (strlen($xmlContent)) {
178  try {
179  ‪$dat = (new ‪Typo3XmlParser())->decode(
180  $xmlContent,
183  ‪Typo3XmlSerializerOptions::LOAD_OPTIONS => \LIBXML_NONET | \LIBXML_NOBLANKS | \LIBXML_PARSEHUGE,
184  // @todo check if needed for imports/throw deprecation for invalid xml
186  ])
187  );
188  $this->dat = is_array(‪$dat) ? ‪$dat : [‪$dat];
189  if ($this->dat['_DOCUMENT_TAG'] === 'T3RecordDocument' && is_array($this->dat['header'] ?? null) && is_array($this->dat['records'] ?? null)) {
190  $this->‪loadInit();
191  } else {
192  $this->‪addError('XML file did not contain proper XML for TYPO3 Import');
193  }
194  } catch (\Throwable $e) {
195  $this->‪addError('XML could not be parsed: ' . $e->getMessage());
196  }
197  } else {
198  $this->‪addError('Error opening file: ' . $filePath);
199  }
200  } elseif ($fileExtension === 't3d') {
201  if ($fd = fopen($filePath, 'rb')) {
202  $this->dat['header'] = $this->‪getNextFilePart($fd, true, 'header');
203  if ($all) {
204  $this->dat['records'] = $this->‪getNextFilePart($fd, true, 'records');
205  $this->dat['files'] = $this->‪getNextFilePart($fd, true, 'files');
206  $this->dat['files_fal'] = $this->‪getNextFilePart($fd, true, 'files_fal');
207  }
208  $this->‪loadInit();
209  fclose($fd);
210  } else {
211  $this->‪addError('Error opening file: ' . $filePath);
212  }
213  }
214  }
215 
216  if ($this->‪hasErrors()) {
218  sprintf('Loading of the import file "%s" failed.', $fileName),
219  1484484620
220  );
221  }
222  }
223 
227  public function ‪getSupportedFileExtensions(): array
228  {
229  if (empty($this->supportedFileExtensions)) {
231  ‪$supportedFileExtensions[] = 'xml';
232  ‪$supportedFileExtensions[] = 't3d';
233  $this->supportedFileExtensions = ‪$supportedFileExtensions;
234  }
236  }
237 
250  protected function ‪getNextFilePart($fd, bool $unserialize = false, string $name = '')
251  {
252  $headerLength = 32 + 1 + 1 + 1 + 10 + 1;
253  $headerString = fread($fd, $headerLength);
254  if (empty($headerString)) {
255  $this->‪addError('File does not contain data for "' . $name . '"');
256  return null;
257  }
258 
259  $header = explode(':', $headerString);
260  if (str_contains($header[0], 'Warning')) {
261  $this->‪addError('File read error: Warning message in file. (' . $headerString . fgets($fd) . ')');
262  return null;
263  }
264  if ((string)$header[3] !== '') {
265  $this->‪addError('File read error: InitString had a wrong length. (' . $name . ')');
266  return null;
267  }
268 
269  $dataString = (string)fread($fd, (int)$header[2]);
270  $isDataCompressed = $header[1] === '1';
271  fread($fd, 1);
272  if (!hash_equals($header[0], md5($dataString))) {
273  $this->‪addError('MD5 check failed (' . $name . ')');
274  return null;
275  }
276 
277  if ($isDataCompressed) {
278  if ($this->decompressionAvailable) {
279  $dataString = (string)gzuncompress($dataString);
280  } else {
281  $this->‪addError('Content read error: This file requires decompression, ' .
282  'but this server does not offer gzcompress()/gzuncompress() functions.');
283  return null;
284  }
285  }
286 
287  return $unserialize ? unserialize($dataString, ['allowed_classes' => false]) : $dataString;
288  }
289 
293  protected function ‪loadInit(): void
294  {
295  $this->relStaticTables = (array)($this->dat['header']['relStaticTables'] ?? []);
296  $this->excludeMap = (array)($this->dat['header']['excludeMap'] ?? []);
297  $this->softrefCfg = (array)($this->dat['header']['softrefCfg'] ?? []);
298  }
299 
300  public function ‪getMetaData(): array
301  {
302  return $this->dat['header']['meta'] ?? [];
303  }
304 
305  /***********************
306  * Import
307  ***********************/
308 
314  public function ‪checkImportPrerequisites(): void
315  {
316  // Check #1: Extension dependencies
317  $extKeysToInstall = [];
318  if (isset($this->dat['header']['extensionDependencies'])) {
319  foreach ($this->dat['header']['extensionDependencies'] as $extKey) {
320  if (!empty($extKey) && !‪ExtensionManagementUtility::isLoaded($extKey)) {
321  $extKeysToInstall[] = $extKey;
322  }
323  }
324  }
325  if (!empty($extKeysToInstall)) {
326  $this->‪addError(
327  sprintf(
328  'Before you can import this file you need to install the extensions "%s".',
329  implode('", "', $extKeysToInstall)
330  )
331  );
332  }
333 
334  // Check #2: Presence of imported storage paths
335  if (!empty($this->dat['header']['records']['sys_file_storage'])) {
336  foreach ($this->dat['header']['records']['sys_file_storage'] as $sysFileStorageUid => $_) {
337  $storageRecord = &$this->dat['records']['sys_file_storage:' . $sysFileStorageUid]['data'];
338  if ($storageRecord['driver'] === 'Local'
339  && $storageRecord['is_writable']
340  && $storageRecord['is_online']
341  ) {
342  $storageMapUid = -1;
343  foreach ($this->storages as $storage) {
344  if ($this->‪isEquivalentStorage($storage, $storageRecord)) {
345  $storageMapUid = $storage->getUid();
346  break;
347  }
348  }
349  // The storage from the import does not have an equivalent storage
350  // in the current instance (same driver, same path, etc.). Before
351  // the storage record can get inserted later on take care the path
352  // it points to really exists and is accessible.
353  if ($storageMapUid === -1) {
354  // Unset the storage record UID when trying to create the storage object
355  // as the record does not already exist in database. The constructor of the
356  // storage object will check whether the target folder exists and set the
357  // isOnline flag depending on the outcome.
358  $storageRecordWithUid0 = $storageRecord;
359  $storageRecordWithUid0['uid'] = 0;
360  $storageObject = $this->‪getStorageRepository()->‪createStorageObject($storageRecordWithUid0);
361  if (!$storageObject->isOnline()) {
362  $configuration = $storageObject->‪getConfiguration();
363  $this->‪addError(
364  sprintf(
365  'The file storage "%s" does not exist. ' .
366  'Please create the directory prior to starting the import!',
367  $storageObject->getName() . $configuration['basePath']
368  )
369  );
370  }
371  }
372  }
373  }
374  }
375 
376  if ($this->‪hasErrors()) {
378  'Prerequisites for file import are not met.',
379  1484484612
380  );
381  }
382  }
383 
389  public function ‪importData(): void
390  {
391  $this->‪initializeImport();
392 
393  // Write sys_file_storages first
395  // Write sys_file records and write the binary file data
396  $this->‪writeSysFileRecords();
397  // Write records, first pages, then the rest
398  // Fields with "hard" relations to database, files and flexform fields are kept empty during this run
399  $this->‪writePages();
400  $this->‪writeRecords();
401  // Finally all the file and database record references must be fixed. This is done after all records have supposedly
402  // been written to database. $this->importMapId will indicate two things:
403  // 1) that a record WAS written to db and
404  // 2) that it has got a new id-number.
405  $this->‪setRelations();
406  // And when all database relations are in place, we can fix file and database relations in flexform fields
407  // - since data structures often depend on relations to a DS record:
408  $this->‪setFlexFormRelations();
409  // Finally, traverse all records and process soft references with substitution attributes.
410  $this->‪processSoftReferences();
411  // Cleanup
413 
414  if ($this->‪hasErrors()) {
415  throw new ‪ImportFailedException('The import has failed.', 1484484613);
416  }
417  }
418 
422  protected function ‪initializeImport(): void
423  {
424  $this->doesImport = true;
425  $this->importMapId = [];
426  $this->importNewId = [];
427  $this->importNewIdPids = [];
428  }
429 
433  protected function ‪writeSysFileStorageRecords(): void
434  {
435  if (!isset($this->dat['header']['records']['sys_file_storage'])) {
436  return;
437  }
438 
439  $importData = [];
440 
441  $storageUidsToBeResetToDefaultStorage = [];
442  foreach ($this->dat['header']['records']['sys_file_storage'] as $sysFileStorageUid => $_) {
443  $storageRecord = &$this->dat['records']['sys_file_storage:' . $sysFileStorageUid]['data'];
444  if ($storageRecord['driver'] === 'Local'
445  && $storageRecord['is_writable']
446  && $storageRecord['is_online']
447  ) {
448  foreach ($this->storages as $storage) {
449  if ($this->‪isEquivalentStorage($storage, $storageRecord)) {
450  $this->importMapId['sys_file_storage'][$sysFileStorageUid] = $storage->getUid();
451  break;
452  }
453  }
454 
455  if (!isset($this->importMapId['sys_file_storage'][$sysFileStorageUid])) {
456  // Local, writable and online storage. May be used later for writing files.
457  // Does not currently exist, mark the storage for import.
458  $this->‪addSingle($importData, 'sys_file_storage', $sysFileStorageUid, 0);
459  }
460  } else {
461  // Storage with non-local drivers can be imported, but must not be used to save files as you cannot
462  // be sure that this is supported. In this case the default storage is used. Non-writable and
463  // non-online storage may be created as duplicates because you were unable to check the detailed
464  // configuration options at that time.
465  $this->‪addSingle($importData, 'sys_file_storage', $sysFileStorageUid, 0);
466  $storageUidsToBeResetToDefaultStorage[] = $sysFileStorageUid;
467  }
468  }
469 
470  // Write new storages to the database
471  $dataHandler = $this->‪createDataHandler();
472  // Because all records are submitted in the correct order with positive pid numbers,
473  // we should internally reverse the order of submission.
474  $dataHandler->reverseOrder = true;
475  $dataHandler->isImporting = true;
476  $dataHandler->start($importData, []);
477  $dataHandler->process_datamap();
478  $this->‪addToMapId($importData, $dataHandler->substNEWwithIDs);
479 
480  // Refresh internal storage representation after potential storage import
481  $this->‪fetchStorages();
482 
483  // Map references of non-local / non-writable / non-online storages to the default storage
484  $defaultStorageUid = $this->defaultStorage !== null ? $this->defaultStorage->getUid() : null;
485  foreach ($storageUidsToBeResetToDefaultStorage as $storageUidToBeResetToDefaultStorage) {
486  $this->importMapId['sys_file_storage'][$storageUidToBeResetToDefaultStorage] = $defaultStorageUid;
487  }
488 
489  // Unset the sys_file_storage records to prevent an import in writeRecords()
490  unset($this->dat['header']['records']['sys_file_storage']);
491  }
492 
501  protected function ‪isEquivalentStorage(ResourceStorage &$storageObject, array &$storageRecord): bool
502  {
503  if ($storageObject->getDriverType() === $storageRecord['driver']
504  && (bool)$storageObject->isWritable() === (bool)$storageRecord['is_writable']
505  && (bool)$storageObject->isOnline() === (bool)$storageRecord['is_online']
506  ) {
507  $storageRecordConfiguration = GeneralUtility::makeInstance(FlexFormService::class)
508  ->convertFlexFormContentToArray($storageRecord['configuration'] ?? '');
509  $storageObjectConfiguration = $storageObject->getConfiguration();
510  if ($storageRecordConfiguration['pathType'] === $storageObjectConfiguration['pathType']
511  && $storageRecordConfiguration['basePath'] === $storageObjectConfiguration['basePath']
512  ) {
513  return true;
514  }
515  }
516  return false;
517  }
518 
522  protected function ‪writeSysFileRecords(): void
523  {
524  if (!isset($this->dat['header']['records']['sys_file'])) {
525  return;
526  }
527 
528  $this->‪addGeneralErrorsByTable('sys_file');
529 
530  $temporaryFolder = $this->‪getOrCreateTemporaryFolderName();
531  $sanitizedFolderMappings = [];
532 
533  foreach ($this->dat['header']['records']['sys_file'] as $sysFileUid => $_) {
534  $fileRecord = &$this->dat['records']['sys_file:' . $sysFileUid]['data'];
535 
536  $temporaryFile = null;
537  $temporaryFilePath = $temporaryFolder . '/' . $fileRecord['sha1'];
538 
539  if ($this->isFilesSavedOutsideImportFile) {
540  if (is_file($temporaryFilePath) && sha1_file($temporaryFilePath) === $fileRecord['sha1']) {
541  $temporaryFile = $temporaryFilePath;
542  } else {
543  $this->‪addError(sprintf(
544  'Error: Temporary file %s could not be found or does not match the checksum!',
545  $temporaryFilePath
546  ));
547  continue;
548  }
549  } else {
550  $fileId = md5($fileRecord['storage'] . ':' . $fileRecord['identifier_hash']);
551  if (isset($this->dat['files_fal'][$fileId]['content'])) {
552  $fileInfo = &$this->dat['files_fal'][$fileId];
553  if (‪GeneralUtility::writeFile($temporaryFilePath, $fileInfo['content'])) {
554  clearstatcache();
555  $temporaryFile = $temporaryFilePath;
556  } else {
557  $this->‪addError(sprintf(
558  'Error: Temporary file %s was not written as it should have been!',
559  $temporaryFilePath
560  ));
561  continue;
562  }
563  } else {
564  $this->‪addError(sprintf('Error: No file found for ID %s', $fileId));
565  continue;
566  }
567  }
568 
569  $storageUid = $this->importMapId['sys_file_storage'][$fileRecord['storage']] ?? $fileRecord['storage'];
570  if (isset($this->storagesAvailableForImport[$storageUid])) {
571  $storage = $this->storagesAvailableForImport[$storageUid];
572  } elseif ($storageUid === 0 || $storageUid === '0') {
573  $storage = $this->‪getStorageRepository()->‪findByUid(0);
574  } elseif ($this->defaultStorage !== null) {
575  $storage = ‪$this->defaultStorage;
576  } else {
577  $this->‪addError(sprintf(
578  'Error: No storage available for the file "%s" with storage uid "%s"',
579  $fileRecord['identifier'],
580  $fileRecord['storage']
581  ));
582  continue;
583  }
584 
586  $file = null;
587  try {
588  if ($storage->hasFile($fileRecord['identifier'])) {
589  $file = $storage->getFile($fileRecord['identifier']);
590  if ($file->getSha1() !== $fileRecord['sha1']) {
591  $file = null;
592  }
593  }
594  } catch (Exception $e) {
595  // @todo: Can this exception be thrown anywhere?
596  $file = null;
597  }
598 
599  if ($file === null) {
600  $folderName = ‪PathUtility::dirname(ltrim($fileRecord['identifier'], '/'));
601  if (in_array($folderName, $sanitizedFolderMappings, true)) {
602  $folderName = $sanitizedFolderMappings[$folderName];
603  }
604  if (!$storage->hasFolder($folderName)) {
605  try {
606  $importFolder = $storage->createFolder($folderName);
607  if ($importFolder->getIdentifier() !== $folderName && !in_array($folderName, $sanitizedFolderMappings, true)) {
608  $sanitizedFolderMappings[$folderName] = $importFolder->getIdentifier();
609  }
610  } catch (Exception $e) {
611  $this->‪addError(sprintf(
612  'Error: Folder "%s" could not be created for file "%s" with storage uid "%s"',
613  $folderName,
614  $fileRecord['identifier'],
615  $fileRecord['storage']
616  ));
617  continue;
618  }
619  } else {
620  $importFolder = $storage->getFolder($folderName);
621  }
622 
623  $this->‪callHook('before_addSysFileRecord', [
624  'fileRecord' => $fileRecord,
625  'importFolder' => $importFolder,
626  'temporaryFile' => $temporaryFile,
627  ]);
628 
629  try {
630  $file = $storage->addFile($temporaryFile, $importFolder, $fileRecord['name']);
631  } catch (Exception $e) {
632  $this->‪addError(sprintf(
633  'Error: File could not be added to the storage: "%s" with storage uid "%s"',
634  $fileRecord['identifier'],
635  $fileRecord['storage']
636  ));
637  continue;
638  }
639 
640  if ($file->getSha1() !== $fileRecord['sha1']) {
641  $this->‪addError(sprintf(
642  'Error: The hash of the written file is not identical to the import data! ' .
643  'File could be corrupted! File: "%s" with storage uid "%s"',
644  $fileRecord['identifier'],
645  $fileRecord['storage']
646  ));
647  }
648  }
649 
650  // save the new uid in the import id map
651  $this->importMapId['sys_file'][$fileRecord['uid']] = $file->getUid();
652  $this->‪fixUidLocalInSysFileReferenceRecords((int)$fileRecord['uid'], $file->getUid());
653  }
654 
655  // unset the sys_file records to prevent an import in writeRecords()
656  unset($this->dat['header']['records']['sys_file']);
657  // remove all sys_file_reference records that point to file records which are unknown
658  // in the system to prevent exceptions
660  }
661 
677  protected function ‪fixUidLocalInSysFileReferenceRecords(int $oldFileUid, int $newFileUid): void
678  {
679  if (!isset($this->dat['header']['records']['sys_file_reference'])) {
680  return;
681  }
682 
683  foreach ($this->dat['header']['records']['sys_file_reference'] as $sysFileReferenceUid => $_) {
684  if (!isset($this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['hasBeenMapped'])
685  && $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data']['uid_local'] == $oldFileUid
686  ) {
687  $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['hasBeenMapped'] = true;
688  $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data']['uid_local'] = $newFileUid;
689  }
690  }
691  }
692 
698  {
699  if (!isset($this->dat['header']['records']['sys_file_reference'])) {
700  return;
701  }
702 
703  foreach ($this->dat['header']['records']['sys_file_reference'] as $sysFileReferenceUid => $_) {
704  $fileReferenceRecord = &$this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data'];
705  if (!in_array($fileReferenceRecord['uid_local'], (array)($this->importMapId['sys_file'] ?? []))) {
706  unset($this->dat['header']['records']['sys_file_reference'][$sysFileReferenceUid]);
707  unset($this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]);
708  $this->‪addError(sprintf(
709  'Error: sys_file_reference record "%s" with relation to sys_file record "%s"'
710  . ', which is not part of the import data, was not imported.',
711  $sysFileReferenceUid,
712  $fileReferenceRecord['uid_local']
713  ));
714  }
715  }
716  }
717 
725  protected function ‪writePages(): void
726  {
727  if (!isset($this->dat['header']['records']['pages'])) {
728  return;
729  }
730 
731  $importData = [];
732 
733  // Add page tree
734  $remainingPages = $this->dat['header']['records']['pages'];
735  if (is_array($this->dat['header']['pagetree'] ?? null)) {
736  $pageList = [];
737  $this->‪flatInversePageTree($this->dat['header']['pagetree'], $pageList);
738  foreach ($pageList as $pageUid => $_) {
739  ‪$pid = $this->dat['header']['records']['pages'][$pageUid]['pid'] ?? null;
740  ‪$pid = $this->importNewIdPids[‪$pid] ?? ‪$this->pid;
741  $this->‪addSingle($importData, 'pages', (int)$pageUid, ‪$pid);
742  unset($remainingPages[$pageUid]);
743  }
744  }
745 
746  // Add remaining pages on root level
747  if (!empty($remainingPages)) {
748  foreach ($remainingPages as $pageUid => $_) {
749  $this->‪addSingle($importData, 'pages', (int)$pageUid, $this->pid);
750  }
751  }
752 
753  // Write pages to the database
754  $dataHandler = $this->‪createDataHandler();
755  $dataHandler->isImporting = true;
756  $this->‪callHook('before_writeRecordsPages', [
757  'tce' => &$dataHandler,
758  'data' => &$importData,
759  ]);
760  $dataHandler->suggestedInsertUids = ‪$this->suggestedInsertUids;
761  $dataHandler->start($importData, []);
762  $dataHandler->process_datamap();
763  $this->‪callHook('after_writeRecordsPages', [
764  'tce' => &$dataHandler,
765  ]);
766  $this->‪addToMapId($importData, $dataHandler->substNEWwithIDs);
767 
768  // Sort pages
769  $this->‪writePagesOrder();
770  }
771 
779  protected function ‪writePagesOrder(): void
780  {
781  if (!$this->update || !is_array($this->dat['header']['pagetree'] ?? null)) {
782  return;
783  }
784 
785  $importCmd = [];
786 
787  // Get uid-pid relations and traverse them in order to map to possible new IDs
788  $pageList = [];
789  $this->‪flatInversePageTree($this->dat['header']['pagetree'], $pageList);
790  foreach ($pageList as $pageUid => $pagePid) {
791  if ($pagePid >= 0 && $this->‪doRespectPid('pages', $pageUid)) {
792  // If the page has been assigned a new ID (because it was created), use that instead!
793  if (!‪MathUtility::canBeInterpretedAsInteger($this->importNewIdPids[$pageUid])) {
794  if ($this->importMapId['pages'][$pageUid]) {
795  $mappedUid = $this->importMapId['pages'][$pageUid];
796  $importCmd['pages'][$mappedUid]['move'] = $pagePid;
797  }
798  } else {
799  $importCmd['pages'][$pageUid]['move'] = $pagePid;
800  }
801  }
802  }
803 
804  // Move pages in the database
805  if (!empty($importCmd)) {
806  $dataHandler = $this->‪createDataHandler();
807  $this->‪callHook('before_writeRecordsPagesOrder', [
808  'tce' => &$dataHandler,
809  'data' => &$importCmd,
810  ]);
811  $dataHandler->start([], $importCmd);
812  $dataHandler->process_cmdmap();
813  $this->‪callHook('after_writeRecordsPagesOrder', [
814  'tce' => &$dataHandler,
815  ]);
816  }
817  }
818 
827  protected function ‪doRespectPid(string $table, int $uid): bool
828  {
829  return ($this->importMode[$table . ':' . $uid] ?? '') !== self::IMPORT_MODE_IGNORE_PID &&
830  (!$this->globalIgnorePid || ($this->importMode[$table . ':' . $uid] ?? '') === self::IMPORT_MODE_RESPECT_PID);
831  }
832 
838  protected function ‪writeRecords(): void
839  {
840  $importData = [];
841 
842  // Write the rest of the records
843  if (is_array($this->dat['header']['records'] ?? null)) {
844  foreach ($this->dat['header']['records'] as $table => $records) {
845  $this->‪addGeneralErrorsByTable($table);
846  if ($table !== 'pages') {
847  foreach ($records as $uid => $record) {
848  // PID: Set the main $this->pid, unless a NEW-id is found
849  ‪$pid = isset($this->importMapId['pages'][$record['pid']])
850  ? (int)$this->importMapId['pages'][$record['pid']]
851  : $this->pid;
852  if (isset(‪$GLOBALS['TCA'][$table]['ctrl']['rootLevel'])) {
853  $rootLevelSetting = (int)‪$GLOBALS['TCA'][$table]['ctrl']['rootLevel'];
854  if ($rootLevelSetting === 1) {
855  ‪$pid = 0;
856  } elseif ($rootLevelSetting === 0 && ‪$pid === 0) {
857  $this->‪addError('Error: Record type ' . $table . ' is not allowed on pid 0');
858  continue;
859  }
860  }
861  // Add record
862  $this->‪addSingle($importData, $table, $uid, ‪$pid);
863  }
864  }
865  }
866  } else {
867  $this->‪addError('Error: No records defined in internal data array.');
868  }
869 
870  // Write records to the database
871  $dataHandler = $this->‪createDataHandler();
872  $this->‪callHook('before_writeRecordsRecords', [
873  'tce' => &$dataHandler,
874  'data' => &$importData,
875  ]);
876  $dataHandler->suggestedInsertUids = ‪$this->suggestedInsertUids;
877  // Because all records are submitted in the correct order with positive pid numbers,
878  // we should internally reverse the order of submission.
879  $dataHandler->reverseOrder = true;
880  $dataHandler->isImporting = true;
881  $dataHandler->start($importData, []);
882  $dataHandler->process_datamap();
883  $this->‪callHook('after_writeRecordsRecords', [
884  'tce' => &$dataHandler,
885  ]);
886  $this->‪addToMapId($importData, $dataHandler->substNEWwithIDs);
887 
888  // Sort records
889  $this->‪writeRecordsOrder();
890  }
891 
899  protected function ‪writeRecordsOrder(): void
900  {
901  if (!$this->update) {
902  return;
903  }
904 
905  $importCmd = [];
906 
907  $pageList = [];
908  if (is_array($this->dat['header']['pagetree'] ?? null)) {
909  $this->‪flatInversePageTree($this->dat['header']['pagetree'], $pageList);
910  }
911  // @todo: drop by-reference and write final $this->dat at the end of method?!
912  if (is_array($this->dat['header']['pid_lookup'] ?? null)) {
913  foreach ($this->dat['header']['pid_lookup'] as ‪$pid => &$recordsByPid) {
914  $mappedPid = $this->importMapId['pages'][‪$pid] ?? ‪$this->pid;
916  foreach ($recordsByPid as $table => &$records) {
917  // If $mappedPid === $this->pid then we are on root level and we can consider to move pages as well!
918  // (they will not be in the page tree!)
919  if ($table !== 'pages' || !isset($pageList[‪$pid])) {
920  foreach (array_reverse(array_keys($records)) as $uid) {
921  if ($this->‪doRespectPid($table, (int)$uid)) {
922  if (isset($this->importMapId[$table][$uid])) {
923  $mappedUid = $this->importMapId[$table][$uid];
924  $importCmd[$table][$mappedUid]['move'] = $mappedPid;
925  }
926  }
927  }
928  }
929  }
930  }
931  }
932  }
933 
934  // Move records in the database
935  if (!empty($importCmd)) {
936  $dataHandler = $this->‪createDataHandler();
937  $this->‪callHook('before_writeRecordsRecordsOrder', [
938  'tce' => &$dataHandler,
939  'data' => &$importCmd,
940  ]);
941  $dataHandler->start([], $importCmd);
942  $dataHandler->process_cmdmap();
943  $this->‪callHook('after_writeRecordsRecordsOrder', [
944  'tce' => &$dataHandler,
945  ]);
946  }
947  }
948 
960  protected function ‪addSingle(array &$importData, string $table, int $uid, ‪$pid): void
961  {
962  // @todo return modified $importData instead of by-reference.
963  if (($this->importMode[$table . ':' . $uid] ?? '') === self::IMPORT_MODE_EXCLUDE) {
964  return;
965  }
966 
967  $record = $this->dat['records'][$table . ':' . $uid]['data'] ?? null;
968 
969  if (!is_array($record)) {
970  if (!($table === 'pages' && $uid === 0)) {
971  // On root level we don't want this error message.
972  $this->‪addError('Error: No record was found in data array!');
973  }
974  return;
975  }
976 
977  // Generate record ID
978  $ID = ‪StringUtility::getUniqueId('NEW');
979  if ($this->update
980  && $this->‪getRecordFromDatabase($table, $uid) !== null
981  && ($this->importMode[$table . ':' . $uid] ?? '') !== self::IMPORT_MODE_AS_NEW
982  ) {
983  $ID = $uid;
984  } elseif ($table === 'sys_file_metadata'
985  && $record['sys_language_uid'] === '0'
986  && isset($this->importMapId['sys_file'][$record['file']])
987  ) {
988  // On adding sys_file records the belonging sys_file_metadata record was also created:
989  // If there is one, the record needs to be overwritten instead of a new one created.
990  $databaseRecord = $this->‪getSysFileMetaDataFromDatabase(
991  0,
992  $this->importMapId['sys_file'][$record['file']],
993  0
994  );
995  if (is_array($databaseRecord)) {
996  $this->importMapId['sys_file_metadata'][$record['uid']] = $databaseRecord['uid'];
997  $ID = $databaseRecord['uid'];
998  }
999  }
1000 
1001  // Mapping of generated record ID to original record UID
1002  $this->importNewId[$table . ':' . $ID] = ['table' => $table, 'uid' => $uid];
1003  if ($table === 'pages') {
1004  $this->importNewIdPids[$uid] = $ID;
1005  }
1006 
1007  // Record data
1008  $importData[$table][$ID] = $record;
1009  $importData[$table][$ID]['tx_impexp_origuid'] = $importData[$table][$ID]['uid'];
1010 
1011  // Record permissions
1012  if ($table === 'pages') {
1013  // Have to reset the user/group IDs so pages are owned by the importing user.
1014  // Otherwise strange things may happen for non-admins!
1015  unset($importData[$table][$ID]['perms_userid']);
1016  unset($importData[$table][$ID]['perms_groupid']);
1017  }
1018 
1019  // Record UID and PID
1020  unset($importData[$table][$ID]['uid']);
1021  // - for existing record
1023  unset($importData[$table][$ID]['pid']);
1024  }
1025  // - for new record
1026  else {
1027  $importData[$table][$ID]['pid'] = ‪$pid;
1028  if ((($this->importMode[$table . ':' . $uid] ?? '') === self::IMPORT_MODE_FORCE_UID && $this->update
1029  || $this->forceAllUids)
1030  && $this->‪getBackendUser()->isAdmin()
1031  ) {
1032  $importData[$table][$ID]['uid'] = $uid;
1033  $this->suggestedInsertUids[$table . ':' . $uid] = 'DELETE';
1034  }
1035  }
1036 
1037  // Record relations
1038  foreach ($this->dat['records'][$table . ':' . $uid]['rels'] as $field => &$relation) {
1039  if (isset($relation['type'])) {
1040  switch ($relation['type']) {
1041  case 'db':
1042  case 'file':
1043  // Set blank now, fix later in setRelations(),
1044  // because we need to know ALL newly created IDs before we can map relations!
1045  // In the meantime we set NO values for relations.
1046  //
1047  // BUT for field uid_local of table sys_file_reference the relation MUST not be cleared here,
1048  // because the value is already the uid of the right imported sys_file record.
1049  // @see fixUidLocalInSysFileReferenceRecords()
1050  // If it's empty or a uid to another record the FileExtensionFilter will throw an exception or
1051  // delete the reference record if the file extension of the related record doesn't match.
1052  if (!($table === 'sys_file_reference' && $field === 'uid_local')
1053  && is_array(‪$GLOBALS['TCA'][$table]['columns'][$field]['config'] ?? false)
1054  ) {
1055  $importData[$table][$ID][$field] = $this->‪getReferenceDefaultValue(‪$GLOBALS['TCA'][$table]['columns'][$field]['config']);
1056  }
1057  break;
1058  case 'flex':
1059  // Set blank now, fix later in setFlexFormRelations().
1060  // In the meantime we set NO values for flexforms - this is mainly because file references
1061  // inside will not be processed properly. In fact references will point to no file
1062  // or existing files (in which case there will be double-references which is a big problem of
1063  // course!).
1064  //
1065  // BUT for the field "configuration" of the table "sys_file_storage" the relation MUST NOT be
1066  // cleared, because the configuration array contains only string values, which are furthermore
1067  // important for the further import, e.g. the base path.
1068  if (!($table === 'sys_file_storage' && $field === 'configuration')) {
1069  $importData[$table][$ID][$field] = $this->‪getReferenceDefaultValue(‪$GLOBALS['TCA'][$table]['columns'][$field]['config']);
1070  }
1071  break;
1072  }
1073  }
1074  }
1075  }
1076 
1083  protected function ‪getReferenceDefaultValue(array $configuration)
1084  {
1085  if (!empty($configuration['MM']) || !empty($configuration['foreign_field'])) {
1086  return 0;
1087  }
1088  if (array_key_exists('default', $configuration)) {
1089  return $configuration['default'];
1090  }
1091  return '';
1092  }
1093 
1102  protected function ‪getSysFileMetaDataFromDatabase(int ‪$pid, int $file, int $sysLanguageUid): ?array
1103  {
1104  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1105  ->getQueryBuilderForTable('sys_file_metadata');
1106 
1107  $databaseRecord = $queryBuilder->select('uid')
1108  ->from('sys_file_metadata')
1109  ->where(
1110  $queryBuilder->expr()->eq(
1111  'file',
1112  $queryBuilder->createNamedParameter($file, ‪Connection::PARAM_INT)
1113  ),
1114  $queryBuilder->expr()->eq(
1115  'sys_language_uid',
1116  $queryBuilder->createNamedParameter($sysLanguageUid, ‪Connection::PARAM_INT)
1117  ),
1118  $queryBuilder->expr()->eq(
1119  'pid',
1120  $queryBuilder->createNamedParameter(‪$pid, ‪Connection::PARAM_INT)
1121  )
1122  )
1123  ->executeQuery()
1124  ->fetchAssociative();
1125 
1126  return is_array($databaseRecord) ? $databaseRecord : null;
1127  }
1136  protected function ‪addToMapId(array $importData, array $substNEWwithIDs): void
1137  {
1138  foreach ($importData as $table => &$records) {
1139  foreach ($records as $ID => &$_) {
1140  $uid = $this->importNewId[$table . ':' . $ID]['uid'];
1141  if (isset($substNEWwithIDs[$ID])) {
1142  $this->importMapId[$table][$uid] = $substNEWwithIDs[$ID];
1143  } elseif ($this->update) {
1144  // Map same ID to same ID....
1145  $this->importMapId[$table][$uid] = $ID;
1146  } else {
1147  // If $this->importMapId contains already the right mapping, skip the error message.
1148  // See special handling of sys_file_metadata in addSingle() => nothing to do.
1149  if (!($table === 'sys_file_metadata'
1150  && isset($this->importMapId[$table][$uid])
1151  && $this->importMapId[$table][$uid] == $ID)
1152  ) {
1153  $this->‪addError(
1154  'Possible error: ' . $table . ':' . $uid . ' had no new id assigned to it. ' .
1155  'This indicates that the record was not added to database during import. ' .
1156  'Please check changelog!'
1157  );
1158  }
1159  }
1160  }
1161  }
1162  }
1163 
1169  protected function ‪createDataHandler(): DataHandler
1170  {
1171  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
1172  $dataHandler->dontProcessTransformations = true;
1173  $dataHandler->enableLogging = ‪$this->enableLogging;
1174  return $dataHandler;
1175  }
1176 
1177  /********************
1178  * Import relations
1179  *******************/
1180 
1189  protected function ‪setRelations(): void
1190  {
1191  $updateData = [];
1192 
1193  foreach ($this->importNewId as $original) {
1194  $table = $original['table'];
1195  $uid = $original['uid'];
1196 
1197  if (isset($this->importMapId[$table][$uid])) {
1198  if (is_array($this->dat['records'][$table . ':' . $uid]['rels'] ?? null)) {
1199  $actualUid = BackendUtility::wsMapId($table, $this->importMapId[$table][$uid]);
1200  foreach ($this->dat['records'][$table . ':' . $uid]['rels'] as $field => $relation) {
1201  // Field "uid_local" of sys_file_reference needs no update because the correct reference uid
1202  // was already written.
1203  // @see ImportExport::fixUidLocalInSysFileReferenceRecords()
1204  if (isset($relation['type']) && !($table === 'sys_file_reference' && $field === 'uid_local')) {
1205  switch ($relation['type']) {
1206  case 'db':
1207  if (is_array($relation['itemArray'] ?? null) && !empty($relation['itemArray'])) {
1208  $fieldTca = &‪$GLOBALS['TCA'][$table]['columns'][$field];
1209  $actualRelations = $this->‪remapRelationsOfField($relation['itemArray'], $fieldTca['config']);
1210  $updateData[$table][$actualUid][$field] = implode(',', $actualRelations);
1211  }
1212  break;
1213  case 'file':
1214  if (is_array($relation['newValueFiles'] ?? null) && !empty($relation['newValueFiles'])) {
1215  $temporaryFiles = $this->‪writeFilesToTemporaryFolder($relation['newValueFiles']);
1216  $updateData[$table][$actualUid][$field] = implode(',', $temporaryFiles);
1217  }
1218  break;
1219  }
1220  }
1221  }
1222  } else {
1223  $this->‪addError(sprintf('Error: This record does not appear to have a relation array! (%s:%s)', $table, $uid));
1224  }
1225  } else {
1226  $this->‪addError(sprintf('Error: This record does not appear to have been created! (%s:%s)', $table, $uid));
1227  }
1228  }
1229 
1230  if (!empty($updateData)) {
1231  $dataHandler = $this->‪createDataHandler();
1232  $dataHandler->isImporting = true;
1233  $this->‪callHook('before_setRelation', [
1234  'tce' => &$dataHandler,
1235  'data' => &$updateData,
1236  ]);
1237  $dataHandler->start($updateData, []);
1238  $dataHandler->process_datamap();
1239  $this->‪callHook('after_setRelations', [
1240  'tce' => &$dataHandler,
1241  ]);
1242  }
1243  }
1244 
1255  protected function ‪remapRelationsOfField(array &$fieldRelations, array $fieldConfig): array
1256  {
1257  $actualRelations = [];
1258 
1259  foreach ($fieldRelations as $relation) {
1260  if (isset($this->importMapId[$relation['table']][$relation['id']])) {
1261  $actualUid = $this->importMapId[$relation['table']][$relation['id']];
1262  if ($fieldConfig['type'] === 'input' && isset($fieldConfig['wizards']['link'])) {
1263  // If an input field has a relation to a sys_file record this need to be converted back to
1264  // the public path. But use getPublicUrl() here, because could normally only be a local file path.
1265  try {
1266  $file = GeneralUtility::makeInstance(ResourceFactory::class)->retrieveFileOrFolderObject($actualUid);
1267  $actualRelations[] = $file->getPublicUrl();
1268  } catch (\Exception $e) {
1269  $actualRelations[] = 'file:' . $actualUid;
1270  }
1271  } else {
1272  $actualRelations[] = $relation['table'] . '_' . $actualUid;
1273  }
1274  } elseif ($this->‪isTableStatic($relation['table']) || $this->‪isRecordExcluded($relation['table'], (int)$relation['id']) || $relation['id'] < 0) {
1275  // Some select types could contain negative values,
1276  // e.g. fe_groups (-1, -2) and sys_language (-1 = ALL languages).
1277  // This must be handled on both export and import.
1278  $actualRelations[] = $relation['table'] . '_' . $relation['id'];
1279  } else {
1280  $this->‪addError('Lost relation: ' . $relation['table'] . ':' . $relation['id']);
1281  }
1282  }
1283 
1284  return $actualRelations;
1285  }
1286 
1296  public function ‪writeFilesToTemporaryFolder(array $files): array
1297  {
1298  $temporaryFiles = [];
1299 
1300  foreach ($files as $fileInfo) {
1301  if (is_array($this->dat['files'][$fileInfo['ID']] ?? null)) {
1302  $fileRecord = &$this->dat['files'][$fileInfo['ID']];
1303 
1304  $temporaryFolder = $this->‪getOrCreateTemporaryFolderName();
1305  $temporaryFilePath = $temporaryFolder . '/' . $fileRecord['content_md5'];
1306 
1307  if (is_file($temporaryFilePath) && md5_file($temporaryFilePath) === $fileRecord['content_md5']) {
1308  $temporaryFiles[] = $temporaryFilePath;
1309  } else {
1310  if (‪GeneralUtility::writeFile($temporaryFilePath, $fileRecord['content'])) {
1311  clearstatcache();
1312  $temporaryFiles[] = $temporaryFilePath;
1313  } else {
1314  $this->‪addError(sprintf(
1315  'Error: Temporary file %s was not written as it should have been!',
1316  $temporaryFilePath
1317  ));
1318  }
1319  }
1320  } else {
1321  $this->‪addError(sprintf('Error: No file found for ID %s', $fileInfo['ID']));
1322  }
1323  }
1324 
1325  return $temporaryFiles;
1326  }
1335  protected function ‪setFlexFormRelations(): void
1336  {
1337  $updateData = [];
1338 
1339  foreach ($this->importNewId as $original) {
1340  $table = $original['table'];
1341  $uid = $original['uid'];
1342 
1343  if (isset($this->importMapId[$table][$uid])) {
1344  if (is_array($this->dat['records'][$table . ':' . $uid]['rels'] ?? null)) {
1345  $actualUid = BackendUtility::wsMapId($table, $this->importMapId[$table][$uid]);
1346  foreach ($this->dat['records'][$table . ':' . $uid]['rels'] as $field => $relation) {
1347  // Field "configuration" of sys_file_storage needs no update because it has not been removed
1348  // and has no relations.
1349  // @see Import::addSingle()
1350  if (isset($relation['type']) && !($table === 'sys_file_storage' && $field === 'configuration')) {
1351  switch ($relation['type']) {
1352  case 'flex':
1353  // Re-insert temporarily removed original FlexForm data as fallback
1354  // @see Import::addSingle()
1355  $updateData[$table][$actualUid][$field] = $this->dat['records'][$table . ':' . $uid]['data'][$field];
1356 
1357  if (!empty($relation['flexFormRels']['db']) || !empty($relation['flexFormRels']['file'])) {
1358  $actualRecord = BackendUtility::getRecord($table, $actualUid, '*');
1359  $fieldTca = &‪$GLOBALS['TCA'][$table]['columns'][$field];
1360  if (is_array($actualRecord) && is_array($fieldTca['config'] ?? null) && $fieldTca['config']['type'] === 'flex') {
1361  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1362  $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
1363  $fieldTca,
1364  $table,
1365  $field,
1366  $actualRecord
1367  );
1368  $dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
1369  $flexFormData = (new ‪Typo3XmlParser())->decodeWithReturningExceptionAsString(
1370  (string)($this->dat['records'][$table . ':' . $uid]['data'][$field] ?? ''),
1373  ])
1374  );
1375  if (is_array($flexFormData['data'] ?? null)) {
1376  $flexFormIterator = GeneralUtility::makeInstance(DataHandler::class);
1377  $flexFormIterator->callBackObj = $this;
1378  $flexFormData['data'] = $flexFormIterator->checkValue_flex_procInData(
1379  $flexFormData['data'],
1380  [],
1381  $dataStructure,
1382  [$relation],
1383  'remapRelationsOfFlexFormCallBack'
1384  );
1385  }
1386  if (is_array($flexFormData['data'] ?? null)) {
1387  $updateData[$table][$actualUid][$field] = $flexFormData;
1388  }
1389  }
1390  }
1391  break;
1392  }
1393  }
1394  }
1395  } else {
1396  $this->‪addError(sprintf('Error: This record does not appear to have a relation array! (%s:%s)', $table, $uid));
1397  }
1398  } else {
1399  $this->‪addError(sprintf('Error: This record does not appear to have been created! (%s:%s)', $table, $uid));
1400  }
1401  }
1402 
1403  if (!empty($updateData)) {
1404  $dataHandler = $this->‪createDataHandler();
1405  $dataHandler->isImporting = true;
1406  $this->‪callHook('before_setFlexFormRelations', [
1407  'tce' => &$dataHandler,
1408  'data' => &$updateData,
1409  ]);
1410  $dataHandler->start($updateData, []);
1411  $dataHandler->process_datamap();
1412  $this->‪callHook('after_setFlexFormRelations', [
1413  'tce' => &$dataHandler,
1414  ]);
1415  }
1416  }
1417 
1430  public function ‪remapRelationsOfFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path): array
1431  {
1432  [$relation] = $pParams;
1433  // In case the $path is used as index without a trailing slash we will remove that
1434  if (!is_array($relation['flexFormRels']['db'][$path] ?? null) && is_array($relation['flexFormRels']['db'][rtrim($path, '/')] ?? false)) {
1435  $path = rtrim($path, '/');
1436  }
1437  if (is_array($relation['flexFormRels']['db'][$path] ?? null)) {
1438  $actualRelations = $this->‪remapRelationsOfField($relation['flexFormRels']['db'][$path], $dsConf);
1439  $dataValue = implode(',', $actualRelations);
1440  }
1441  if (is_array($relation['flexFormRels']['file'][$path] ?? null)) {
1442  $temporaryFiles = $this->‪writeFilesToTemporaryFolder($relation['flexFormRels']['file'][$path]);
1443  $dataValue = implode(',', $temporaryFiles);
1444  }
1445  return ['value' => $dataValue];
1446  }
1448  /**************************
1449  * Import soft references
1450  *************************/
1451 
1455  protected function ‪processSoftReferences(): void
1456  {
1457  $updateData = [];
1458 
1459  if (is_array($this->dat['header']['records'] ?? null)) {
1460  foreach ($this->dat['header']['records'] as $table => $records) {
1461  if (isset(‪$GLOBALS['TCA'][$table])) {
1462  foreach ($records as $uid => $record) {
1463  if (is_array($record['softrefs'] ?? null)) {
1464  $actualUid = BackendUtility::wsMapId($table, $this->importMapId[$table][$uid] ?? 0);
1465  // First, group soft references by record field ...
1466  // (this could probably also have been done with $this->dat['records'] instead of $this->dat['header'])
1467  $softrefs = [];
1468  foreach ($record['softrefs'] as $softref) {
1469  if ($softref['field'] && is_array($softref['subst'] ?? null) && $softref['subst']['tokenID']) {
1470  $softrefs[$softref['field']][$softref['subst']['tokenID']] = $softref;
1471  }
1472  }
1473  // ... then process only fields which require substitution.
1474  foreach ($softrefs as $field => $softrefsByField) {
1475  if (is_array(‪$GLOBALS['TCA'][$table]['columns'][$field] ?? null)) {
1476  $fieldTca = &‪$GLOBALS['TCA'][$table]['columns'][$field];
1477  if ($fieldTca['config']['type'] === 'flex') {
1478  $actualRecord = BackendUtility::getRecord($table, $actualUid, '*');
1479  if (is_array($actualRecord)) {
1480  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1481  $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
1482  $fieldTca,
1483  $table,
1484  $field,
1485  $actualRecord
1486  );
1487  $dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
1488  $flexFormData = (new ‪Typo3XmlParser())->decodeWithReturningExceptionAsString(
1489  (string)($actualRecord[$field] ?? ''),
1492  ])
1493  );
1494  if (is_array($flexFormData['data'] ?? null)) {
1495  $flexFormIterator = GeneralUtility::makeInstance(DataHandler::class);
1496  $flexFormIterator->callBackObj = $this;
1497  $flexFormData['data'] = $flexFormIterator->checkValue_flex_procInData(
1498  $flexFormData['data'],
1499  [],
1500  $dataStructure,
1501  [$table, $uid, $field, $softrefsByField],
1502  'processSoftReferencesFlexFormCallBack'
1503  );
1504  }
1505  if (is_array($flexFormData['data'] ?? null)) {
1506  $updateData[$table][$actualUid][$field] = $flexFormData;
1507  }
1508  }
1509  } else {
1510  // Get tokenizedContent string and proceed only if that is not blank:
1511  $tokenizedContent = $this->dat['records'][$table . ':' . $uid]['rels'][$field]['softrefs']['tokenizedContent'];
1512  if (strlen($tokenizedContent) && is_array($softrefsByField)) {
1513  $updateData[$table][$actualUid][$field] = $this->‪processSoftReferencesSubstTokens($tokenizedContent, $softrefsByField, $table, (string)$uid);
1514  }
1515  }
1516  }
1517  }
1518  }
1519  }
1520  }
1521  }
1522  }
1523 
1524  // Update soft references in the database
1525  $dataHandler = $this->‪createDataHandler();
1526  $dataHandler->isImporting = true;
1527  $this->‪callHook('before_processSoftReferences', [
1528  'tce' => $dataHandler,
1529  'data' => &$updateData,
1530  ]);
1531  $dataHandler->enableLogging = true;
1532  $dataHandler->start($updateData, []);
1533  $dataHandler->process_datamap();
1534  $this->‪callHook('after_processSoftReferences', [
1535  'tce' => $dataHandler,
1536  ]);
1537  }
1538 
1550  public function ‪processSoftReferencesFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path): array
1551  {
1552  [$table, $origUid, $field, $softrefs] = $pParams;
1553  if (is_array($softrefs)) {
1554  // Filter for soft references of this path ...
1555  $softrefsByPath = [];
1556  foreach ($softrefs as $tokenID => $softref) {
1557  if ($softref['structurePath'] === $path) {
1558  $softrefsByPath[$tokenID] = $softref;
1559  }
1560  }
1561  // ... and perform the processing.
1562  if (!empty($softrefsByPath)) {
1563  $tokenizedContent = $this->dat['records'][$table . ':' . $origUid]['rels'][$field]['flexFormRels']['softrefs'][$path]['tokenizedContent'];
1564  if (strlen($tokenizedContent)) {
1565  $dataValue = $this->‪processSoftReferencesSubstTokens($tokenizedContent, $softrefsByPath, $table, (string)$origUid);
1566  }
1567  }
1568  }
1569  return ['value' => $dataValue];
1570  }
1571 
1581  protected function ‪processSoftReferencesSubstTokens(string $tokenizedContent, array $softrefs, string $table, string $uid): string
1582  {
1583  foreach ($softrefs as &$softref) {
1584  $tokenID = $softref['subst']['tokenID'];
1585  $insertValue = $softref['subst']['tokenValue'];
1586  switch ((string)($this->softrefCfg[$tokenID]['mode'] ?? '')) {
1588  // This is the same as handling static relations:
1589  // Do not create or update the related file or record and do not change the link in any way,
1590  // but use the link as it was when exported.
1591  break;
1593  // This is the same as "exclude" with the option to manually edit the link before importing.
1594  $insertValue = $this->softrefInputValues[$tokenID];
1595  break;
1596  default:
1597  // This is almost the same as handling relations:
1598  // - Creating or updating related files and adjusting the file reference to link to the new file.
1599  // - Adjusting the record reference to link to the already imported record - if any.
1600  switch ((string)$softref['subst']['type']) {
1601  case 'file':
1602  $insertValue = $this->‪processSoftReferencesSaveFile($softref['subst']['relFileName'], $softref, $table, $uid);
1603  break;
1604  case 'db':
1605  default:
1606  [$tempTable, $tempUid] = explode(':', (string)($softref['subst']['recordRef'] ?? ':'));
1607  if (isset($this->importMapId[$tempTable][$tempUid])) {
1608  $insertValue = BackendUtility::wsMapId($tempTable, $this->importMapId[$tempTable][$tempUid]);
1609  $tokenValue = (string)$softref['subst']['tokenValue'];
1610  if (str_contains($tokenValue, ':')) {
1611  [$tokenKey] = explode(':', $tokenValue);
1612  $insertValue = $tokenKey . ':' . $insertValue;
1613  }
1614  }
1615  }
1616  }
1617  // Finally, replace the soft reference token in tokenized content
1618  $tokenizedContent = str_replace('{softref:' . $tokenID . '}', (string)$insertValue, $tokenizedContent);
1619  }
1620  return $tokenizedContent;
1621  }
1622 
1632  protected function ‪processSoftReferencesSaveFile(string $relFileName, array $softref, string $table, string $uid): string
1633  {
1634  if ($this->dat['header']['files'][$softref['file_ID']]) {
1635  // Initialize; Get directory prefix for file and find possible RTE filename
1636  $dirPrefix = ‪PathUtility::dirname($relFileName) . '/';
1637  if (str_starts_with($dirPrefix, $this->‪getFileadminFolderName() . '/')) {
1638  // File in fileadmin/ folder:
1639  // Create file (and possible resources)
1640  $newFileName = $this->‪processSoftReferencesSaveFileCreateRelFile($dirPrefix, ‪PathUtility::basename($relFileName), $softref['file_ID'], $table, $uid) ?: '';
1641  if (strlen($newFileName)) {
1642  $relFileName = $newFileName;
1643  } else {
1644  $this->‪addError('ERROR: No new file created for "' . $relFileName . '"');
1645  }
1646  } else {
1647  $this->‪addError('ERROR: Sorry, cannot operate on non-RTE files which are outside the fileadmin folder.');
1648  }
1649  } else {
1650  $this->‪addError('ERROR: Could not find file ID in header.');
1651  }
1652  // Return (new) filename relative to public web path
1653  return $relFileName;
1654  }
1655 
1666  protected function ‪processSoftReferencesSaveFileCreateRelFile(string $origDirPrefix, string $fileName, string $fileID, string $table, string $uid): ?string
1667  {
1668  // If the fileID map contains an entry for this fileID then just return the relative filename of that entry;
1669  // we don't want to write another unique filename for this one!
1670  if (isset($this->fileIdMap[$fileID])) {
1671  return ‪PathUtility::stripPathSitePrefix($this->fileIdMap[$fileID]);
1672  }
1673  // Verify FileMount access to dir-prefix. Returns the best alternative relative path if any
1674  $dirPrefix = $this->‪resolveStoragePath($origDirPrefix);
1675  if ($dirPrefix !== null && (!$this->update || $origDirPrefix === $dirPrefix) && $this->‪checkOrCreateDir($dirPrefix)) {
1676  $fileHeaderInfo = $this->dat['header']['files'][$fileID];
1677  $updMode = $this->update && $this->importMapId[$table][$uid] === $uid && ($this->importMode[$table . ':' . $uid] ?? '') !== self::IMPORT_MODE_AS_NEW;
1678  // Create new name for file:
1679  // Must have same ID in map array (just for security, is not really needed) and NOT be set "as_new".
1680 
1681  // Write main file:
1682  if ($updMode) {
1683  $newName = ‪Environment::getPublicPath() . '/' . $dirPrefix . $fileName;
1684  } else {
1685  // Create unique filename:
1687  $newName = (string)‪$fileProcObj->‪getUniqueName($fileName, ‪Environment::getPublicPath() . '/' . $dirPrefix);
1688  }
1689  if ($this->‪writeFileVerify($newName, $fileID)) {
1690  // If the resource was an HTML/CSS file with resources attached, we will write those as well!
1691  if (is_array($fileHeaderInfo['EXT_RES_ID'] ?? null)) {
1692  $tokenizedContent = $this->dat['files'][$fileID]['tokenizedContent'];
1693  $tokenSubstituted = false;
1695  if ($updMode) {
1696  foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
1697  if ($this->dat['files'][$res_fileID]['filename']) {
1698  // Resolve original filename:
1699  $relResourceFileName = $this->dat['files'][$res_fileID]['parentRelFileName'];
1700  $absResourceFileName = GeneralUtility::resolveBackPath(‪Environment::getPublicPath() . '/' . $origDirPrefix . $relResourceFileName);
1701  $absResourceFileName = GeneralUtility::getFileAbsFileName($absResourceFileName);
1702  if ($absResourceFileName && str_starts_with($absResourceFileName, ‪Environment::getPublicPath() . '/' . $this->‪getFileadminFolderName() . '/')) {
1703  $destDir = ‪PathUtility::stripPathSitePrefix(‪PathUtility::dirname($absResourceFileName) . '/');
1704  if ($this->‪resolveStoragePath($destDir, false) !== null && $this->‪checkOrCreateDir($destDir)) {
1705  $this->‪writeFileVerify($absResourceFileName, $res_fileID);
1706  } else {
1707  $this->‪addError('ERROR: Could not create file in directory "' . $destDir . '"');
1708  }
1709  } else {
1710  $this->‪addError('ERROR: Could not resolve path for "' . $relResourceFileName . '"');
1711  }
1712  $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent);
1713  $tokenSubstituted = true;
1714  }
1715  }
1716  } else {
1717  // Create the ressource's directory name (filename without extension, suffixed "_FILES")
1718  $resourceDir = ‪PathUtility::dirname($newName) . '/' . preg_replace('/\\.[^.]*$/', '', ‪PathUtility::basename($newName)) . '_FILES';
1719  if (‪GeneralUtility::mkdir($resourceDir)) {
1720  foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
1721  if ($this->dat['files'][$res_fileID]['filename']) {
1722  $absResourceFileName = (string)‪$fileProcObj->‪getUniqueName($this->dat['files'][$res_fileID]['filename'], $resourceDir);
1723  $relResourceFileName = substr($absResourceFileName, strlen(‪PathUtility::dirname($resourceDir)) + 1);
1724  $this->‪writeFileVerify($absResourceFileName, $res_fileID);
1725  $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent);
1726  $tokenSubstituted = true;
1727  }
1728  }
1729  }
1730  }
1731  // If substitutions has been made, write the content to the file again:
1732  if ($tokenSubstituted) {
1733  ‪GeneralUtility::writeFile($newName, $tokenizedContent);
1734  }
1735  }
1736  return ‪PathUtility::stripPathSitePrefix($newName);
1737  }
1738  }
1739  return null;
1740  }
1741 
1750  protected function ‪writeFileVerify(string $fileName, string $fileID, bool $bypassMountCheck = false): bool
1751  {
1753  if (!‪$fileProcObj->actionPerms['addFile']) {
1754  $this->‪addError('ERROR: You did not have sufficient permissions to write the file "' . $fileName . '"');
1755  return false;
1756  }
1757  // Just for security, check again. Should actually not be necessary.
1758  if (!$bypassMountCheck) {
1759  try {
1760  GeneralUtility::makeInstance(ResourceFactory::class)->getFolderObjectFromCombinedIdentifier(‪PathUtility::dirname($fileName));
1761  } catch (InsufficientFolderAccessPermissionsException $e) {
1762  $this->‪addError('ERROR: Filename "' . $fileName . '" was not allowed in destination path!');
1763  return false;
1764  }
1765  }
1766  $pathInfo = GeneralUtility::split_fileref($fileName);
1767  if (!GeneralUtility::makeInstance(FileNameValidator::class)->isValid($pathInfo['file'])) {
1768  $this->‪addError('ERROR: Filename "' . $fileName . '" failed against extension check or deny-pattern!');
1769  return false;
1770  }
1771  if (!GeneralUtility::getFileAbsFileName($fileName)) {
1772  $this->‪addError('ERROR: Filename "' . $fileName . '" was not a valid relative file path!');
1773  return false;
1774  }
1775  if (!$this->dat['files'][$fileID]) {
1776  $this->‪addError('ERROR: File ID "' . $fileID . '" could not be found');
1777  return false;
1778  }
1779  ‪GeneralUtility::writeFile($fileName, $this->dat['files'][$fileID]['content']);
1780  $this->fileIdMap[$fileID] = $fileName;
1781  if (hash_equals(md5((string)file_get_contents($fileName)), $this->dat['files'][$fileID]['content_md5'])) {
1782  return true;
1783  }
1784  $this->‪addError('ERROR: File content "' . $fileName . '" was corrupted');
1785  return false;
1786  }
1787 
1794  protected function ‪checkOrCreateDir(string $dirPrefix): bool
1795  {
1796  // Split dir path and remove first directory (which should be "fileadmin")
1797  $filePathParts = explode('/', $dirPrefix);
1798  $firstDir = array_shift($filePathParts);
1799  if ($firstDir === $this->‪getFileadminFolderName() && GeneralUtility::getFileAbsFileName($dirPrefix)) {
1800  $pathAcc = '';
1801  foreach ($filePathParts as $dirname) {
1802  $pathAcc .= '/' . $dirname;
1803  if (strlen($dirname)) {
1804  if (!@is_dir(‪Environment::getPublicPath() . '/' . $this->‪getFileadminFolderName() . $pathAcc)) {
1806  $this->‪addError('ERROR: Directory could not be created....B');
1807  return false;
1808  }
1809  }
1810  } elseif ($dirPrefix === $this->‪getFileadminFolderName() . $pathAcc) {
1811  return true;
1812  } else {
1813  $this->‪addError('ERROR: Directory could not be created....A');
1814  }
1815  }
1816  }
1817  return false;
1818  }
1819 
1826  protected function ‪callHook(string $name, array $params): void
1827  {
1828  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name] ?? [] as $hook) {
1829  GeneralUtility::callUserFunction($hook, $params, $this);
1830  }
1831  }
1833  /**************************
1834  * Getters and Setters
1835  *************************/
1836 
1840  public function ‪isEnableLogging(): bool
1841  {
1842  return ‪$this->enableLogging;
1843  }
1844 
1848  public function ‪setEnableLogging(bool ‪$enableLogging): void
1849  {
1850  $this->enableLogging = ‪$enableLogging;
1851  }
1852 
1856  public function ‪isDecompressionAvailable(): bool
1857  {
1859  }
1860 }
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:86
‪TYPO3\CMS\Impexp\ImportExport\getFileadminFolderName
‪string getFileadminFolderName()
Definition: ImportExport.php:1122
‪TYPO3\CMS\Impexp\ImportExport\getOrCreateTemporaryFolderName
‪string getOrCreateTemporaryFolderName()
Definition: ImportExport.php:1137
‪TYPO3\CMS\Impexp\Import\$enableLogging
‪bool $enableLogging
Definition: Import.php:75
‪TYPO3\CMS\Core\Resource\StorageRepository\createStorageObject
‪ResourceStorage createStorageObject(array $storageRecord, array $storageConfiguration=null)
Definition: StorageRepository.php:470
‪TYPO3\CMS\Impexp\Import\addSingle
‪addSingle(array &$importData, string $table, int $uid, $pid)
Definition: Import.php:952
‪TYPO3\CMS\Impexp\Import\processSoftReferencesSaveFile
‪string processSoftReferencesSaveFile(string $relFileName, array $softref, string $table, string $uid)
Definition: Import.php:1624
‪TYPO3\CMS\Impexp\Import\setEnableLogging
‪setEnableLogging(bool $enableLogging)
Definition: Import.php:1840
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:25
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:49
‪TYPO3\CMS\Impexp\Import\getSysFileMetaDataFromDatabase
‪array null getSysFileMetaDataFromDatabase(int $pid, int $file, int $sysLanguageUid)
Definition: Import.php:1094
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Impexp\ImportExport\addGeneralErrorsByTable
‪addGeneralErrorsByTable(string $table)
Definition: ImportExport.php:456
‪TYPO3\CMS\Impexp\ImportExport\addError
‪addError(string $message)
Definition: ImportExport.php:1453
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:206
‪TYPO3\CMS\Impexp\Import\IMPORT_MODE_AS_NEW
‪const IMPORT_MODE_AS_NEW
Definition: Import.php:52
‪TYPO3\CMS\Impexp\Import\writeFilesToTemporaryFolder
‪array writeFilesToTemporaryFolder(array $files)
Definition: Import.php:1288
‪TYPO3\CMS\Impexp\Import\writePages
‪writePages()
Definition: Import.php:717
‪TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
Definition: InsufficientFolderAccessPermissionsException.php:23
‪TYPO3\CMS\Impexp\Import\processSoftReferencesSubstTokens
‪string processSoftReferencesSubstTokens(string $tokenizedContent, array $softrefs, string $table, string $uid)
Definition: Import.php:1573
‪TYPO3\CMS\Core\Resource\ResourceStorage\getDriverType
‪string getDriverType()
Definition: ResourceStorage.php:2904
‪TYPO3\CMS\Core\Utility\PathUtility\dirname
‪static string dirname($path)
Definition: PathUtility.php:251
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Impexp\Import\doRespectPid
‪bool doRespectPid(string $table, int $uid)
Definition: Import.php:819
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator
Definition: FileNameValidator.php:25
‪TYPO3\CMS\Impexp\Import\$mode
‪string $mode
Definition: Import.php:62
‪TYPO3\CMS\Core\Serializer\Typo3XmlSerializerOptions
Definition: Typo3XmlSerializerOptions.php:24
‪TYPO3\CMS\Impexp\ImportExport\getStorageRepository
‪StorageRepository getStorageRepository()
Definition: ImportExport.php:1436
‪TYPO3\CMS\Impexp\Import\isDecompressionAvailable
‪bool isDecompressionAvailable()
Definition: Import.php:1848
‪TYPO3\CMS\Impexp\Exception\PrerequisitesNotMetException
Definition: PrerequisitesNotMetException.php:27
‪TYPO3\CMS\Impexp\Import\setRelations
‪setRelations()
Definition: Import.php:1181
‪TYPO3\CMS\Core\Utility\PathUtility\stripPathSitePrefix
‪static string stripPathSitePrefix($path)
Definition: PathUtility.php:445
‪TYPO3\CMS\Impexp\Import\loadInit
‪loadInit()
Definition: Import.php:285
‪TYPO3\CMS\Impexp\ImportExport\fetchStorages
‪fetchStorages()
Definition: ImportExport.php:251
‪TYPO3\CMS\Impexp\ImportExport\$pid
‪int $pid
Definition: ImportExport.php:62
‪TYPO3\CMS\Impexp\Import\remapRelationsOfFlexFormCallBack
‪array remapRelationsOfFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path)
Definition: Import.php:1422
‪TYPO3\CMS\Impexp\ImportExport\isTableStatic
‪bool isTableStatic(string $table)
Definition: ImportExport.php:1264
‪TYPO3\CMS\Core\Exception
‪TYPO3\CMS\Impexp\Import\getNextFilePart
‪array string null getNextFilePart($fd, bool $unserialize=false, string $name='')
Definition: Import.php:242
‪TYPO3\CMS\Core\Serializer\Typo3XmlSerializerOptions\RETURN_ROOT_NODE_NAME
‪const RETURN_ROOT_NODE_NAME
Definition: Typo3XmlSerializerOptions.php:30
‪TYPO3\CMS\Impexp\Exception\LoadingFileFailedException
Definition: LoadingFileFailedException.php:27
‪TYPO3\CMS\Impexp\Import\writeRecords
‪writeRecords()
Definition: Import.php:830
‪TYPO3\CMS\Impexp\Import\$importNewId
‪array $importNewId
Definition: Import.php:83
‪TYPO3\CMS\Impexp\Import\__construct
‪__construct()
Definition: Import.php:106
‪TYPO3\CMS\Impexp\Import\processSoftReferencesSaveFileCreateRelFile
‪string null processSoftReferencesSaveFileCreateRelFile(string $origDirPrefix, string $fileName, string $fileID, string $table, string $uid)
Definition: Import.php:1658
‪TYPO3\CMS\Core\Serializer\Typo3XmlParser
Definition: Typo3XmlParser.php:38
‪TYPO3\CMS\Impexp\Import\IMPORT_MODE_FORCE_UID
‪const IMPORT_MODE_FORCE_UID
Definition: Import.php:51
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static string basename($path)
Definition: PathUtility.php:226
‪TYPO3\CMS\Impexp\Import\writePagesOrder
‪writePagesOrder()
Definition: Import.php:771
‪TYPO3\CMS\Impexp\Import\addToMapId
‪addToMapId(array $importData, array $substNEWwithIDs)
Definition: Import.php:1128
‪TYPO3\CMS\Impexp\Import\isEnableLogging
‪bool isEnableLogging()
Definition: Import.php:1832
‪TYPO3\CMS\Core\Service\FlexFormService
Definition: FlexFormService.php:25
‪TYPO3\CMS\Core\Serializer\Typo3XmlSerializerOptions\ALLOW_UNDEFINED_NAMESPACES
‪const ALLOW_UNDEFINED_NAMESPACES
Definition: Typo3XmlSerializerOptions.php:29
‪TYPO3\CMS\Impexp\Import\IMPORT_MODE_EXCLUDE
‪const IMPORT_MODE_EXCLUDE
Definition: Import.php:53
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:43
‪TYPO3\CMS\Impexp\Import\loadFile
‪loadFile(string $fileName, bool $all=false)
Definition: Import.php:123
‪TYPO3\CMS\Impexp\ImportExport\getRecordFromDatabase
‪array null getRecordFromDatabase(string $table, int $uid, string $fields='uid, pid')
Definition: ImportExport.php:1306
‪TYPO3\CMS\Impexp\Import\writeSysFileStorageRecords
‪writeSysFileStorageRecords()
Definition: Import.php:425
‪TYPO3\CMS\Impexp\Import\$suggestedInsertUids
‪array $suggestedInsertUids
Definition: Import.php:69
‪TYPO3\CMS\Core\Resource\ResourceStorage\isOnline
‪bool isOnline()
Definition: ResourceStorage.php:468
‪TYPO3\CMS\Impexp\Import\removeSysFileReferenceRecordsWithRelationToMissingFile
‪removeSysFileReferenceRecordsWithRelationToMissingFile()
Definition: Import.php:689
‪TYPO3\CMS\Impexp\Import\processSoftReferencesFlexFormCallBack
‪array processSoftReferencesFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path)
Definition: Import.php:1542
‪TYPO3\CMS\Impexp\Import
Definition: Import.php:50
‪TYPO3\CMS\Impexp\ImportExport\$fileProcObj
‪ExtendedFileUtility $fileProcObj
Definition: ImportExport.php:177
‪TYPO3\CMS\Impexp\Import\writeSysFileRecords
‪writeSysFileRecords()
Definition: Import.php:514
‪TYPO3\CMS\Impexp\Import\getReferenceDefaultValue
‪int float string getReferenceDefaultValue(array $configuration)
Definition: Import.php:1075
‪TYPO3\CMS\Impexp\Import\writeRecordsOrder
‪writeRecordsOrder()
Definition: Import.php:891
‪TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools
Definition: FlexFormTools.php:40
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:41
‪TYPO3\CMS\Impexp\Import\callHook
‪callHook(string $name, array $params)
Definition: Import.php:1818
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:24
‪TYPO3\CMS\Impexp\Import\createDataHandler
‪DataHandler createDataHandler()
Definition: Import.php:1161
‪TYPO3\CMS\Impexp\Import\getSupportedFileExtensions
‪array getSupportedFileExtensions()
Definition: Import.php:219
‪TYPO3\CMS\Impexp\Import\initializeImport
‪initializeImport()
Definition: Import.php:414
‪TYPO3\CMS\Core\Resource\ResourceStorage\getConfiguration
‪array getConfiguration()
Definition: ResourceStorage.php:268
‪TYPO3\CMS\Impexp\Import\IMPORT_MODE_RESPECT_PID
‪const IMPORT_MODE_RESPECT_PID
Definition: Import.php:55
‪TYPO3\CMS\Impexp\Import\IMPORT_MODE_IGNORE_PID
‪const IMPORT_MODE_IGNORE_PID
Definition: Import.php:54
‪TYPO3\CMS\Impexp\ImportExport\$defaultStorage
‪ResourceStorage $defaultStorage
Definition: ImportExport.php:226
‪TYPO3\CMS\Impexp\ImportExport
Definition: ImportExport.php:45
‪TYPO3\CMS\Impexp\Import\SOFTREF_IMPORT_MODE_EDITABLE
‪const SOFTREF_IMPORT_MODE_EDITABLE
Definition: Import.php:58
‪TYPO3\CMS\Core\Utility\File\BasicFileUtility\getUniqueName
‪string null getUniqueName($theFile, $theDest, $dontCheckForUnique=false)
Definition: BasicFileUtility.php:84
‪TYPO3\CMS\Impexp\Import\SOFTREF_IMPORT_MODE_EXCLUDE
‪const SOFTREF_IMPORT_MODE_EXCLUDE
Definition: Import.php:57
‪TYPO3\CMS\Impexp\Import\fixUidLocalInSysFileReferenceRecords
‪fixUidLocalInSysFileReferenceRecords(int $oldFileUid, int $newFileUid)
Definition: Import.php:669
‪TYPO3\CMS\Impexp\Exception\ImportFailedException
Definition: ImportFailedException.php:27
‪TYPO3\CMS\Impexp\Import\checkOrCreateDir
‪bool checkOrCreateDir(string $dirPrefix)
Definition: Import.php:1786
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:38
‪TYPO3\CMS\Impexp\ImportExport\flatInversePageTree
‪flatInversePageTree(array $pageTree, array &$list, int $pid=-1)
Definition: ImportExport.php:1246
‪TYPO3\CMS\Core\Resource\ResourceStorage
Definition: ResourceStorage.php:125
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:128
‪TYPO3\CMS\Impexp\ImportExport\hasErrors
‪hasErrors()
Definition: ImportExport.php:1458
‪TYPO3\CMS\Impexp\Import\writeFileVerify
‪bool writeFileVerify(string $fileName, string $fileID, bool $bypassMountCheck=false)
Definition: Import.php:1742
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Impexp\ImportExport\getBackendUser
‪BackendUserAuthentication getBackendUser()
Definition: ImportExport.php:1466
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:43
‪TYPO3\CMS\Impexp\ImportExport\resolveStoragePath
‪string resolveStoragePath(string $dirPrefix, bool $checkAlternatives=true)
Definition: ImportExport.php:1219
‪TYPO3\CMS\Impexp\Import\$isFilesSavedOutsideImportFile
‪bool $isFilesSavedOutsideImportFile
Definition: Import.php:101
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Impexp\Import\$importNewIdPids
‪array $importNewIdPids
Definition: Import.php:89
‪TYPO3\CMS\Impexp\ImportExport\getFileProcObj
‪ExtendedFileUtility getFileProcObj()
Definition: ImportExport.php:1422
‪TYPO3\CMS\Impexp\Import\setFlexFormRelations
‪setFlexFormRelations()
Definition: Import.php:1327
‪TYPO3\CMS\Impexp\Import\remapRelationsOfField
‪array remapRelationsOfField(array &$fieldRelations, array $fieldConfig)
Definition: Import.php:1247
‪TYPO3\CMS\Core\Resource\ResourceStorage\isWritable
‪bool isWritable()
Definition: ResourceStorage.php:403
‪TYPO3\CMS\Impexp\ImportExport\$dat
‪array $dat
Definition: ImportExport.php:171
‪TYPO3\CMS\Impexp\Import\isEquivalentStorage
‪bool isEquivalentStorage(ResourceStorage &$storageObject, array &$storageRecord)
Definition: Import.php:493
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Core\Serializer\Typo3XmlSerializerOptions\LOAD_OPTIONS
‪const LOAD_OPTIONS
Definition: Typo3XmlSerializerOptions.php:27
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir
‪static bool mkdir($newFolder)
Definition: GeneralUtility.php:1891
‪TYPO3\CMS\Impexp\ImportExport\removeTemporaryFolderName
‪removeTemporaryFolderName()
Definition: ImportExport.php:1160
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFile
‪static bool writeFile($file, $content, $changePermissions=false)
Definition: GeneralUtility.php:1722
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static bool isLoaded($key)
Definition: ExtensionManagementUtility.php:114
‪TYPO3\CMS\Core\Resource\StorageRepository\findByUid
‪findByUid(int $uid)
Definition: StorageRepository.php:92
‪TYPO3\CMS\Impexp\ImportExport\isRecordExcluded
‪bool isRecordExcluded(string $table, int $uid)
Definition: ImportExport.php:1281
‪TYPO3\CMS\Impexp
‪TYPO3\CMS\Impexp\Import\processSoftReferences
‪processSoftReferences()
Definition: Import.php:1447
‪TYPO3\CMS\Impexp\Import\getMetaData
‪getMetaData()
Definition: Import.php:292
‪TYPO3\CMS\Impexp\Import\importData
‪importData()
Definition: Import.php:381
‪TYPO3\CMS\Impexp\Import\$decompressionAvailable
‪bool $decompressionAvailable
Definition: Import.php:93
‪TYPO3\CMS\Impexp\Import\checkImportPrerequisites
‪checkImportPrerequisites()
Definition: Import.php:306
‪TYPO3\CMS\Impexp\Import\$supportedFileExtensions
‪array $supportedFileExtensions
Definition: Import.php:97