‪TYPO3CMS  ‪main
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;
44 
51 {
52  public const ‪IMPORT_MODE_FORCE_UID = 'force_uid';
53  public const ‪IMPORT_MODE_AS_NEW = 'as_new';
54  public const ‪IMPORT_MODE_EXCLUDE = 'exclude';
55  public const ‪IMPORT_MODE_IGNORE_PID = 'ignore_pid';
56  public const ‪IMPORT_MODE_RESPECT_PID = 'respect_pid';
57 
58  public const ‪SOFTREF_IMPORT_MODE_EXCLUDE = 'exclude';
59  public const ‪SOFTREF_IMPORT_MODE_EDITABLE = 'editable';
60 
61  protected string ‪$mode = 'import';
62 
67  protected array ‪$suggestedInsertUids = [];
68 
72  protected bool ‪$enableLogging = false;
73 
79  protected array ‪$importNewId = [];
80 
84  protected array ‪$importNewIdPids = [];
85 
86  protected bool ‪$decompressionAvailable = false;
87  private array ‪$supportedFileExtensions = [
88  'xml',
89  't3d',
90  ];
91  protected bool ‪$isFilesSavedOutsideImportFile = false;
92 
98  protected array ‪$storages = [];
99 
105  protected array ‪$storagesAvailableForImport = [];
106 
112 
116  protected string ‪$fileadminFolderName = '';
117 
118  public function ‪__construct()
119  {
120  $this->storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
121  parent::__construct();
122  $this->‪fetchStorages();
123  $this->decompressionAvailable = function_exists('gzuncompress');
124  }
125 
132  protected function ‪fetchStorages(): void
133  {
134  $this->storages = [];
135  $this->storagesAvailableForImport = [];
136  $this->defaultStorage = null;
137 
138  $this->storageRepository->flush();
139 
140  ‪$storages = $this->storageRepository->findAll();
141  foreach (‪$storages as $storage) {
142  $this->storages[$storage->getUid()] = $storage;
143  if ($storage->isOnline() && $storage->isWritable() && $storage->getDriverType() === 'Local') {
144  $this->storagesAvailableForImport[$storage->getUid()] = $storage;
145  }
146  if ($this->defaultStorage === null && $storage->isDefault()) {
147  $this->defaultStorage = $storage;
148  }
149  }
150  }
151 
152  /**************************
153  * File Input
154  *************************/
155 
162  public function ‪loadFile(string $fileName): void
163  {
164  $filePath = GeneralUtility::getFileAbsFileName($fileName);
165 
166  if (empty($filePath)) {
167  $this->‪addError('File path is not valid: ' . $fileName);
168  } elseif (!@is_file($filePath)) {
169  $this->‪addError('File not found: ' . $filePath);
170  }
171 
172  if ($this->‪hasErrors()) {
174  sprintf('Loading of the import file "%s" failed.', $fileName),
175  1484484619
176  );
177  }
178 
179  $pathInfo = pathinfo($filePath);
180  $fileExtension = strtolower($pathInfo['extension']);
181 
182  if (!in_array($fileExtension, $this->supportedFileExtensions, true)) {
183  $this->‪addError(
184  sprintf(
185  'File extension "%s" is not valid. Supported file extensions are %s.',
186  $fileExtension,
187  implode(', ', array_map(static function ($supportedFileExtension) {
188  return '"' . $supportedFileExtension . '"';
190  )
191  );
192  }
193 
194  if ($this->‪hasErrors() === false) {
195  if (@is_dir($filePath . '.files')) {
196  if (GeneralUtility::isAllowedAbsPath($filePath . '.files')) {
197  // copy the folder lowlevel to typo3temp, because the files would be deleted after import
198  GeneralUtility::copyDirectory($filePath . '.files', $this->‪getOrCreateTemporaryFolderName());
199  } else {
200  $this->‪addError('External import files for the given import source is currently not supported.');
201  }
202  $this->isFilesSavedOutsideImportFile = true;
203  } else {
204  $this->isFilesSavedOutsideImportFile = false;
205  }
206  if ($fileExtension === 'xml') {
207  $xmlContent = (string)file_get_contents($filePath);
208  if (strlen($xmlContent)) {
209  try {
210  ‪$dat = (new ‪Typo3XmlParser())->decode(
211  $xmlContent,
214  ‪Typo3XmlSerializerOptions::LOAD_OPTIONS => \LIBXML_NONET | \LIBXML_NOBLANKS | \LIBXML_PARSEHUGE,
215  // @todo check if needed for imports/throw deprecation for invalid xml
217  ])
218  );
219  $this->dat = is_array(‪$dat) ? ‪$dat : [‪$dat];
220  if ($this->dat['_DOCUMENT_TAG'] === 'T3RecordDocument' && is_array($this->dat['header'] ?? null) && is_array($this->dat['records'] ?? null)) {
221  $this->‪loadInit();
222  } else {
223  $this->‪addError('XML file did not contain proper XML for TYPO3 Import');
224  }
225  } catch (\Throwable $e) {
226  $this->‪addError('XML could not be parsed: ' . $e->getMessage());
227  }
228  } else {
229  $this->‪addError('Error opening file: ' . $filePath);
230  }
231  } elseif ($fileExtension === 't3d') {
232  if ($fd = fopen($filePath, 'rb')) {
233  $this->dat['header'] = $this->‪getNextFilePart($fd, 'header');
234  $this->dat['records'] = $this->‪getNextFilePart($fd, 'records');
235  $this->dat['files'] = $this->‪getNextFilePart($fd, 'files');
236  $this->dat['files_fal'] = $this->‪getNextFilePart($fd, 'files_fal');
237  $this->‪loadInit();
238  fclose($fd);
239  } else {
240  $this->‪addError('Error opening file: ' . $filePath);
241  }
242  }
243  }
244 
245  if ($this->‪hasErrors()) {
247  sprintf('Loading of the import file "%s" failed.', $fileName),
248  1484484620
249  );
250  }
251  }
252 
262  protected function ‪getNextFilePart($fd, string $name): ?array
263  {
264  $headerLength = 32 + 1 + 1 + 1 + 10 + 1;
265  $headerString = fread($fd, $headerLength);
266  if (empty($headerString)) {
267  $this->‪addError('File does not contain data for "' . $name . '"');
268  return null;
269  }
270 
271  $header = explode(':', $headerString);
272  if (str_contains($header[0], 'Warning')) {
273  $this->‪addError('File read error: Warning message in file. (' . $headerString . fgets($fd) . ')');
274  return null;
275  }
276  if ((string)$header[3] !== '') {
277  $this->‪addError('File read error: InitString had a wrong length. (' . $name . ')');
278  return null;
279  }
280 
281  $dataString = (string)fread($fd, (int)$header[2]);
282  $isDataCompressed = $header[1] === '1';
283  fread($fd, 1);
284  if (!hash_equals($header[0], md5($dataString))) {
285  $this->‪addError('MD5 check failed (' . $name . ')');
286  return null;
287  }
288 
289  if ($isDataCompressed) {
290  if ($this->decompressionAvailable) {
291  $dataString = (string)gzuncompress($dataString);
292  } else {
293  $this->‪addError('Content read error: This file requires decompression, ' .
294  'but this server does not offer gzcompress()/gzuncompress() functions.');
295  return null;
296  }
297  }
298 
299  return unserialize($dataString, ['allowed_classes' => false]) ?: null;
300  }
301 
305  protected function ‪loadInit(): void
306  {
307  $this->relStaticTables = (array)($this->dat['header']['relStaticTables'] ?? []);
308  $this->excludeMap = (array)($this->dat['header']['excludeMap'] ?? []);
309  $this->softrefCfg = (array)($this->dat['header']['softrefCfg'] ?? []);
310  }
311 
312  public function ‪getMetaData(): array
313  {
314  return $this->dat['header']['meta'] ?? [];
315  }
316 
317  /***********************
318  * Import
319  ***********************/
320 
326  public function ‪checkImportPrerequisites(): void
327  {
328  // Check #1: Extension dependencies
329  $extKeysToInstall = [];
330  foreach ($this->dat['header']['extensionDependencies'] ?? [] as $extKey) {
331  if (!empty($extKey) && !‪ExtensionManagementUtility::isLoaded($extKey)) {
332  $extKeysToInstall[] = $extKey;
333  }
334  }
335  if ($extKeysToInstall !== []) {
336  $this->‪addError(
337  sprintf(
338  'Before you can import this file you need to install the extensions "%s".',
339  implode('", "', $extKeysToInstall)
340  )
341  );
342  }
343 
344  // Check #2: Presence of imported storage paths
345  foreach ($this->dat['header']['records']['sys_file_storage'] ?? [] as $sysFileStorageUid => $_) {
346  $storageRecord = &$this->dat['records']['sys_file_storage:' . $sysFileStorageUid]['data'];
347  if ($storageRecord['driver'] === 'Local'
348  && $storageRecord['is_writable']
349  && $storageRecord['is_online']
350  ) {
351  $storageMapUid = -1;
352  foreach ($this->storages as $storage) {
353  if ($this->‪isEquivalentStorage($storage, $storageRecord)) {
354  $storageMapUid = $storage->getUid();
355  break;
356  }
357  }
358  // The storage from the import does not have an equivalent storage
359  // in the current instance (same driver, same path, etc.). Before
360  // the storage record can get inserted later on take care the path
361  // it points to really exists and is accessible.
362  if ($storageMapUid === -1) {
363  // Unset the storage record UID when trying to create the storage object
364  // as the record does not already exist in database. The constructor of the
365  // storage object will check whether the target folder exists and set the
366  // isOnline flag depending on the outcome.
367  $storageRecordWithUid0 = $storageRecord;
368  $storageRecordWithUid0['uid'] = 0;
369  $storageObject = $this->storageRepository->createFromRecord($storageRecordWithUid0);
370  if (!$storageObject->isOnline()) {
371  $configuration = $storageObject->getConfiguration();
372  $this->‪addError(
373  sprintf(
374  'The file storage "%s" does not exist. ' .
375  'Please create the directory prior to starting the import!',
376  $storageObject->getName() . $configuration['basePath']
377  )
378  );
379  }
380  }
381  }
382  }
383 
384  if ($this->‪hasErrors()) {
386  'Prerequisites for file import are not met.',
387  1484484612
388  );
389  }
390  }
391 
397  public function ‪importData(): void
398  {
399  $this->‪initializeImport();
400 
401  // Write sys_file_storages first
403  // Write sys_file records and write the binary file data
404  $this->‪writeSysFileRecords();
405  // Write records, first pages, then the rest
406  // Fields with "hard" relations to database, files and flexform fields are kept empty during this run
407  $this->‪writePages();
408  $this->‪writeRecords();
409  // Finally all the file and database record references must be fixed. This is done after all records have supposedly
410  // been written to database. $this->importMapId will indicate two things:
411  // 1) that a record WAS written to db and
412  // 2) that it has got a new id-number.
413  $this->‪setRelations();
414  // And when all database relations are in place, we can fix file and database relations in flexform fields
415  // - since data structures often depend on relations to a DS record:
416  $this->‪setFlexFormRelations();
417  // Finally, traverse all records and process soft references with substitution attributes.
418  $this->‪processSoftReferences();
419  // Cleanup
421 
422  if ($this->‪hasErrors()) {
423  throw new ‪ImportFailedException('The import has failed.', 1484484613);
424  }
425  }
426 
430  protected function ‪initializeImport(): void
431  {
432  $this->doesImport = true;
433  $this->importMapId = [];
434  $this->importNewId = [];
435  $this->importNewIdPids = [];
436  }
437 
441  protected function ‪writeSysFileStorageRecords(): void
442  {
443  if (!isset($this->dat['header']['records']['sys_file_storage'])) {
444  return;
445  }
446 
447  $importData = [];
448 
449  $storageUidsToBeResetToDefaultStorage = [];
450  foreach ($this->dat['header']['records']['sys_file_storage'] as $sysFileStorageUid => $_) {
451  $storageRecord = &$this->dat['records']['sys_file_storage:' . $sysFileStorageUid]['data'];
452  if ($storageRecord['driver'] === 'Local'
453  && $storageRecord['is_writable']
454  && $storageRecord['is_online']
455  ) {
456  foreach ($this->storages as $storage) {
457  if ($this->‪isEquivalentStorage($storage, $storageRecord)) {
458  $this->importMapId['sys_file_storage'][$sysFileStorageUid] = $storage->getUid();
459  break;
460  }
461  }
462 
463  if (!isset($this->importMapId['sys_file_storage'][$sysFileStorageUid])) {
464  // Local, writable and online storage. May be used later for writing files.
465  // Does not currently exist, mark the storage for import.
466  $this->‪addSingle($importData, 'sys_file_storage', $sysFileStorageUid, 0);
467  }
468  } else {
469  // Storage with non-local drivers can be imported, but must not be used to save files as you cannot
470  // be sure that this is supported. In this case the default storage is used. Non-writable and
471  // non-online storage may be created as duplicates because you were unable to check the detailed
472  // configuration options at that time.
473  $this->‪addSingle($importData, 'sys_file_storage', $sysFileStorageUid, 0);
474  $storageUidsToBeResetToDefaultStorage[] = $sysFileStorageUid;
475  }
476  }
477 
478  // Write new storages to the database
479  $dataHandler = $this->‪createDataHandler();
480  // Because all records are submitted in the correct order with positive pid numbers,
481  // we should internally reverse the order of submission.
482  $dataHandler->reverseOrder = true;
483  $dataHandler->isImporting = true;
484  $dataHandler->start($importData, []);
485  $dataHandler->process_datamap();
486  $this->‪addToMapId($importData, $dataHandler->substNEWwithIDs);
487 
488  // Refresh internal storage representation after potential storage import
489  $this->‪fetchStorages();
490 
491  // Map references of non-local / non-writable / non-online storages to the default storage
492  $defaultStorageUid = $this->defaultStorage?->getUid();
493  foreach ($storageUidsToBeResetToDefaultStorage as $storageUidToBeResetToDefaultStorage) {
494  $this->importMapId['sys_file_storage'][$storageUidToBeResetToDefaultStorage] = $defaultStorageUid;
495  }
496 
497  // Unset the sys_file_storage records to prevent an import in writeRecords()
498  unset($this->dat['header']['records']['sys_file_storage']);
499  }
500 
509  protected function ‪isEquivalentStorage(‪ResourceStorage $storageObject, array &$storageRecord): bool
510  {
511  if ($storageObject->‪getDriverType() === $storageRecord['driver']
512  && $storageObject->‪isWritable() === (bool)$storageRecord['is_writable']
513  && $storageObject->‪isOnline() === (bool)$storageRecord['is_online']
514  ) {
515  $storageRecordConfiguration = GeneralUtility::makeInstance(FlexFormService::class)
516  ->convertFlexFormContentToArray($storageRecord['configuration'] ?? '');
517  $storageObjectConfiguration = $storageObject->‪getConfiguration();
518  if ($storageRecordConfiguration['pathType'] === $storageObjectConfiguration['pathType']
519  && $storageRecordConfiguration['basePath'] === $storageObjectConfiguration['basePath']
520  ) {
521  return true;
522  }
523  }
524  return false;
525  }
526 
530  protected function ‪writeSysFileRecords(): void
531  {
532  if (!isset($this->dat['header']['records']['sys_file'])) {
533  return;
534  }
535 
536  $this->‪addGeneralErrorsByTable('sys_file');
537 
538  $temporaryFolder = $this->‪getOrCreateTemporaryFolderName();
539  $sanitizedFolderMappings = [];
540 
541  foreach ($this->dat['header']['records']['sys_file'] as $sysFileUid => $_) {
542  $fileRecord = &$this->dat['records']['sys_file:' . $sysFileUid]['data'];
543 
544  $temporaryFile = null;
545  $temporaryFilePath = $temporaryFolder . '/' . $fileRecord['sha1'];
546 
547  if ($this->isFilesSavedOutsideImportFile) {
548  if (is_file($temporaryFilePath) && sha1_file($temporaryFilePath) === $fileRecord['sha1']) {
549  $temporaryFile = $temporaryFilePath;
550  } else {
551  $this->‪addError(sprintf(
552  'Error: Temporary file %s could not be found or does not match the checksum!',
553  $temporaryFilePath
554  ));
555  continue;
556  }
557  } else {
558  $fileId = md5($fileRecord['storage'] . ':' . $fileRecord['identifier_hash']);
559  if (isset($this->dat['files_fal'][$fileId]['content'])) {
560  $fileInfo = &$this->dat['files_fal'][$fileId];
561  if (‪GeneralUtility::writeFile($temporaryFilePath, $fileInfo['content'])) {
562  clearstatcache();
563  $temporaryFile = $temporaryFilePath;
564  } else {
565  $this->‪addError(sprintf(
566  'Error: Temporary file %s was not written as it should have been!',
567  $temporaryFilePath
568  ));
569  continue;
570  }
571  } else {
572  $this->‪addError(sprintf('Error: No file found for ID %s', $fileId));
573  continue;
574  }
575  }
576 
577  $storageUid = $this->importMapId['sys_file_storage'][$fileRecord['storage']] ?? $fileRecord['storage'];
578  if (isset($this->storagesAvailableForImport[$storageUid])) {
579  $storage = $this->storagesAvailableForImport[$storageUid];
580  } elseif ($storageUid === 0 || $storageUid === '0') {
581  $storage = $this->storageRepository->findByUid(0);
582  } elseif ($this->defaultStorage !== null) {
583  $storage = ‪$this->defaultStorage;
584  } else {
585  $this->‪addError(sprintf(
586  'Error: No storage available for the file "%s" with storage uid "%s"',
587  $fileRecord['identifier'],
588  $fileRecord['storage']
589  ));
590  continue;
591  }
592 
593  $file = null;
594  try {
595  if ($storage->hasFile($fileRecord['identifier'])) {
597  $file = $storage->getFile($fileRecord['identifier']);
598  if ($file->getSha1() !== $fileRecord['sha1']) {
599  $file = null;
600  }
601  }
602  } catch (‪Exception $e) {
603  // @todo: Can this exception be thrown anywhere?
604  $file = null;
605  }
606 
607  if ($file === null) {
608  $folderName = ‪PathUtility::dirname(ltrim($fileRecord['identifier'], '/'));
609  if (in_array($folderName, $sanitizedFolderMappings, true)) {
610  $folderName = $sanitizedFolderMappings[$folderName];
611  }
612  if (!$storage->hasFolder($folderName)) {
613  try {
614  $importFolder = $storage->createFolder($folderName);
615  if ($importFolder->getIdentifier() !== $folderName && !in_array($folderName, $sanitizedFolderMappings, true)) {
616  $sanitizedFolderMappings[$folderName] = $importFolder->getIdentifier();
617  }
618  } catch (‪Exception $e) {
619  $this->‪addError(sprintf(
620  'Error: Folder "%s" could not be created for file "%s" with storage uid "%s"',
621  $folderName,
622  $fileRecord['identifier'],
623  $fileRecord['storage']
624  ));
625  continue;
626  }
627  } else {
628  $importFolder = $storage->getFolder($folderName);
629  }
630 
631  $this->‪callHook('before_addSysFileRecord', [
632  'fileRecord' => $fileRecord,
633  'importFolder' => $importFolder,
634  'temporaryFile' => $temporaryFile,
635  ]);
636 
637  try {
639  $file = $storage->addFile($temporaryFile, $importFolder, $fileRecord['name']);
640  } catch (‪Exception $e) {
641  $this->‪addError(sprintf(
642  'Error: File could not be added to the storage: "%s" with storage uid "%s"',
643  $fileRecord['identifier'],
644  $fileRecord['storage']
645  ));
646  continue;
647  }
648 
649  if ($file->getSha1() !== $fileRecord['sha1']) {
650  $this->‪addError(sprintf(
651  'Error: The hash of the written file is not identical to the import data! ' .
652  'File could be corrupted! File: "%s" with storage uid "%s"',
653  $fileRecord['identifier'],
654  $fileRecord['storage']
655  ));
656  }
657  }
658 
659  // save the new uid in the import id map
660  $this->importMapId['sys_file'][$fileRecord['uid']] = $file->getUid();
661  $this->‪fixUidLocalInSysFileReferenceRecords((int)$fileRecord['uid'], $file->getUid());
662  }
663 
664  // unset the sys_file records to prevent an import in writeRecords()
665  unset($this->dat['header']['records']['sys_file']);
666  // remove all sys_file_reference records that point to file records which are unknown
667  // in the system to prevent exceptions
669  }
670 
686  protected function ‪fixUidLocalInSysFileReferenceRecords(int $oldFileUid, int $newFileUid): void
687  {
688  if (!isset($this->dat['header']['records']['sys_file_reference'])) {
689  return;
690  }
691 
692  foreach ($this->dat['header']['records']['sys_file_reference'] as $sysFileReferenceUid => $_) {
693  if (!isset($this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['hasBeenMapped'])
694  && $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data']['uid_local'] == $oldFileUid
695  ) {
696  $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['hasBeenMapped'] = true;
697  $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data']['uid_local'] = $newFileUid;
698  }
699  }
700  }
701 
707  {
708  if (!isset($this->dat['header']['records']['sys_file_reference'])) {
709  return;
710  }
711 
712  foreach ($this->dat['header']['records']['sys_file_reference'] as $sysFileReferenceUid => $_) {
713  $fileReferenceRecord = &$this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data'];
714  if (!in_array($fileReferenceRecord['uid_local'], (array)($this->importMapId['sys_file'] ?? []))) {
715  unset($this->dat['header']['records']['sys_file_reference'][$sysFileReferenceUid]);
716  unset($this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]);
717  $this->‪addError(sprintf(
718  'Error: sys_file_reference record "%s" with relation to sys_file record "%s"'
719  . ', which is not part of the import data, was not imported.',
720  $sysFileReferenceUid,
721  $fileReferenceRecord['uid_local']
722  ));
723  }
724  }
725  }
726 
734  protected function ‪writePages(): void
735  {
736  if (!isset($this->dat['header']['records']['pages'])) {
737  return;
738  }
739 
740  $importData = [];
741 
742  // Add page tree
743  $remainingPages = $this->dat['header']['records']['pages'];
744  if (is_array($this->dat['header']['pagetree'] ?? null)) {
745  $pageList = [];
746  $this->‪flatInversePageTree($this->dat['header']['pagetree'], $pageList);
747  foreach ($pageList as $pageUid => $_) {
748  ‪$pid = $this->dat['header']['records']['pages'][$pageUid]['pid'] ?? null;
749  ‪$pid = $this->importNewIdPids[‪$pid] ?? ‪$this->pid;
750  $this->‪addSingle($importData, 'pages', (int)$pageUid, ‪$pid);
751  unset($remainingPages[$pageUid]);
752  }
753  }
754 
755  // Add remaining pages on root level
756  foreach ($remainingPages as $pageUid => $_) {
757  $this->‪addSingle($importData, 'pages', (int)$pageUid, $this->pid);
758  }
759 
760  // Write pages to the database
761  $dataHandler = $this->‪createDataHandler();
762  $dataHandler->isImporting = true;
763  $this->‪callHook('before_writeRecordsPages', [
764  'tce' => $dataHandler,
765  'data' => &$importData,
766  ]);
767  $dataHandler->suggestedInsertUids = ‪$this->suggestedInsertUids;
768  $dataHandler->start($importData, []);
769  $dataHandler->process_datamap();
770  $this->‪callHook('after_writeRecordsPages', [
771  'tce' => $dataHandler,
772  ]);
773  $this->‪addToMapId($importData, $dataHandler->substNEWwithIDs);
774 
775  // Sort pages
776  $this->‪writePagesOrder();
777  }
778 
786  protected function ‪writePagesOrder(): void
787  {
788  if (!$this->update || !is_array($this->dat['header']['pagetree'] ?? null)) {
789  return;
790  }
791 
792  $importCmd = [];
793 
794  // Get uid-pid relations and traverse them in order to map to possible new IDs
795  $pageList = [];
796  $this->‪flatInversePageTree($this->dat['header']['pagetree'], $pageList);
797  foreach ($pageList as $pageUid => $pagePid) {
798  if ($pagePid >= 0 && $this->‪doRespectPid('pages', $pageUid)) {
799  // If the page has been assigned a new ID (because it was created), use that instead!
800  if (!‪MathUtility::canBeInterpretedAsInteger($this->importNewIdPids[$pageUid])) {
801  if ($this->importMapId['pages'][$pageUid]) {
802  $mappedUid = $this->importMapId['pages'][$pageUid];
803  $importCmd['pages'][$mappedUid]['move'] = $pagePid;
804  }
805  } else {
806  $importCmd['pages'][$pageUid]['move'] = $pagePid;
807  }
808  }
809  }
810 
811  // Move pages in the database
812  if (!empty($importCmd)) {
813  $dataHandler = $this->‪createDataHandler();
814  $this->‪callHook('before_writeRecordsPagesOrder', [
815  'tce' => &$dataHandler,
816  'data' => &$importCmd,
817  ]);
818  $dataHandler->start([], $importCmd);
819  $dataHandler->process_cmdmap();
820  $this->‪callHook('after_writeRecordsPagesOrder', [
821  'tce' => &$dataHandler,
822  ]);
823  }
824  }
825 
834  protected function ‪doRespectPid(string $table, int ‪$uid): bool
835  {
836  return ($this->importMode[$table . ':' . ‪$uid] ?? '') !== self::IMPORT_MODE_IGNORE_PID &&
837  (!$this->globalIgnorePid || ($this->importMode[$table . ':' . ‪$uid] ?? '') === self::IMPORT_MODE_RESPECT_PID);
838  }
839 
845  protected function ‪writeRecords(): void
846  {
847  $importData = [];
848 
849  // Write the rest of the records
850  if (is_array($this->dat['header']['records'] ?? null)) {
851  foreach ($this->dat['header']['records'] as $table => $records) {
852  $this->‪addGeneralErrorsByTable($table);
853  if ($table !== 'pages') {
854  foreach ($records as ‪$uid => ‪$record) {
855  // PID: Set the main $this->pid, unless a NEW-id is found
856  ‪$pid = isset($this->importMapId['pages'][‪$record['pid']])
857  ? (int)$this->importMapId['pages'][‪$record['pid']]
858  : $this->pid;
859  if (isset(‪$GLOBALS['TCA'][$table]['ctrl']['rootLevel'])) {
860  $rootLevelSetting = (int)‪$GLOBALS['TCA'][$table]['ctrl']['rootLevel'];
861  if ($rootLevelSetting === 1) {
862  ‪$pid = 0;
863  } elseif ($rootLevelSetting === 0 && ‪$pid === 0) {
864  $this->‪addError('Error: Record type ' . $table . ' is not allowed on pid 0');
865  continue;
866  }
867  }
868  // Add record
869  $this->‪addSingle($importData, $table, ‪$uid, ‪$pid);
870  }
871  }
872  }
873  } else {
874  $this->‪addError('Error: No records defined in internal data array.');
875  }
876 
877  // Write records to the database
878  $dataHandler = $this->‪createDataHandler();
879  $this->‪callHook('before_writeRecordsRecords', [
880  'tce' => $dataHandler,
881  'data' => &$importData,
882  ]);
883  $dataHandler->suggestedInsertUids = ‪$this->suggestedInsertUids;
884  // Because all records are submitted in the correct order with positive pid numbers,
885  // we should internally reverse the order of submission.
886  $dataHandler->reverseOrder = true;
887  $dataHandler->isImporting = true;
888  $dataHandler->start($importData, []);
889  $dataHandler->process_datamap();
890  $this->‪callHook('after_writeRecordsRecords', [
891  'tce' => $dataHandler,
892  ]);
893  $this->‪addToMapId($importData, $dataHandler->substNEWwithIDs);
894 
895  // Sort records
896  $this->‪writeRecordsOrder();
897  }
898 
906  protected function ‪writeRecordsOrder(): void
907  {
908  if (!$this->update) {
909  return;
910  }
911 
912  $importCmd = [];
913 
914  $pageList = [];
915  if (is_array($this->dat['header']['pagetree'] ?? null)) {
916  $this->‪flatInversePageTree($this->dat['header']['pagetree'], $pageList);
917  }
918  // @todo: drop by-reference and write final $this->dat at the end of method?!
919  if (is_array($this->dat['header']['pid_lookup'] ?? null)) {
920  foreach ($this->dat['header']['pid_lookup'] as ‪$pid => &$recordsByPid) {
921  $mappedPid = $this->importMapId['pages'][‪$pid] ?? ‪$this->pid;
923  foreach ($recordsByPid as $table => &$records) {
924  // If $mappedPid === $this->pid then we are on root level and we can consider to move pages as well!
925  // (they will not be in the page tree!)
926  if ($table !== 'pages' || !isset($pageList[‪$pid])) {
927  foreach (array_reverse(array_keys($records)) as ‪$uid) {
928  if ($this->‪doRespectPid($table, (int)‪$uid)) {
929  if (isset($this->importMapId[$table][‪$uid])) {
930  $mappedUid = $this->importMapId[$table][‪$uid];
931  $importCmd[$table][$mappedUid]['move'] = $mappedPid;
932  }
933  }
934  }
935  }
936  }
937  }
938  }
939  }
940 
941  // Move records in the database
942  if (!empty($importCmd)) {
943  $dataHandler = $this->‪createDataHandler();
944  $this->‪callHook('before_writeRecordsRecordsOrder', [
945  'tce' => $dataHandler,
946  'data' => &$importCmd,
947  ]);
948  $dataHandler->start([], $importCmd);
949  $dataHandler->process_cmdmap();
950  $this->‪callHook('after_writeRecordsRecordsOrder', [
951  'tce' => $dataHandler,
952  ]);
953  }
954  }
955 
967  protected function ‪addSingle(array &$importData, string $table, int ‪$uid, ‪$pid): void
968  {
969  // @todo return modified $importData instead of by-reference.
970  if (($this->importMode[$table . ':' . ‪$uid] ?? '') === self::IMPORT_MODE_EXCLUDE) {
971  return;
972  }
973 
974  ‪$record = $this->dat['records'][$table . ':' . ‪$uid]['data'] ?? null;
975 
976  if (!is_array(‪$record)) {
977  if (!($table === 'pages' && ‪$uid === 0)) {
978  // On root level we don't want this error message.
979  $this->‪addError('Error: No record was found in data array!');
980  }
981  return;
982  }
983 
984  // Generate record ID
985  $ID = ‪StringUtility::getUniqueId('NEW');
986  if ($this->update
987  && $this->‪getRecordFromDatabase($table, ‪$uid) !== null
988  && ($this->importMode[$table . ':' . ‪$uid] ?? '') !== self::IMPORT_MODE_AS_NEW
989  ) {
990  $ID = ‪$uid;
991  } elseif ($table === 'sys_file_metadata'
992  && ‪$record['sys_language_uid'] === '0'
993  && isset($this->importMapId['sys_file'][‪$record['file']])
994  ) {
995  // On adding sys_file records the belonging sys_file_metadata record was also created:
996  // If there is one, the record needs to be overwritten instead of a new one created.
997  $databaseRecord = $this->‪getSysFileMetaDataFromDatabase(
998  $this->importMapId['sys_file'][‪$record['file']],
999  0
1000  );
1001  if (is_array($databaseRecord)) {
1002  $this->importMapId['sys_file_metadata'][‪$record['uid']] = $databaseRecord['uid'];
1003  $ID = $databaseRecord['uid'];
1004  }
1005  }
1006 
1007  // Mapping of generated record ID to original record UID
1008  $this->importNewId[$table . ':' . $ID] = ['table' => $table, 'uid' => ‪$uid];
1009  if ($table === 'pages') {
1010  $this->importNewIdPids[‪$uid] = $ID;
1011  }
1012 
1013  // Record data
1014  $importData[$table][$ID] = ‪$record;
1015  $importData[$table][$ID]['tx_impexp_origuid'] = $importData[$table][$ID]['uid'];
1016 
1017  // Record permissions
1018  if ($table === 'pages') {
1019  // Have to reset the user/group IDs so pages are owned by the importing user.
1020  // Otherwise strange things may happen for non-admins!
1021  unset($importData[$table][$ID]['perms_userid']);
1022  unset($importData[$table][$ID]['perms_groupid']);
1023  }
1024 
1025  // Record UID and PID
1026  unset($importData[$table][$ID]['uid']);
1027  // - for existing record
1029  unset($importData[$table][$ID]['pid']);
1030  }
1031  // - for new record
1032  else {
1033  $importData[$table][$ID]['pid'] = ‪$pid;
1034  if ((($this->importMode[$table . ':' . ‪$uid] ?? '') === self::IMPORT_MODE_FORCE_UID && $this->update
1035  || $this->forceAllUids)
1036  && $this->‪getBackendUser()->isAdmin()
1037  ) {
1038  $importData[$table][$ID]['uid'] = ‪$uid;
1039  $this->suggestedInsertUids[$table . ':' . ‪$uid] = 'DELETE';
1040  }
1041  }
1042 
1043  // Record relations
1044  foreach ($this->dat['records'][$table . ':' . ‪$uid]['rels'] as $field => &$relation) {
1045  switch ($relation['type'] ?? '') {
1046  case 'db':
1047  case 'file':
1048  // Set blank now, fix later in setRelations(),
1049  // because we need to know ALL newly created IDs before we can map relations!
1050  // In the meantime we set NO values for relations.
1051  //
1052  // BUT for field uid_local of table sys_file_reference the relation MUST not be cleared here,
1053  // because the value is already the uid of the right imported sys_file record.
1054  // @see fixUidLocalInSysFileReferenceRecords()
1055  // If it's empty or a uid to another record the FileExtensionFilter will throw an exception or
1056  // delete the reference record if the file extension of the related record doesn't match.
1057  if (!($table === 'sys_file_reference' && $field === 'uid_local')
1058  && is_array(‪$GLOBALS['TCA'][$table]['columns'][$field]['config'] ?? false)
1059  ) {
1060  $importData[$table][$ID][$field] = $this->‪getReferenceDefaultValue(‪$GLOBALS['TCA'][$table]['columns'][$field]['config']);
1061  }
1062  // Set to "0" for integer fields, or else we will get a db error in DataHandler persistence.
1063  if (!empty(‪$GLOBALS['TCA'][$table]['ctrl']['translationSource'] ?? '')
1064  && $field === ‪$GLOBALS['TCA'][$table]['ctrl']['translationSource']
1065  ) {
1066  $importData[$table][$ID][$field] = 0;
1067  }
1068  break;
1069  case 'flex':
1070  // Set blank now, fix later in setFlexFormRelations().
1071  // In the meantime we set NO values for flexforms - this is mainly because file references
1072  // inside will not be processed properly. In fact references will point to no file
1073  // or existing files (in which case there will be double-references which is a big problem of
1074  // course!).
1075  //
1076  // BUT for the field "configuration" of the table "sys_file_storage" the relation MUST NOT be
1077  // cleared, because the configuration array contains only string values, which are furthermore
1078  // important for the further import, e.g. the base path.
1079  if (!($table === 'sys_file_storage' && $field === 'configuration')) {
1080  $importData[$table][$ID][$field] = $this->‪getReferenceDefaultValue(‪$GLOBALS['TCA'][$table]['columns'][$field]['config']);
1081  }
1082  break;
1083  }
1084  }
1085  }
1086 
1092  protected function ‪getReferenceDefaultValue(array $configuration): int|float|string
1093  {
1094  if (!empty($configuration['MM']) || !empty($configuration['foreign_field'])) {
1095  return 0;
1096  }
1097  if (array_key_exists('default', $configuration)) {
1098  return $configuration['default'];
1099  }
1100  return '';
1101  }
1102 
1106  protected function ‪getSysFileMetaDataFromDatabase(int $file, int $sysLanguageUid): ?array
1107  {
1108  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1109  ->getQueryBuilderForTable('sys_file_metadata');
1110 
1111  $databaseRecord = $queryBuilder->select('uid')
1112  ->from('sys_file_metadata')
1113  ->where(
1114  $queryBuilder->expr()->eq(
1115  'file',
1116  $queryBuilder->createNamedParameter($file, ‪Connection::PARAM_INT)
1117  ),
1118  $queryBuilder->expr()->eq(
1119  'sys_language_uid',
1120  $queryBuilder->createNamedParameter($sysLanguageUid, ‪Connection::PARAM_INT)
1121  )
1122  )
1123  ->executeQuery()
1124  ->fetchAssociative();
1125 
1126  return is_array($databaseRecord) ? $databaseRecord : null;
1127  }
1128 
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 
1164  protected function ‪createDataHandler(): ‪DataHandler
1165  {
1166  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
1167  $dataHandler->dontProcessTransformations = true;
1168  $dataHandler->enableLogging = ‪$this->enableLogging;
1169  return $dataHandler;
1170  }
1171 
1172  /********************
1173  * Import relations
1174  *******************/
1175 
1184  protected function ‪setRelations(): void
1185  {
1186  $updateData = [];
1187 
1188  foreach ($this->importNewId as $original) {
1189  $table = $original['table'];
1190  ‪$uid = $original['uid'];
1191 
1192  if (isset($this->importMapId[$table][‪$uid])) {
1193  if (is_array($this->dat['records'][$table . ':' . ‪$uid]['rels'] ?? null)) {
1194  $actualUid = BackendUtility::wsMapId($table, $this->importMapId[$table][‪$uid]);
1195  foreach ($this->dat['records'][$table . ':' . ‪$uid]['rels'] as $field => $relation) {
1196  // Field "uid_local" of sys_file_reference needs no update because the correct reference uid was already written.
1197  // @see ImportExport::fixUidLocalInSysFileReferenceRecords()
1198  if (isset($relation['type']) && !($table === 'sys_file_reference' && $field === 'uid_local') && $relation['type'] === 'db' && isset(‪$GLOBALS['TCA'][$table]['columns'][$field])) {
1199  if (is_array($relation['itemArray'] ?? null) && !empty($relation['itemArray'])) {
1200  $fieldTca = ‪$GLOBALS['TCA'][$table]['columns'][$field];
1201  $actualRelations = $this->‪remapRelationsOfField($relation['itemArray'], $fieldTca['config'], $field);
1202  $updateData[$table][$actualUid][$field] = implode(',', $actualRelations);
1203  }
1204  }
1205  }
1206  } else {
1207  $this->‪addError(sprintf('Error: This record does not appear to have a relation array! (%s:%s)', $table, ‪$uid));
1208  }
1209  } else {
1210  $this->‪addError(sprintf('Error: This record does not appear to have been created! (%s:%s)', $table, ‪$uid));
1211  }
1212  }
1213 
1214  if (!empty($updateData)) {
1215  $dataHandler = $this->‪createDataHandler();
1216  $dataHandler->isImporting = true;
1217  $this->‪callHook('before_setRelation', [
1218  'tce' => $dataHandler,
1219  'data' => &$updateData,
1220  ]);
1221  $dataHandler->start($updateData, []);
1222  $dataHandler->process_datamap();
1223  $this->‪callHook('after_setRelations', [
1224  'tce' => $dataHandler,
1225  ]);
1226  }
1227  }
1228 
1240  protected function ‪remapRelationsOfField(array $fieldRelations, array $fieldConfig, string $field = ''): array
1241  {
1242  $actualRelations = [];
1243  foreach ($fieldRelations as $relation) {
1244  if (isset($this->importMapId[$relation['table']][$relation['id']])) {
1245  $actualUid = $this->importMapId[$relation['table']][$relation['id']];
1246  if ($fieldConfig['type'] === 'input' && isset($fieldConfig['wizards']['link'])) {
1247  // If an input field has a relation to a sys_file record this need to be converted back to
1248  // the public path. But use getPublicUrl() here, because could normally only be a local file path.
1249  try {
1250  $file = GeneralUtility::makeInstance(ResourceFactory::class)->retrieveFileOrFolderObject($actualUid);
1251  $actualRelations[] = $file->getPublicUrl();
1252  } catch (\‪Exception $e) {
1253  $actualRelations[] = 'file:' . $actualUid;
1254  }
1255  } elseif (!empty(‪$GLOBALS['TCA'][$relation['table']]['ctrl']['translationSource'] ?? '')
1256  && $field === ‪$GLOBALS['TCA'][$relation['table']]['ctrl']['translationSource']
1257  ) {
1258  // "l10n_source" is of type "passthrough" so the "_" syntax won't be replaced.
1259  $actualRelations[] = $actualUid;
1260  } else {
1261  $actualRelations[] = $relation['table'] . '_' . $actualUid;
1262  }
1263  } elseif ($this->‪isTableStatic($relation['table']) || $this->‪isRecordExcluded($relation['table'], (int)$relation['id']) || $relation['id'] < 0) {
1264  // Some select types could contain negative values, e.g. fe_groups (-1, -2).
1265  // This must be handled on both export and import.
1266  $actualRelations[] = $relation['table'] . '_' . $relation['id'];
1267  } else {
1268  $this->‪addError('Lost relation: ' . $relation['table'] . ':' . $relation['id']);
1269  }
1270  }
1271 
1272  return $actualRelations;
1273  }
1274 
1282  protected function ‪setFlexFormRelations(): void
1283  {
1284  $updateData = [];
1285 
1286  foreach ($this->importNewId as $original) {
1287  $table = $original['table'];
1288  ‪$uid = $original['uid'];
1289 
1290  if (isset($this->importMapId[$table][‪$uid])) {
1291  if (is_array($this->dat['records'][$table . ':' . ‪$uid]['rels'] ?? null)) {
1292  $actualUid = BackendUtility::wsMapId($table, $this->importMapId[$table][‪$uid]);
1293  foreach ($this->dat['records'][$table . ':' . ‪$uid]['rels'] as $field => $relation) {
1294  // Field "configuration" of sys_file_storage needs no update because it has not been removed
1295  // and has no relations.
1296  // @see Import::addSingle()
1297  if (isset($relation['type']) && $relation['type'] == 'flex' && !($table === 'sys_file_storage' && $field === 'configuration')) {
1298  // Re-insert temporarily removed original FlexForm data as fallback
1299  // @see Import::addSingle()
1300  $updateData[$table][$actualUid][$field] = $this->dat['records'][$table . ':' . ‪$uid]['data'][$field];
1301  if (!empty($relation['flexFormRels']['db'])) {
1302  $actualRecord = BackendUtility::getRecord($table, $actualUid, '*');
1303  $fieldTca = &‪$GLOBALS['TCA'][$table]['columns'][$field];
1304  if (is_array($actualRecord) && is_array($fieldTca['config'] ?? null) && $fieldTca['config']['type'] === 'flex') {
1305  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1306  $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
1307  $fieldTca,
1308  $table,
1309  $field,
1310  $actualRecord
1311  );
1312  $dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
1313  $flexFormData = (new ‪Typo3XmlParser())->decodeWithReturningExceptionAsString(
1314  (string)($this->dat['records'][$table . ':' . ‪$uid]['data'][$field] ?? ''),
1317  ])
1318  );
1319  if (is_array($flexFormData['data'] ?? null)) {
1320  $flexFormIterator = GeneralUtility::makeInstance(DataHandler::class);
1321  $flexFormIterator->callBackObj = $this;
1322  $flexFormData['data'] = $flexFormIterator->checkValue_flex_procInData(
1323  $flexFormData['data'],
1324  [],
1325  $dataStructure,
1326  [$relation],
1327  'remapRelationsOfFlexFormCallBack'
1328  );
1329  }
1330  if (is_array($flexFormData['data'] ?? null)) {
1331  $updateData[$table][$actualUid][$field] = $flexFormData;
1332  }
1333  }
1334  }
1335  }
1336  }
1337  } else {
1338  $this->‪addError(sprintf('Error: This record does not appear to have a relation array! (%s:%s)', $table, ‪$uid));
1339  }
1340  } else {
1341  $this->‪addError(sprintf('Error: This record does not appear to have been created! (%s:%s)', $table, ‪$uid));
1342  }
1343  }
1344 
1345  if (!empty($updateData)) {
1346  $dataHandler = $this->‪createDataHandler();
1347  $dataHandler->isImporting = true;
1348  $this->‪callHook('before_setFlexFormRelations', [
1349  'tce' => $dataHandler,
1350  'data' => &$updateData,
1351  ]);
1352  $dataHandler->start($updateData, []);
1353  $dataHandler->process_datamap();
1354  $this->‪callHook('after_setFlexFormRelations', [
1355  'tce' => $dataHandler,
1356  ]);
1357  }
1358  }
1359 
1372  public function ‪remapRelationsOfFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path): array
1373  {
1374  [$relation] = $pParams;
1375  // In case the $path is used as index without a trailing slash we will remove that
1376  if (!is_array($relation['flexFormRels']['db'][$path] ?? null) && is_array($relation['flexFormRels']['db'][rtrim($path, '/')] ?? false)) {
1377  $path = rtrim($path, '/');
1378  }
1379  if (is_array($relation['flexFormRels']['db'][$path] ?? null)) {
1380  $actualRelations = $this->‪remapRelationsOfField($relation['flexFormRels']['db'][$path], $dsConf);
1381  $dataValue = implode(',', $actualRelations);
1382  }
1383  return ['value' => $dataValue];
1384  }
1385 
1386  /**************************
1387  * Import soft references
1388  *************************/
1389 
1393  protected function ‪processSoftReferences(): void
1394  {
1395  $updateData = [];
1396 
1397  foreach ($this->dat['header']['records'] ?? [] as $table => $records) {
1398  if (!isset(‪$GLOBALS['TCA'][$table])) {
1399  continue;
1400  }
1401  foreach ($records as ‪$uid => ‪$record) {
1402  if (is_array(‪$record['softrefs'] ?? null)) {
1403  $actualUid = BackendUtility::wsMapId($table, $this->importMapId[$table][‪$uid] ?? 0);
1404  // First, group soft references by record field ...
1405  // (this could probably also have been done with $this->dat['records'] instead of $this->dat['header'])
1406  $softrefs = [];
1407  foreach (‪$record['softrefs'] as $softref) {
1408  if ($softref['field'] && is_array($softref['subst'] ?? null) && $softref['subst']['tokenID']) {
1409  $softrefs[$softref['field']][$softref['subst']['tokenID']] = $softref;
1410  }
1411  }
1412  // ... then process only fields which require substitution.
1413  foreach ($softrefs as $field => $softrefsByField) {
1414  if (is_array(‪$GLOBALS['TCA'][$table]['columns'][$field] ?? null)) {
1415  $fieldTca = ‪$GLOBALS['TCA'][$table]['columns'][$field];
1416  if ($fieldTca['config']['type'] === 'flex') {
1417  $actualRecord = BackendUtility::getRecord($table, $actualUid, '*');
1418  if (is_array($actualRecord)) {
1419  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1420  $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
1421  $fieldTca,
1422  $table,
1423  $field,
1424  $actualRecord
1425  );
1426  $dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
1427  $flexFormData = (new ‪Typo3XmlParser())->decodeWithReturningExceptionAsString(
1428  (string)($actualRecord[$field] ?? ''),
1431  ])
1432  );
1433  if (is_array($flexFormData['data'] ?? null)) {
1434  $flexFormIterator = GeneralUtility::makeInstance(DataHandler::class);
1435  $flexFormIterator->callBackObj = $this;
1436  $flexFormData['data'] = $flexFormIterator->checkValue_flex_procInData(
1437  $flexFormData['data'],
1438  [],
1439  $dataStructure,
1440  [$table, ‪$uid, $field, $softrefsByField],
1441  'processSoftReferencesFlexFormCallBack'
1442  );
1443  }
1444  if (is_array($flexFormData['data'] ?? null)) {
1445  $updateData[$table][$actualUid][$field] = $flexFormData;
1446  }
1447  }
1448  } else {
1449  // Get tokenizedContent string and proceed only if that is not blank:
1450  $tokenizedContent = $this->dat['records'][$table . ':' . ‪$uid]['rels'][$field]['softrefs']['tokenizedContent'] ?? '';
1451  if ($tokenizedContent !== '' && is_array($softrefsByField)) {
1452  $updateData[$table][$actualUid][$field] = $this->‪processSoftReferencesSubstTokens($tokenizedContent, $softrefsByField, $table, (string)‪$uid);
1453  }
1454  }
1455  }
1456  }
1457  }
1458  }
1459  }
1460 
1461  // Update soft references in the database
1462  $dataHandler = $this->‪createDataHandler();
1463  $dataHandler->isImporting = true;
1464  $this->‪callHook('before_processSoftReferences', [
1465  'tce' => $dataHandler,
1466  'data' => &$updateData,
1467  ]);
1468  $dataHandler->enableLogging = true;
1469  $dataHandler->start($updateData, []);
1470  $dataHandler->process_datamap();
1471  $this->‪callHook('after_processSoftReferences', [
1472  'tce' => $dataHandler,
1473  ]);
1474  }
1475 
1487  public function ‪processSoftReferencesFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path): array
1488  {
1489  [$table, $origUid, $field, $softrefs] = $pParams;
1490  if (is_array($softrefs)) {
1491  // Filter for soft references of this path ...
1492  $softrefsByPath = [];
1493  foreach ($softrefs as $tokenID => $softref) {
1494  if ($softref['structurePath'] === $path) {
1495  $softrefsByPath[$tokenID] = $softref;
1496  }
1497  }
1498  // ... and perform the processing.
1499  if (!empty($softrefsByPath)) {
1500  $tokenizedContent = $this->dat['records'][$table . ':' . $origUid]['rels'][$field]['flexFormRels']['softrefs'][$path]['tokenizedContent'];
1501  if (strlen($tokenizedContent)) {
1502  $dataValue = $this->‪processSoftReferencesSubstTokens($tokenizedContent, $softrefsByPath, $table, (string)$origUid);
1503  }
1504  }
1505  }
1506  return ['value' => $dataValue];
1507  }
1508 
1518  protected function ‪processSoftReferencesSubstTokens(string $tokenizedContent, array $softrefs, string $table, string ‪$uid): string
1519  {
1520  foreach ($softrefs as $softref) {
1521  $tokenID = $softref['subst']['tokenID'];
1522  $insertValue = $softref['subst']['tokenValue'];
1523  switch ((string)($this->softrefCfg[$tokenID]['mode'] ?? '')) {
1525  // This is the same as handling static relations:
1526  // Do not create or update the related file or record and do not change the link in any way,
1527  // but use the link as it was when exported.
1528  break;
1530  // This is the same as "exclude" with the option to manually edit the link before importing.
1531  $insertValue = $this->softrefInputValues[$tokenID];
1532  break;
1533  default:
1534  // This is almost the same as handling relations:
1535  // - Creating or updating related files and adjusting the file reference to link to the new file.
1536  // - Adjusting the record reference to link to the already imported record - if any.
1537  switch ((string)$softref['subst']['type']) {
1538  case 'file':
1539  $insertValue = $this->‪processSoftReferencesSaveFile($softref['subst']['relFileName'], $softref, $table, ‪$uid);
1540  break;
1541  case 'db':
1542  default:
1543  [$tempTable, $tempUid] = explode(':', (string)($softref['subst']['recordRef'] ?? ':'));
1544  if (isset($this->importMapId[$tempTable][$tempUid])) {
1545  $insertValue = BackendUtility::wsMapId($tempTable, $this->importMapId[$tempTable][$tempUid]);
1546  $tokenValue = (string)$softref['subst']['tokenValue'];
1547  if (str_contains($tokenValue, ':')) {
1548  [$tokenKey] = explode(':', $tokenValue);
1549  $insertValue = $tokenKey . ':' . $insertValue;
1550  }
1551  }
1552  }
1553  }
1554  // Finally, replace the soft reference token in tokenized content
1555  $tokenizedContent = str_replace('{softref:' . $tokenID . '}', (string)$insertValue, $tokenizedContent);
1556  }
1557  return $tokenizedContent;
1558  }
1559 
1569  protected function ‪processSoftReferencesSaveFile(string $relFileName, array $softref, string $table, string ‪$uid): string
1570  {
1571  if (isset($this->dat['header']['files'][$softref['file_ID']])) {
1572  // Initialize; Get directory prefix for file and find possible filename
1573  $dirPrefix = ‪PathUtility::dirname($relFileName) . '/';
1574  if (str_starts_with($dirPrefix, $this->‪getFileadminFolderName() . '/')) {
1575  // File in fileadmin/ folder:
1576  // Create file (and possible resources)
1577  $newFileName = $this->‪processSoftReferencesSaveFileCreateRelFile($dirPrefix, ‪PathUtility::basename($relFileName), $softref['file_ID'], $table, ‪$uid) ?: '';
1578  if (strlen($newFileName)) {
1579  $relFileName = $newFileName;
1580  } else {
1581  $this->‪addError('ERROR: No new file created for "' . $relFileName . '"');
1582  }
1583  } else {
1584  $this->‪addError('ERROR: Sorry, cannot operate on files which are outside the fileadmin folder.');
1585  }
1586  } else {
1587  $this->‪addError('ERROR: Could not find file ID in header.');
1588  }
1589  // Return (new) filename relative to public web path
1590  return $relFileName;
1591  }
1592 
1603  protected function ‪processSoftReferencesSaveFileCreateRelFile(string $origDirPrefix, string $fileName, string $fileID, string $table, string ‪$uid): ?string
1604  {
1605  // If the fileID map contains an entry for this fileID then just return the relative filename of that entry;
1606  // we don't want to write another unique filename for this one!
1607  if (isset($this->fileIdMap[$fileID])) {
1608  return ‪PathUtility::stripPathSitePrefix($this->fileIdMap[$fileID]);
1609  }
1610  // Verify FileMount access to dir-prefix. Returns the best alternative relative path if any
1611  $dirPrefix = $this->‪resolveStoragePath($origDirPrefix);
1612  if ($dirPrefix !== null && (!$this->update || $origDirPrefix === $dirPrefix) && $this->‪checkOrCreateDir($dirPrefix)) {
1613  $fileHeaderInfo = $this->dat['header']['files'][$fileID];
1614  $updMode = $this->update && $this->importMapId[$table][‪$uid] === ‪$uid && ($this->importMode[$table . ':' . ‪$uid] ?? '') !== self::IMPORT_MODE_AS_NEW;
1615  // Create new name for file:
1616  // Must have same ID in map array (just for security, is not really needed) and NOT be set "as_new".
1617 
1618  // Write main file:
1619  if ($updMode) {
1620  $newName = ‪Environment::getPublicPath() . '/' . $dirPrefix . $fileName;
1621  } else {
1622  // Create unique filename:
1624  $newName = (string)‪$fileProcObj->‪getUniqueName($fileName, ‪Environment::getPublicPath() . '/' . $dirPrefix);
1625  }
1626  if ($this->‪writeFileVerify($newName, $fileID)) {
1627  // If the resource was an HTML/CSS file with resources attached, we will write those as well!
1628  if (is_array($fileHeaderInfo['EXT_RES_ID'] ?? null)) {
1629  $tokenizedContent = $this->dat['files'][$fileID]['tokenizedContent'];
1630  $tokenSubstituted = false;
1632  if ($updMode) {
1633  foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
1634  if ($this->dat['files'][$res_fileID]['filename']) {
1635  // Resolve original filename:
1636  $relResourceFileName = $this->dat['files'][$res_fileID]['parentRelFileName'];
1637  $absResourceFileName = GeneralUtility::resolveBackPath(‪Environment::getPublicPath() . '/' . $origDirPrefix . $relResourceFileName);
1638  $absResourceFileName = GeneralUtility::getFileAbsFileName($absResourceFileName);
1639  if ($absResourceFileName && str_starts_with($absResourceFileName, ‪Environment::getPublicPath() . '/' . $this->‪getFileadminFolderName() . '/')) {
1640  $destDir = ‪PathUtility::stripPathSitePrefix(‪PathUtility::dirname($absResourceFileName) . '/');
1641  if ($this->‪resolveStoragePath($destDir, false) !== null && $this->‪checkOrCreateDir($destDir)) {
1642  $this->‪writeFileVerify($absResourceFileName, $res_fileID);
1643  } else {
1644  $this->‪addError('ERROR: Could not create file in directory "' . $destDir . '"');
1645  }
1646  } else {
1647  $this->‪addError('ERROR: Could not resolve path for "' . $relResourceFileName . '"');
1648  }
1649  $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent);
1650  $tokenSubstituted = true;
1651  }
1652  }
1653  } else {
1654  // Create the ressource's directory name (filename without extension, suffixed "_FILES")
1655  $resourceDir = ‪PathUtility::dirname($newName) . '/' . preg_replace('/\\.[^.]*$/', '', ‪PathUtility::basename($newName)) . '_FILES';
1656  if (‪GeneralUtility::mkdir($resourceDir)) {
1657  foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
1658  if ($this->dat['files'][$res_fileID]['filename']) {
1659  $absResourceFileName = (string)‪$fileProcObj->‪getUniqueName($this->dat['files'][$res_fileID]['filename'], $resourceDir);
1660  $relResourceFileName = substr($absResourceFileName, strlen(‪PathUtility::dirname($resourceDir)) + 1);
1661  $this->‪writeFileVerify($absResourceFileName, $res_fileID);
1662  $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent);
1663  $tokenSubstituted = true;
1664  }
1665  }
1666  }
1667  }
1668  // If substitutions has been made, write the content to the file again:
1669  if ($tokenSubstituted) {
1670  ‪GeneralUtility::writeFile($newName, $tokenizedContent);
1671  }
1672  }
1673  return ‪PathUtility::stripPathSitePrefix($newName);
1674  }
1675  }
1676  return null;
1677  }
1678 
1686  protected function ‪writeFileVerify(string $fileName, string $fileID): bool
1687  {
1689  if (!‪$fileProcObj->actionPerms['addFile']) {
1690  $this->‪addError('ERROR: You did not have sufficient permissions to write the file "' . $fileName . '"');
1691  return false;
1692  }
1693  // Just for security, check again. Should actually not be necessary.
1694  try {
1695  GeneralUtility::makeInstance(ResourceFactory::class)->getFolderObjectFromCombinedIdentifier(‪PathUtility::dirname($fileName));
1697  $this->‪addError('ERROR: Filename "' . $fileName . '" was not allowed in destination path!');
1698  return false;
1699  }
1700  $pathInfo = GeneralUtility::split_fileref($fileName);
1701  if (!GeneralUtility::makeInstance(FileNameValidator::class)->isValid($pathInfo['file'])) {
1702  $this->‪addError('ERROR: Filename "' . $fileName . '" failed against extension check or deny-pattern!');
1703  return false;
1704  }
1705  if (!GeneralUtility::getFileAbsFileName($fileName)) {
1706  $this->‪addError('ERROR: Filename "' . $fileName . '" was not a valid relative file path!');
1707  return false;
1708  }
1709  if (!$this->dat['files'][$fileID]) {
1710  $this->‪addError('ERROR: File ID "' . $fileID . '" could not be found');
1711  return false;
1712  }
1713  ‪GeneralUtility::writeFile($fileName, $this->dat['files'][$fileID]['content']);
1714  $this->fileIdMap[$fileID] = $fileName;
1715  if (hash_equals(md5((string)file_get_contents($fileName)), $this->dat['files'][$fileID]['content_md5'])) {
1716  return true;
1717  }
1718  $this->‪addError('ERROR: File content "' . $fileName . '" was corrupted');
1719  return false;
1720  }
1721 
1728  protected function ‪checkOrCreateDir(string $dirPrefix): bool
1729  {
1730  // Split dir path and remove first directory (which should be "fileadmin")
1731  $filePathParts = explode('/', $dirPrefix);
1732  $firstDir = array_shift($filePathParts);
1733  if ($firstDir === $this->‪getFileadminFolderName() && GeneralUtility::getFileAbsFileName($dirPrefix)) {
1734  $pathAcc = '';
1735  foreach ($filePathParts as $dirname) {
1736  $pathAcc .= '/' . $dirname;
1737  if (strlen($dirname)) {
1738  if (!@is_dir(‪Environment::getPublicPath() . '/' . $this->‪getFileadminFolderName() . $pathAcc)) {
1740  $this->‪addError('ERROR: Directory could not be created....B');
1741  return false;
1742  }
1743  }
1744  } elseif ($dirPrefix === $this->‪getFileadminFolderName() . $pathAcc) {
1745  return true;
1746  } else {
1747  $this->‪addError('ERROR: Directory could not be created....A');
1748  }
1749  }
1750  }
1751  return false;
1752  }
1753 
1754  protected function ‪getFileadminFolderName(): string
1755  {
1756  if (empty($this->fileadminFolderName)) {
1757  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'])) {
1758  $this->fileadminFolderName = rtrim(‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/');
1759  } else {
1760  $this->fileadminFolderName = 'fileadmin';
1761  }
1762  }
1764  }
1765 
1772  protected function ‪callHook(string $name, array $params): void
1773  {
1774  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name] ?? [] as $hook) {
1775  GeneralUtility::callUserFunction($hook, $params, $this);
1776  }
1777  }
1778 
1779  public function ‪isEnableLogging(): bool
1780  {
1781  return ‪$this->enableLogging;
1782  }
1783 
1784  public function ‪setEnableLogging(bool ‪$enableLogging): void
1785  {
1786  $this->enableLogging = ‪$enableLogging;
1787  }
1788 
1789  public function ‪isDecompressionAvailable(): bool
1790  {
1792  }
1793 }
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:94
‪TYPO3\CMS\Impexp\Import\$enableLogging
‪bool $enableLogging
Definition: Import.php:72
‪TYPO3\CMS\Impexp\Import\addSingle
‪addSingle(array &$importData, string $table, int $uid, $pid)
Definition: Import.php:967
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir
‪static bool mkdir(string $newFolder)
Definition: GeneralUtility.php:1633
‪TYPO3\CMS\Core\Utility\PathUtility\stripPathSitePrefix
‪static stripPathSitePrefix(string $path)
Definition: PathUtility.php:428
‪TYPO3\CMS\Impexp\Import\processSoftReferencesSaveFile
‪string processSoftReferencesSaveFile(string $relFileName, array $softref, string $table, string $uid)
Definition: Import.php:1569
‪TYPO3\CMS\Impexp\Import\getFileadminFolderName
‪getFileadminFolderName()
Definition: Import.php:1754
‪TYPO3\CMS\Impexp\Import\setEnableLogging
‪setEnableLogging(bool $enableLogging)
Definition: Import.php:1784
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Impexp\ImportExport\addGeneralErrorsByTable
‪addGeneralErrorsByTable(string $table)
Definition: ImportExport.php:358
‪TYPO3\CMS\Impexp\ImportExport\addError
‪addError(string $message)
Definition: ImportExport.php:1314
‪TYPO3\CMS\Impexp\Import\IMPORT_MODE_AS_NEW
‪const IMPORT_MODE_AS_NEW
Definition: Import.php:53
‪TYPO3\CMS\Impexp\Import\writePages
‪writePages()
Definition: Import.php:734
‪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:1518
‪TYPO3\CMS\Impexp\Import\$fileadminFolderName
‪string $fileadminFolderName
Definition: Import.php:116
‪TYPO3\CMS\Impexp\Import\isEnableLogging
‪isEnableLogging()
Definition: Import.php:1779
‪TYPO3\CMS\Core\Resource\ResourceStorage\getDriverType
‪string getDriverType()
Definition: ResourceStorage.php:2897
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Impexp\Import\doRespectPid
‪bool doRespectPid(string $table, int $uid)
Definition: Import.php:834
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator
Definition: FileNameValidator.php:25
‪TYPO3\CMS\Impexp\Import\$mode
‪string $mode
Definition: Import.php:61
‪TYPO3\CMS\Core\Serializer\Typo3XmlSerializerOptions
Definition: Typo3XmlSerializerOptions.php:24
‪TYPO3\CMS\Impexp\Exception\PrerequisitesNotMetException
Definition: PrerequisitesNotMetException.php:27
‪TYPO3\CMS\Impexp\Import\setRelations
‪setRelations()
Definition: Import.php:1184
‪TYPO3\CMS\Impexp\Import\loadInit
‪loadInit()
Definition: Import.php:305
‪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:1372
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static getPublicPath()
Definition: Environment.php:187
‪TYPO3\CMS\Impexp\ImportExport\isTableStatic
‪bool isTableStatic(string $table)
Definition: ImportExport.php:1138
‪TYPO3\CMS\Impexp\Import\$defaultStorage
‪ResourceStorage $defaultStorage
Definition: Import.php:110
‪TYPO3\CMS\Core\Exception
‪TYPO3\CMS\Core\Serializer\Typo3XmlSerializerOptions\RETURN_ROOT_NODE_NAME
‪const RETURN_ROOT_NODE_NAME
Definition: Typo3XmlSerializerOptions.php:30
‪TYPO3\CMS\Impexp\Import\getNextFilePart
‪array null getNextFilePart($fd, string $name)
Definition: Import.php:262
‪TYPO3\CMS\Impexp\Exception\LoadingFileFailedException
Definition: LoadingFileFailedException.php:27
‪TYPO3\CMS\Impexp\Import\writeRecords
‪writeRecords()
Definition: Import.php:845
‪TYPO3\CMS\Impexp\Import\$importNewId
‪array $importNewId
Definition: Import.php:79
‪TYPO3\CMS\Impexp\Import\__construct
‪__construct()
Definition: Import.php:118
‪TYPO3\CMS\Impexp\Import\processSoftReferencesSaveFileCreateRelFile
‪string null processSoftReferencesSaveFileCreateRelFile(string $origDirPrefix, string $fileName, string $fileID, string $table, string $uid)
Definition: Import.php:1603
‪TYPO3\CMS\Impexp\Import\getSysFileMetaDataFromDatabase
‪getSysFileMetaDataFromDatabase(int $file, int $sysLanguageUid)
Definition: Import.php:1106
‪TYPO3\CMS\Core\Serializer\Typo3XmlParser
Definition: Typo3XmlParser.php:38
‪TYPO3\CMS\Impexp\Import\$storageRepository
‪StorageRepository $storageRepository
Definition: Import.php:111
‪TYPO3\CMS\Impexp\Import\IMPORT_MODE_FORCE_UID
‪const IMPORT_MODE_FORCE_UID
Definition: Import.php:52
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static basename(string $path)
Definition: PathUtility.php:219
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static isLoaded(string $key)
Definition: ExtensionManagementUtility.php:55
‪TYPO3\CMS\Impexp\Import\writePagesOrder
‪writePagesOrder()
Definition: Import.php:786
‪TYPO3\CMS\Impexp\Import\addToMapId
‪addToMapId(array $importData, array $substNEWwithIDs)
Definition: Import.php:1136
‪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:54
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:32
‪TYPO3\CMS\Impexp\Import\isDecompressionAvailable
‪isDecompressionAvailable()
Definition: Import.php:1789
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Impexp\ImportExport\getRecordFromDatabase
‪array null getRecordFromDatabase(string $table, int $uid, string $fields='uid, pid')
Definition: ImportExport.php:1180
‪TYPO3\CMS\Impexp\Import\createDataHandler
‪createDataHandler()
Definition: Import.php:1164
‪TYPO3\CMS\Impexp\Import\writeSysFileStorageRecords
‪writeSysFileStorageRecords()
Definition: Import.php:441
‪TYPO3\CMS\Impexp\Import\$suggestedInsertUids
‪array $suggestedInsertUids
Definition: Import.php:67
‪TYPO3\CMS\Core\Utility\PathUtility\dirname
‪static dirname(string $path)
Definition: PathUtility.php:243
‪TYPO3\CMS\Core\Resource\ResourceStorage\isOnline
‪bool isOnline()
Definition: ResourceStorage.php:464
‪TYPO3\CMS\Impexp\ImportExport\getOrCreateTemporaryFolderName
‪getOrCreateTemporaryFolderName()
Definition: ImportExport.php:990
‪TYPO3\CMS\Impexp\Import\removeSysFileReferenceRecordsWithRelationToMissingFile
‪removeSysFileReferenceRecordsWithRelationToMissingFile()
Definition: Import.php:706
‪TYPO3\CMS\Impexp\Import\remapRelationsOfField
‪array remapRelationsOfField(array $fieldRelations, array $fieldConfig, string $field='')
Definition: Import.php:1240
‪TYPO3\CMS\Impexp\Import\processSoftReferencesFlexFormCallBack
‪array processSoftReferencesFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path)
Definition: Import.php:1487
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFile
‪static bool writeFile(string $file, string $content, bool $changePermissions=false)
Definition: GeneralUtility.php:1464
‪TYPO3\CMS\Impexp\Import
Definition: Import.php:51
‪TYPO3\CMS\Impexp\ImportExport\$fileProcObj
‪ExtendedFileUtility $fileProcObj
Definition: ImportExport.php:159
‪TYPO3\CMS\Impexp\Import\$storagesAvailableForImport
‪array $storagesAvailableForImport
Definition: Import.php:105
‪TYPO3\CMS\Impexp\Import\writeSysFileRecords
‪writeSysFileRecords()
Definition: Import.php:530
‪TYPO3\CMS\Impexp\Import\writeRecordsOrder
‪writeRecordsOrder()
Definition: Import.php:906
‪TYPO3\CMS\Impexp\Import\getReferenceDefaultValue
‪getReferenceDefaultValue(array $configuration)
Definition: Import.php:1092
‪TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools
Definition: FlexFormTools.php:40
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:42
‪TYPO3\CMS\Impexp\Import\callHook
‪callHook(string $name, array $params)
Definition: Import.php:1772
‪TYPO3\CMS\Core\Resource\StorageRepository
Definition: StorageRepository.php:38
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:26
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Impexp\Import\initializeImport
‪initializeImport()
Definition: Import.php:430
‪TYPO3\CMS\Core\Resource\ResourceStorage\getConfiguration
‪array getConfiguration()
Definition: ResourceStorage.php:278
‪TYPO3\CMS\Impexp\Import\IMPORT_MODE_RESPECT_PID
‪const IMPORT_MODE_RESPECT_PID
Definition: Import.php:56
‪TYPO3\CMS\Impexp\Import\fetchStorages
‪fetchStorages()
Definition: Import.php:132
‪TYPO3\CMS\Impexp\Import\IMPORT_MODE_IGNORE_PID
‪const IMPORT_MODE_IGNORE_PID
Definition: Import.php:55
‪TYPO3\CMS\Impexp\ImportExport
Definition: ImportExport.php:46
‪TYPO3\CMS\Impexp\Import\SOFTREF_IMPORT_MODE_EDITABLE
‪const SOFTREF_IMPORT_MODE_EDITABLE
Definition: Import.php:59
‪TYPO3\CMS\Core\Utility\File\BasicFileUtility\getUniqueName
‪string null getUniqueName($theFile, $theDest, $dontCheckForUnique=false)
Definition: BasicFileUtility.php:86
‪TYPO3\CMS\Impexp\Import\SOFTREF_IMPORT_MODE_EXCLUDE
‪const SOFTREF_IMPORT_MODE_EXCLUDE
Definition: Import.php:58
‪TYPO3\CMS\Impexp\Import\fixUidLocalInSysFileReferenceRecords
‪fixUidLocalInSysFileReferenceRecords(int $oldFileUid, int $newFileUid)
Definition: Import.php:686
‪TYPO3\CMS\Impexp\Exception\ImportFailedException
Definition: ImportFailedException.php:27
‪TYPO3\CMS\Impexp\Import\checkOrCreateDir
‪bool checkOrCreateDir(string $dirPrefix)
Definition: Import.php:1728
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Impexp\ImportExport\flatInversePageTree
‪flatInversePageTree(array $pageTree, array &$list, int $pid=-1)
Definition: ImportExport.php:1120
‪TYPO3\CMS\Core\Resource\ResourceStorage
Definition: ResourceStorage.php:128
‪TYPO3\CMS\Impexp\ImportExport\hasErrors
‪hasErrors()
Definition: ImportExport.php:1319
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Impexp\Import\$storages
‪array $storages
Definition: Import.php:98
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Impexp\ImportExport\resolveStoragePath
‪string null resolveStoragePath(string $dirPrefix, bool $checkAlternatives=true)
Definition: ImportExport.php:1093
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Impexp\Import\$isFilesSavedOutsideImportFile
‪bool $isFilesSavedOutsideImportFile
Definition: Import.php:91
‪TYPO3\CMS\Impexp\Import\writeFileVerify
‪bool writeFileVerify(string $fileName, string $fileID)
Definition: Import.php:1686
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Impexp\Import\$importNewIdPids
‪array $importNewIdPids
Definition: Import.php:84
‪TYPO3\CMS\Impexp\ImportExport\getFileProcObj
‪ExtendedFileUtility getFileProcObj()
Definition: ImportExport.php:1296
‪TYPO3\CMS\Impexp\Import\setFlexFormRelations
‪setFlexFormRelations()
Definition: Import.php:1282
‪TYPO3\CMS\Core\Resource\ResourceStorage\isWritable
‪bool isWritable()
Definition: ResourceStorage.php:403
‪TYPO3\CMS\Impexp\ImportExport\$dat
‪array $dat
Definition: ImportExport.php:154
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Core\Serializer\Typo3XmlSerializerOptions\LOAD_OPTIONS
‪const LOAD_OPTIONS
Definition: Typo3XmlSerializerOptions.php:27
‪TYPO3\CMS\Impexp\ImportExport\removeTemporaryFolderName
‪removeTemporaryFolderName()
Definition: ImportExport.php:1013
‪TYPO3\CMS\Impexp\ImportExport\isRecordExcluded
‪bool isRecordExcluded(string $table, int $uid)
Definition: ImportExport.php:1155
‪TYPO3\CMS\Impexp
‪TYPO3\CMS\Impexp\Import\processSoftReferences
‪processSoftReferences()
Definition: Import.php:1393
‪TYPO3\CMS\Impexp\Import\getMetaData
‪getMetaData()
Definition: Import.php:312
‪TYPO3\CMS\Impexp\Import\importData
‪importData()
Definition: Import.php:397
‪TYPO3\CMS\Impexp\Import\isEquivalentStorage
‪bool isEquivalentStorage(ResourceStorage $storageObject, array &$storageRecord)
Definition: Import.php:509
‪TYPO3\CMS\Impexp\ImportExport\getBackendUser
‪getBackendUser()
Definition: ImportExport.php:1324
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57
‪TYPO3\CMS\Impexp\Import\loadFile
‪loadFile(string $fileName)
Definition: Import.php:162
‪TYPO3\CMS\Impexp\Import\$decompressionAvailable
‪bool $decompressionAvailable
Definition: Import.php:86
‪TYPO3\CMS\Impexp\Import\checkImportPrerequisites
‪checkImportPrerequisites()
Definition: Import.php:326
‪TYPO3\CMS\Impexp\Import\$supportedFileExtensions
‪array $supportedFileExtensions
Definition: Import.php:87