‪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;
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 
224  public function ‪getSupportedFileExtensions(): array
225  {
226  if (empty($this->supportedFileExtensions)) {
228  ‪$supportedFileExtensions[] = 'xml';
229  ‪$supportedFileExtensions[] = 't3d';
230  $this->supportedFileExtensions = ‪$supportedFileExtensions;
231  }
233  }
234 
247  protected function ‪getNextFilePart($fd, bool $unserialize = false, string $name = '')
248  {
249  $headerLength = 32 + 1 + 1 + 1 + 10 + 1;
250  $headerString = fread($fd, $headerLength);
251  if (empty($headerString)) {
252  $this->‪addError('File does not contain data for "' . $name . '"');
253  return null;
254  }
255 
256  $header = explode(':', $headerString);
257  if (str_contains($header[0], 'Warning')) {
258  $this->‪addError('File read error: Warning message in file. (' . $headerString . fgets($fd) . ')');
259  return null;
260  }
261  if ((string)$header[3] !== '') {
262  $this->‪addError('File read error: InitString had a wrong length. (' . $name . ')');
263  return null;
264  }
265 
266  $dataString = (string)fread($fd, (int)$header[2]);
267  $isDataCompressed = $header[1] === '1';
268  fread($fd, 1);
269  if (!hash_equals($header[0], md5($dataString))) {
270  $this->‪addError('MD5 check failed (' . $name . ')');
271  return null;
272  }
273 
274  if ($isDataCompressed) {
275  if ($this->decompressionAvailable) {
276  $dataString = (string)gzuncompress($dataString);
277  } else {
278  $this->‪addError('Content read error: This file requires decompression, ' .
279  'but this server does not offer gzcompress()/gzuncompress() functions.');
280  return null;
281  }
282  }
283 
284  return $unserialize ? unserialize($dataString, ['allowed_classes' => false]) : $dataString;
285  }
286 
290  protected function ‪loadInit(): void
291  {
292  $this->relStaticTables = (array)($this->dat['header']['relStaticTables'] ?? []);
293  $this->excludeMap = (array)($this->dat['header']['excludeMap'] ?? []);
294  $this->softrefCfg = (array)($this->dat['header']['softrefCfg'] ?? []);
295  }
296 
297  public function ‪getMetaData(): array
298  {
299  return $this->dat['header']['meta'] ?? [];
300  }
301 
302  /***********************
303  * Import
304  ***********************/
305 
311  public function ‪checkImportPrerequisites(): void
312  {
313  // Check #1: Extension dependencies
314  $extKeysToInstall = [];
315  if (isset($this->dat['header']['extensionDependencies'])) {
316  foreach ($this->dat['header']['extensionDependencies'] as $extKey) {
317  if (!empty($extKey) && !‪ExtensionManagementUtility::isLoaded($extKey)) {
318  $extKeysToInstall[] = $extKey;
319  }
320  }
321  }
322  if (!empty($extKeysToInstall)) {
323  $this->‪addError(
324  sprintf(
325  'Before you can import this file you need to install the extensions "%s".',
326  implode('", "', $extKeysToInstall)
327  )
328  );
329  }
330 
331  // Check #2: Presence of imported storage paths
332  if (!empty($this->dat['header']['records']['sys_file_storage'])) {
333  foreach ($this->dat['header']['records']['sys_file_storage'] as $sysFileStorageUid => $_) {
334  $storageRecord = &$this->dat['records']['sys_file_storage:' . $sysFileStorageUid]['data'];
335  if ($storageRecord['driver'] === 'Local'
336  && $storageRecord['is_writable']
337  && $storageRecord['is_online']
338  ) {
339  $storageMapUid = -1;
340  foreach ($this->storages as $storage) {
341  if ($this->‪isEquivalentStorage($storage, $storageRecord)) {
342  $storageMapUid = $storage->getUid();
343  break;
344  }
345  }
346  // The storage from the import does not have an equivalent storage
347  // in the current instance (same driver, same path, etc.). Before
348  // the storage record can get inserted later on take care the path
349  // it points to really exists and is accessible.
350  if ($storageMapUid === -1) {
351  // Unset the storage record UID when trying to create the storage object
352  // as the record does not already exist in database. The constructor of the
353  // storage object will check whether the target folder exists and set the
354  // isOnline flag depending on the outcome.
355  $storageRecordWithUid0 = $storageRecord;
356  $storageRecordWithUid0['uid'] = 0;
357  $storageObject = $this->‪getStorageRepository()->createFromRecord($storageRecordWithUid0);
358  if (!$storageObject->isOnline()) {
359  $configuration = $storageObject->getConfiguration();
360  $this->‪addError(
361  sprintf(
362  'The file storage "%s" does not exist. ' .
363  'Please create the directory prior to starting the import!',
364  $storageObject->getName() . $configuration['basePath']
365  )
366  );
367  }
368  }
369  }
370  }
371  }
372 
373  if ($this->‪hasErrors()) {
375  'Prerequisites for file import are not met.',
376  1484484612
377  );
378  }
379  }
380 
386  public function ‪importData(): void
387  {
388  $this->‪initializeImport();
389 
390  // Write sys_file_storages first
392  // Write sys_file records and write the binary file data
393  $this->‪writeSysFileRecords();
394  // Write records, first pages, then the rest
395  // Fields with "hard" relations to database, files and flexform fields are kept empty during this run
396  $this->‪writePages();
397  $this->‪writeRecords();
398  // Finally all the file and database record references must be fixed. This is done after all records have supposedly
399  // been written to database. $this->importMapId will indicate two things:
400  // 1) that a record WAS written to db and
401  // 2) that it has got a new id-number.
402  $this->‪setRelations();
403  // And when all database relations are in place, we can fix file and database relations in flexform fields
404  // - since data structures often depend on relations to a DS record:
405  $this->‪setFlexFormRelations();
406  // Finally, traverse all records and process soft references with substitution attributes.
407  $this->‪processSoftReferences();
408  // Cleanup
410 
411  if ($this->‪hasErrors()) {
412  throw new ‪ImportFailedException('The import has failed.', 1484484613);
413  }
414  }
415 
419  protected function ‪initializeImport(): void
420  {
421  $this->doesImport = true;
422  $this->importMapId = [];
423  $this->importNewId = [];
424  $this->importNewIdPids = [];
425  }
426 
430  protected function ‪writeSysFileStorageRecords(): void
431  {
432  if (!isset($this->dat['header']['records']['sys_file_storage'])) {
433  return;
434  }
435 
436  $importData = [];
437 
438  $storageUidsToBeResetToDefaultStorage = [];
439  foreach ($this->dat['header']['records']['sys_file_storage'] as $sysFileStorageUid => $_) {
440  $storageRecord = &$this->dat['records']['sys_file_storage:' . $sysFileStorageUid]['data'];
441  if ($storageRecord['driver'] === 'Local'
442  && $storageRecord['is_writable']
443  && $storageRecord['is_online']
444  ) {
445  foreach ($this->storages as $storage) {
446  if ($this->‪isEquivalentStorage($storage, $storageRecord)) {
447  $this->importMapId['sys_file_storage'][$sysFileStorageUid] = $storage->getUid();
448  break;
449  }
450  }
451 
452  if (!isset($this->importMapId['sys_file_storage'][$sysFileStorageUid])) {
453  // Local, writable and online storage. May be used later for writing files.
454  // Does not currently exist, mark the storage for import.
455  $this->‪addSingle($importData, 'sys_file_storage', $sysFileStorageUid, 0);
456  }
457  } else {
458  // Storage with non-local drivers can be imported, but must not be used to save files as you cannot
459  // be sure that this is supported. In this case the default storage is used. Non-writable and
460  // non-online storage may be created as duplicates because you were unable to check the detailed
461  // configuration options at that time.
462  $this->‪addSingle($importData, 'sys_file_storage', $sysFileStorageUid, 0);
463  $storageUidsToBeResetToDefaultStorage[] = $sysFileStorageUid;
464  }
465  }
466 
467  // Write new storages to the database
468  $dataHandler = $this->‪createDataHandler();
469  // Because all records are submitted in the correct order with positive pid numbers,
470  // we should internally reverse the order of submission.
471  $dataHandler->reverseOrder = true;
472  $dataHandler->isImporting = true;
473  $dataHandler->start($importData, []);
474  $dataHandler->process_datamap();
475  $this->‪addToMapId($importData, $dataHandler->substNEWwithIDs);
476 
477  // Refresh internal storage representation after potential storage import
478  $this->‪fetchStorages();
479 
480  // Map references of non-local / non-writable / non-online storages to the default storage
481  $defaultStorageUid = $this->defaultStorage !== null ? $this->defaultStorage->getUid() : null;
482  foreach ($storageUidsToBeResetToDefaultStorage as $storageUidToBeResetToDefaultStorage) {
483  $this->importMapId['sys_file_storage'][$storageUidToBeResetToDefaultStorage] = $defaultStorageUid;
484  }
485 
486  // Unset the sys_file_storage records to prevent an import in writeRecords()
487  unset($this->dat['header']['records']['sys_file_storage']);
488  }
489 
498  protected function ‪isEquivalentStorage(ResourceStorage &$storageObject, array &$storageRecord): bool
499  {
500  if ($storageObject->getDriverType() === $storageRecord['driver']
501  && (bool)$storageObject->isWritable() === (bool)$storageRecord['is_writable']
502  && (bool)$storageObject->isOnline() === (bool)$storageRecord['is_online']
503  ) {
504  $storageRecordConfiguration = GeneralUtility::makeInstance(FlexFormService::class)
505  ->convertFlexFormContentToArray($storageRecord['configuration'] ?? '');
506  $storageObjectConfiguration = $storageObject->getConfiguration();
507  if ($storageRecordConfiguration['pathType'] === $storageObjectConfiguration['pathType']
508  && $storageRecordConfiguration['basePath'] === $storageObjectConfiguration['basePath']
509  ) {
510  return true;
511  }
512  }
513  return false;
514  }
515 
519  protected function ‪writeSysFileRecords(): void
520  {
521  if (!isset($this->dat['header']['records']['sys_file'])) {
522  return;
523  }
524 
525  $this->‪addGeneralErrorsByTable('sys_file');
526 
527  $temporaryFolder = $this->‪getOrCreateTemporaryFolderName();
528  $sanitizedFolderMappings = [];
529 
530  foreach ($this->dat['header']['records']['sys_file'] as $sysFileUid => $_) {
531  $fileRecord = &$this->dat['records']['sys_file:' . $sysFileUid]['data'];
532 
533  $temporaryFile = null;
534  $temporaryFilePath = $temporaryFolder . '/' . $fileRecord['sha1'];
535 
536  if ($this->isFilesSavedOutsideImportFile) {
537  if (is_file($temporaryFilePath) && sha1_file($temporaryFilePath) === $fileRecord['sha1']) {
538  $temporaryFile = $temporaryFilePath;
539  } else {
540  $this->‪addError(sprintf(
541  'Error: Temporary file %s could not be found or does not match the checksum!',
542  $temporaryFilePath
543  ));
544  continue;
545  }
546  } else {
547  $fileId = md5($fileRecord['storage'] . ':' . $fileRecord['identifier_hash']);
548  if (isset($this->dat['files_fal'][$fileId]['content'])) {
549  $fileInfo = &$this->dat['files_fal'][$fileId];
550  if (‪GeneralUtility::writeFile($temporaryFilePath, $fileInfo['content'])) {
551  clearstatcache();
552  $temporaryFile = $temporaryFilePath;
553  } else {
554  $this->‪addError(sprintf(
555  'Error: Temporary file %s was not written as it should have been!',
556  $temporaryFilePath
557  ));
558  continue;
559  }
560  } else {
561  $this->‪addError(sprintf('Error: No file found for ID %s', $fileId));
562  continue;
563  }
564  }
565 
566  $storageUid = $this->importMapId['sys_file_storage'][$fileRecord['storage']] ?? $fileRecord['storage'];
567  if (isset($this->storagesAvailableForImport[$storageUid])) {
568  $storage = $this->storagesAvailableForImport[$storageUid];
569  } elseif ($storageUid === 0 || $storageUid === '0') {
570  $storage = $this->‪getStorageRepository()->‪findByUid(0);
571  } elseif ($this->defaultStorage !== null) {
572  $storage = ‪$this->defaultStorage;
573  } else {
574  $this->‪addError(sprintf(
575  'Error: No storage available for the file "%s" with storage uid "%s"',
576  $fileRecord['identifier'],
577  $fileRecord['storage']
578  ));
579  continue;
580  }
581 
583  $file = null;
584  try {
585  if ($storage->hasFile($fileRecord['identifier'])) {
586  $file = $storage->getFile($fileRecord['identifier']);
587  if ($file->getSha1() !== $fileRecord['sha1']) {
588  $file = null;
589  }
590  }
591  } catch (Exception $e) {
592  // @todo: Can this exception be thrown anywhere?
593  $file = null;
594  }
595 
596  if ($file === null) {
597  $folderName = ‪PathUtility::dirname(ltrim($fileRecord['identifier'], '/'));
598  if (in_array($folderName, $sanitizedFolderMappings, true)) {
599  $folderName = $sanitizedFolderMappings[$folderName];
600  }
601  if (!$storage->hasFolder($folderName)) {
602  try {
603  $importFolder = $storage->createFolder($folderName);
604  if ($importFolder->getIdentifier() !== $folderName && !in_array($folderName, $sanitizedFolderMappings, true)) {
605  $sanitizedFolderMappings[$folderName] = $importFolder->getIdentifier();
606  }
607  } catch (Exception $e) {
608  $this->‪addError(sprintf(
609  'Error: Folder "%s" could not be created for file "%s" with storage uid "%s"',
610  $folderName,
611  $fileRecord['identifier'],
612  $fileRecord['storage']
613  ));
614  continue;
615  }
616  } else {
617  $importFolder = $storage->getFolder($folderName);
618  }
619 
620  $this->‪callHook('before_addSysFileRecord', [
621  'fileRecord' => $fileRecord,
622  'importFolder' => $importFolder,
623  'temporaryFile' => $temporaryFile,
624  ]);
625 
626  try {
627  $file = $storage->addFile($temporaryFile, $importFolder, $fileRecord['name']);
628  } catch (Exception $e) {
629  $this->‪addError(sprintf(
630  'Error: File could not be added to the storage: "%s" with storage uid "%s"',
631  $fileRecord['identifier'],
632  $fileRecord['storage']
633  ));
634  continue;
635  }
636 
637  if ($file->getSha1() !== $fileRecord['sha1']) {
638  $this->‪addError(sprintf(
639  'Error: The hash of the written file is not identical to the import data! ' .
640  'File could be corrupted! File: "%s" with storage uid "%s"',
641  $fileRecord['identifier'],
642  $fileRecord['storage']
643  ));
644  }
645  }
646 
647  // save the new uid in the import id map
648  $this->importMapId['sys_file'][$fileRecord['uid']] = $file->getUid();
649  $this->‪fixUidLocalInSysFileReferenceRecords((int)$fileRecord['uid'], $file->getUid());
650  }
651 
652  // unset the sys_file records to prevent an import in writeRecords()
653  unset($this->dat['header']['records']['sys_file']);
654  // remove all sys_file_reference records that point to file records which are unknown
655  // in the system to prevent exceptions
657  }
658 
674  protected function ‪fixUidLocalInSysFileReferenceRecords(int $oldFileUid, int $newFileUid): void
675  {
676  if (!isset($this->dat['header']['records']['sys_file_reference'])) {
677  return;
678  }
679 
680  foreach ($this->dat['header']['records']['sys_file_reference'] as $sysFileReferenceUid => $_) {
681  if (!isset($this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['hasBeenMapped'])
682  && $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data']['uid_local'] == $oldFileUid
683  ) {
684  $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['hasBeenMapped'] = true;
685  $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data']['uid_local'] = $newFileUid;
686  }
687  }
688  }
689 
695  {
696  if (!isset($this->dat['header']['records']['sys_file_reference'])) {
697  return;
698  }
699 
700  foreach ($this->dat['header']['records']['sys_file_reference'] as $sysFileReferenceUid => $_) {
701  $fileReferenceRecord = &$this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data'];
702  if (!in_array($fileReferenceRecord['uid_local'], (array)($this->importMapId['sys_file'] ?? []))) {
703  unset($this->dat['header']['records']['sys_file_reference'][$sysFileReferenceUid]);
704  unset($this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]);
705  $this->‪addError(sprintf(
706  'Error: sys_file_reference record "%s" with relation to sys_file record "%s"'
707  . ', which is not part of the import data, was not imported.',
708  $sysFileReferenceUid,
709  $fileReferenceRecord['uid_local']
710  ));
711  }
712  }
713  }
714 
722  protected function ‪writePages(): void
723  {
724  if (!isset($this->dat['header']['records']['pages'])) {
725  return;
726  }
727 
728  $importData = [];
729 
730  // Add page tree
731  $remainingPages = $this->dat['header']['records']['pages'];
732  if (is_array($this->dat['header']['pagetree'] ?? null)) {
733  $pageList = [];
734  $this->‪flatInversePageTree($this->dat['header']['pagetree'], $pageList);
735  foreach ($pageList as $pageUid => $_) {
736  ‪$pid = $this->dat['header']['records']['pages'][$pageUid]['pid'] ?? null;
737  ‪$pid = $this->importNewIdPids[‪$pid] ?? ‪$this->pid;
738  $this->‪addSingle($importData, 'pages', (int)$pageUid, ‪$pid);
739  unset($remainingPages[$pageUid]);
740  }
741  }
742 
743  // Add remaining pages on root level
744  if (!empty($remainingPages)) {
745  foreach ($remainingPages as $pageUid => $_) {
746  $this->‪addSingle($importData, 'pages', (int)$pageUid, $this->pid);
747  }
748  }
749 
750  // Write pages to the database
751  $dataHandler = $this->‪createDataHandler();
752  $dataHandler->isImporting = true;
753  $this->‪callHook('before_writeRecordsPages', [
754  'tce' => &$dataHandler,
755  'data' => &$importData,
756  ]);
757  $dataHandler->suggestedInsertUids = ‪$this->suggestedInsertUids;
758  $dataHandler->start($importData, []);
759  $dataHandler->process_datamap();
760  $this->‪callHook('after_writeRecordsPages', [
761  'tce' => &$dataHandler,
762  ]);
763  $this->‪addToMapId($importData, $dataHandler->substNEWwithIDs);
764 
765  // Sort pages
766  $this->‪writePagesOrder();
767  }
768 
776  protected function ‪writePagesOrder(): void
777  {
778  if (!$this->update || !is_array($this->dat['header']['pagetree'] ?? null)) {
779  return;
780  }
781 
782  $importCmd = [];
783 
784  // Get uid-pid relations and traverse them in order to map to possible new IDs
785  $pageList = [];
786  $this->‪flatInversePageTree($this->dat['header']['pagetree'], $pageList);
787  foreach ($pageList as $pageUid => $pagePid) {
788  if ($pagePid >= 0 && $this->‪doRespectPid('pages', $pageUid)) {
789  // If the page has been assigned a new ID (because it was created), use that instead!
790  if (!‪MathUtility::canBeInterpretedAsInteger($this->importNewIdPids[$pageUid])) {
791  if ($this->importMapId['pages'][$pageUid]) {
792  $mappedUid = $this->importMapId['pages'][$pageUid];
793  $importCmd['pages'][$mappedUid]['move'] = $pagePid;
794  }
795  } else {
796  $importCmd['pages'][$pageUid]['move'] = $pagePid;
797  }
798  }
799  }
800 
801  // Move pages in the database
802  if (!empty($importCmd)) {
803  $dataHandler = $this->‪createDataHandler();
804  $this->‪callHook('before_writeRecordsPagesOrder', [
805  'tce' => &$dataHandler,
806  'data' => &$importCmd,
807  ]);
808  $dataHandler->start([], $importCmd);
809  $dataHandler->process_cmdmap();
810  $this->‪callHook('after_writeRecordsPagesOrder', [
811  'tce' => &$dataHandler,
812  ]);
813  }
814  }
815 
824  protected function ‪doRespectPid(string $table, int ‪$uid): bool
825  {
826  return ($this->importMode[$table . ':' . ‪$uid] ?? '') !== self::IMPORT_MODE_IGNORE_PID &&
827  (!$this->globalIgnorePid || ($this->importMode[$table . ':' . ‪$uid] ?? '') === self::IMPORT_MODE_RESPECT_PID);
828  }
829 
835  protected function ‪writeRecords(): void
836  {
837  $importData = [];
838 
839  // Write the rest of the records
840  if (is_array($this->dat['header']['records'] ?? null)) {
841  foreach ($this->dat['header']['records'] as $table => $records) {
842  $this->‪addGeneralErrorsByTable($table);
843  if ($table !== 'pages') {
844  foreach ($records as ‪$uid => ‪$record) {
845  // PID: Set the main $this->pid, unless a NEW-id is found
846  ‪$pid = isset($this->importMapId['pages'][‪$record['pid']])
847  ? (int)$this->importMapId['pages'][‪$record['pid']]
848  : $this->pid;
849  if (isset(‪$GLOBALS['TCA'][$table]['ctrl']['rootLevel'])) {
850  $rootLevelSetting = (int)‪$GLOBALS['TCA'][$table]['ctrl']['rootLevel'];
851  if ($rootLevelSetting === 1) {
852  ‪$pid = 0;
853  } elseif ($rootLevelSetting === 0 && ‪$pid === 0) {
854  $this->‪addError('Error: Record type ' . $table . ' is not allowed on pid 0');
855  continue;
856  }
857  }
858  // Add record
859  $this->‪addSingle($importData, $table, ‪$uid, ‪$pid);
860  }
861  }
862  }
863  } else {
864  $this->‪addError('Error: No records defined in internal data array.');
865  }
866 
867  // Write records to the database
868  $dataHandler = $this->‪createDataHandler();
869  $this->‪callHook('before_writeRecordsRecords', [
870  'tce' => &$dataHandler,
871  'data' => &$importData,
872  ]);
873  $dataHandler->suggestedInsertUids = ‪$this->suggestedInsertUids;
874  // Because all records are submitted in the correct order with positive pid numbers,
875  // we should internally reverse the order of submission.
876  $dataHandler->reverseOrder = true;
877  $dataHandler->isImporting = true;
878  $dataHandler->start($importData, []);
879  $dataHandler->process_datamap();
880  $this->‪callHook('after_writeRecordsRecords', [
881  'tce' => &$dataHandler,
882  ]);
883  $this->‪addToMapId($importData, $dataHandler->substNEWwithIDs);
884 
885  // Sort records
886  $this->‪writeRecordsOrder();
887  }
888 
896  protected function ‪writeRecordsOrder(): void
897  {
898  if (!$this->update) {
899  return;
900  }
901 
902  $importCmd = [];
903 
904  $pageList = [];
905  if (is_array($this->dat['header']['pagetree'] ?? null)) {
906  $this->‪flatInversePageTree($this->dat['header']['pagetree'], $pageList);
907  }
908  // @todo: drop by-reference and write final $this->dat at the end of method?!
909  if (is_array($this->dat['header']['pid_lookup'] ?? null)) {
910  foreach ($this->dat['header']['pid_lookup'] as ‪$pid => &$recordsByPid) {
911  $mappedPid = $this->importMapId['pages'][‪$pid] ?? ‪$this->pid;
913  foreach ($recordsByPid as $table => &$records) {
914  // If $mappedPid === $this->pid then we are on root level and we can consider to move pages as well!
915  // (they will not be in the page tree!)
916  if ($table !== 'pages' || !isset($pageList[‪$pid])) {
917  foreach (array_reverse(array_keys($records)) as ‪$uid) {
918  if ($this->‪doRespectPid($table, (int)‪$uid)) {
919  if (isset($this->importMapId[$table][‪$uid])) {
920  $mappedUid = $this->importMapId[$table][‪$uid];
921  $importCmd[$table][$mappedUid]['move'] = $mappedPid;
922  }
923  }
924  }
925  }
926  }
927  }
928  }
929  }
930 
931  // Move records in the database
932  if (!empty($importCmd)) {
933  $dataHandler = $this->‪createDataHandler();
934  $this->‪callHook('before_writeRecordsRecordsOrder', [
935  'tce' => &$dataHandler,
936  'data' => &$importCmd,
937  ]);
938  $dataHandler->start([], $importCmd);
939  $dataHandler->process_cmdmap();
940  $this->‪callHook('after_writeRecordsRecordsOrder', [
941  'tce' => &$dataHandler,
942  ]);
943  }
944  }
945 
957  protected function ‪addSingle(array &$importData, string $table, int ‪$uid, ‪$pid): void
958  {
959  // @todo return modified $importData instead of by-reference.
960  if (($this->importMode[$table . ':' . ‪$uid] ?? '') === self::IMPORT_MODE_EXCLUDE) {
961  return;
962  }
963 
964  ‪$record = $this->dat['records'][$table . ':' . ‪$uid]['data'] ?? null;
965 
966  if (!is_array(‪$record)) {
967  if (!($table === 'pages' && ‪$uid === 0)) {
968  // On root level we don't want this error message.
969  $this->‪addError('Error: No record was found in data array!');
970  }
971  return;
972  }
973 
974  // Generate record ID
975  $ID = ‪StringUtility::getUniqueId('NEW');
976  if ($this->update
977  && $this->‪getRecordFromDatabase($table, ‪$uid) !== null
978  && ($this->importMode[$table . ':' . ‪$uid] ?? '') !== self::IMPORT_MODE_AS_NEW
979  ) {
980  $ID = ‪$uid;
981  } elseif ($table === 'sys_file_metadata'
982  && ‪$record['sys_language_uid'] === '0'
983  && isset($this->importMapId['sys_file'][‪$record['file']])
984  ) {
985  // On adding sys_file records the belonging sys_file_metadata record was also created:
986  // If there is one, the record needs to be overwritten instead of a new one created.
987  $databaseRecord = $this->‪getSysFileMetaDataFromDatabase(
988  0,
989  $this->importMapId['sys_file'][‪$record['file']],
990  0
991  );
992  if (is_array($databaseRecord)) {
993  $this->importMapId['sys_file_metadata'][‪$record['uid']] = $databaseRecord['uid'];
994  $ID = $databaseRecord['uid'];
995  }
996  }
997 
998  // Mapping of generated record ID to original record UID
999  $this->importNewId[$table . ':' . $ID] = ['table' => $table, 'uid' => ‪$uid];
1000  if ($table === 'pages') {
1001  $this->importNewIdPids[‪$uid] = $ID;
1002  }
1003 
1004  // Record data
1005  $importData[$table][$ID] = ‪$record;
1006  $importData[$table][$ID]['tx_impexp_origuid'] = $importData[$table][$ID]['uid'];
1007 
1008  // Record permissions
1009  if ($table === 'pages') {
1010  // Have to reset the user/group IDs so pages are owned by the importing user.
1011  // Otherwise strange things may happen for non-admins!
1012  unset($importData[$table][$ID]['perms_userid']);
1013  unset($importData[$table][$ID]['perms_groupid']);
1014  }
1015 
1016  // Record UID and PID
1017  unset($importData[$table][$ID]['uid']);
1018  // - for existing record
1020  unset($importData[$table][$ID]['pid']);
1021  }
1022  // - for new record
1023  else {
1024  $importData[$table][$ID]['pid'] = ‪$pid;
1025  if ((($this->importMode[$table . ':' . ‪$uid] ?? '') === self::IMPORT_MODE_FORCE_UID && $this->update
1026  || $this->forceAllUids)
1027  && $this->‪getBackendUser()->isAdmin()
1028  ) {
1029  $importData[$table][$ID]['uid'] = ‪$uid;
1030  $this->suggestedInsertUids[$table . ':' . ‪$uid] = 'DELETE';
1031  }
1032  }
1033 
1034  // Record relations
1035  foreach ($this->dat['records'][$table . ':' . ‪$uid]['rels'] as $field => &$relation) {
1036  if (isset($relation['type'])) {
1037  switch ($relation['type']) {
1038  case 'db':
1039  case 'file':
1040  // Set blank now, fix later in setRelations(),
1041  // because we need to know ALL newly created IDs before we can map relations!
1042  // In the meantime we set NO values for relations.
1043  //
1044  // BUT for field uid_local of table sys_file_reference the relation MUST not be cleared here,
1045  // because the value is already the uid of the right imported sys_file record.
1046  // @see fixUidLocalInSysFileReferenceRecords()
1047  // If it's empty or a uid to another record the FileExtensionFilter will throw an exception or
1048  // delete the reference record if the file extension of the related record doesn't match.
1049  if (!($table === 'sys_file_reference' && $field === 'uid_local')
1050  && is_array(‪$GLOBALS['TCA'][$table]['columns'][$field]['config'] ?? false)
1051  ) {
1052  $importData[$table][$ID][$field] = $this->‪getReferenceDefaultValue(‪$GLOBALS['TCA'][$table]['columns'][$field]['config']);
1053  }
1054  break;
1055  case 'flex':
1056  // Set blank now, fix later in setFlexFormRelations().
1057  // In the meantime we set NO values for flexforms - this is mainly because file references
1058  // inside will not be processed properly. In fact references will point to no file
1059  // or existing files (in which case there will be double-references which is a big problem of
1060  // course!).
1061  //
1062  // BUT for the field "configuration" of the table "sys_file_storage" the relation MUST NOT be
1063  // cleared, because the configuration array contains only string values, which are furthermore
1064  // important for the further import, e.g. the base path.
1065  if (!($table === 'sys_file_storage' && $field === 'configuration')) {
1066  $importData[$table][$ID][$field] = $this->‪getReferenceDefaultValue(‪$GLOBALS['TCA'][$table]['columns'][$field]['config']);
1067  }
1068  break;
1069  }
1070  }
1071  }
1072  }
1073 
1079  protected function ‪getReferenceDefaultValue(array $configuration): int|float|string
1080  {
1081  if (!empty($configuration['MM']) || !empty($configuration['foreign_field'])) {
1082  return 0;
1083  }
1084  if (array_key_exists('default', $configuration)) {
1085  return $configuration['default'];
1086  }
1087  return '';
1088  }
1089 
1093  protected function ‪getSysFileMetaDataFromDatabase(int ‪$pid, int $file, int $sysLanguageUid): ?array
1094  {
1095  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1096  ->getQueryBuilderForTable('sys_file_metadata');
1097 
1098  $databaseRecord = $queryBuilder->select('uid')
1099  ->from('sys_file_metadata')
1100  ->where(
1101  $queryBuilder->expr()->eq(
1102  'file',
1103  $queryBuilder->createNamedParameter($file, ‪Connection::PARAM_INT)
1104  ),
1105  $queryBuilder->expr()->eq(
1106  'sys_language_uid',
1107  $queryBuilder->createNamedParameter($sysLanguageUid, ‪Connection::PARAM_INT)
1108  ),
1109  $queryBuilder->expr()->eq(
1110  'pid',
1111  $queryBuilder->createNamedParameter(‪$pid, ‪Connection::PARAM_INT)
1112  )
1113  )
1114  ->executeQuery()
1115  ->fetchAssociative();
1116 
1117  return is_array($databaseRecord) ? $databaseRecord : null;
1118  }
1127  protected function ‪addToMapId(array $importData, array $substNEWwithIDs): void
1128  {
1129  foreach ($importData as $table => &$records) {
1130  foreach ($records as $ID => &$_) {
1131  ‪$uid = $this->importNewId[$table . ':' . $ID]['uid'];
1132  if (isset($substNEWwithIDs[$ID])) {
1133  $this->importMapId[$table][‪$uid] = $substNEWwithIDs[$ID];
1134  } elseif ($this->update) {
1135  // Map same ID to same ID....
1136  $this->importMapId[$table][‪$uid] = $ID;
1137  } else {
1138  // If $this->importMapId contains already the right mapping, skip the error message.
1139  // See special handling of sys_file_metadata in addSingle() => nothing to do.
1140  if (!($table === 'sys_file_metadata'
1141  && isset($this->importMapId[$table][‪$uid])
1142  && $this->importMapId[$table][‪$uid] == $ID)
1143  ) {
1144  $this->‪addError(
1145  'Possible error: ' . $table . ':' . ‪$uid . ' had no new id assigned to it. ' .
1146  'This indicates that the record was not added to database during import. ' .
1147  'Please check changelog!'
1148  );
1149  }
1150  }
1151  }
1152  }
1153  }
1154 
1160  protected function ‪createDataHandler(): DataHandler
1161  {
1162  $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
1163  $dataHandler->dontProcessTransformations = true;
1164  $dataHandler->enableLogging = ‪$this->enableLogging;
1165  return $dataHandler;
1166  }
1167 
1168  /********************
1169  * Import relations
1170  *******************/
1171 
1180  protected function ‪setRelations(): void
1181  {
1182  $updateData = [];
1183 
1184  foreach ($this->importNewId as $original) {
1185  $table = $original['table'];
1186  ‪$uid = $original['uid'];
1187 
1188  if (isset($this->importMapId[$table][‪$uid])) {
1189  if (is_array($this->dat['records'][$table . ':' . ‪$uid]['rels'] ?? null)) {
1190  $actualUid = BackendUtility::wsMapId($table, $this->importMapId[$table][‪$uid]);
1191  foreach ($this->dat['records'][$table . ':' . ‪$uid]['rels'] as $field => $relation) {
1192  // Field "uid_local" of sys_file_reference needs no update because the correct reference uid
1193  // was already written.
1194  // @see ImportExport::fixUidLocalInSysFileReferenceRecords()
1195  if (isset($relation['type']) && !($table === 'sys_file_reference' && $field === 'uid_local')) {
1196  switch ($relation['type']) {
1197  case 'db':
1198  if (is_array($relation['itemArray'] ?? null) && !empty($relation['itemArray'])) {
1199  $fieldTca = &‪$GLOBALS['TCA'][$table]['columns'][$field];
1200  $actualRelations = $this->‪remapRelationsOfField($relation['itemArray'], $fieldTca['config']);
1201  $updateData[$table][$actualUid][$field] = implode(',', $actualRelations);
1202  }
1203  break;
1204  case 'file':
1205  if (is_array($relation['newValueFiles'] ?? null) && !empty($relation['newValueFiles'])) {
1206  $temporaryFiles = $this->‪writeFilesToTemporaryFolder($relation['newValueFiles']);
1207  $updateData[$table][$actualUid][$field] = implode(',', $temporaryFiles);
1208  }
1209  break;
1210  }
1211  }
1212  }
1213  } else {
1214  $this->‪addError(sprintf('Error: This record does not appear to have a relation array! (%s:%s)', $table, ‪$uid));
1215  }
1216  } else {
1217  $this->‪addError(sprintf('Error: This record does not appear to have been created! (%s:%s)', $table, ‪$uid));
1218  }
1219  }
1220 
1221  if (!empty($updateData)) {
1222  $dataHandler = $this->‪createDataHandler();
1223  $dataHandler->isImporting = true;
1224  $this->‪callHook('before_setRelation', [
1225  'tce' => &$dataHandler,
1226  'data' => &$updateData,
1227  ]);
1228  $dataHandler->start($updateData, []);
1229  $dataHandler->process_datamap();
1230  $this->‪callHook('after_setRelations', [
1231  'tce' => &$dataHandler,
1232  ]);
1233  }
1234  }
1235 
1246  protected function ‪remapRelationsOfField(array &$fieldRelations, array $fieldConfig): array
1247  {
1248  $actualRelations = [];
1249 
1250  foreach ($fieldRelations as $relation) {
1251  if (isset($this->importMapId[$relation['table']][$relation['id']])) {
1252  $actualUid = $this->importMapId[$relation['table']][$relation['id']];
1253  if ($fieldConfig['type'] === 'input' && isset($fieldConfig['wizards']['link'])) {
1254  // If an input field has a relation to a sys_file record this need to be converted back to
1255  // the public path. But use getPublicUrl() here, because could normally only be a local file path.
1256  try {
1257  $file = GeneralUtility::makeInstance(ResourceFactory::class)->retrieveFileOrFolderObject($actualUid);
1258  $actualRelations[] = $file->getPublicUrl();
1259  } catch (\Exception $e) {
1260  $actualRelations[] = 'file:' . $actualUid;
1261  }
1262  } else {
1263  $actualRelations[] = $relation['table'] . '_' . $actualUid;
1264  }
1265  } elseif ($this->‪isTableStatic($relation['table']) || $this->‪isRecordExcluded($relation['table'], (int)$relation['id']) || $relation['id'] < 0) {
1266  // Some select types could contain negative values, e.g. fe_groups (-1, -2).
1267  // This must be handled on both export and import.
1268  $actualRelations[] = $relation['table'] . '_' . $relation['id'];
1269  } else {
1270  $this->‪addError('Lost relation: ' . $relation['table'] . ':' . $relation['id']);
1271  }
1272  }
1273 
1274  return $actualRelations;
1275  }
1276 
1286  public function ‪writeFilesToTemporaryFolder(array $files): array
1287  {
1288  $temporaryFiles = [];
1289 
1290  foreach ($files as $fileInfo) {
1291  if (is_array($this->dat['files'][$fileInfo['ID']] ?? null)) {
1292  $fileRecord = &$this->dat['files'][$fileInfo['ID']];
1293 
1294  $temporaryFolder = $this->‪getOrCreateTemporaryFolderName();
1295  $temporaryFilePath = $temporaryFolder . '/' . $fileRecord['content_md5'];
1296 
1297  if (is_file($temporaryFilePath) && md5_file($temporaryFilePath) === $fileRecord['content_md5']) {
1298  $temporaryFiles[] = $temporaryFilePath;
1299  } else {
1300  if (‪GeneralUtility::writeFile($temporaryFilePath, $fileRecord['content'])) {
1301  clearstatcache();
1302  $temporaryFiles[] = $temporaryFilePath;
1303  } else {
1304  $this->‪addError(sprintf(
1305  'Error: Temporary file %s was not written as it should have been!',
1306  $temporaryFilePath
1307  ));
1308  }
1309  }
1310  } else {
1311  $this->‪addError(sprintf('Error: No file found for ID %s', $fileInfo['ID']));
1312  }
1313  }
1314 
1315  return $temporaryFiles;
1316  }
1325  protected function ‪setFlexFormRelations(): void
1326  {
1327  $updateData = [];
1328 
1329  foreach ($this->importNewId as $original) {
1330  $table = $original['table'];
1331  ‪$uid = $original['uid'];
1332 
1333  if (isset($this->importMapId[$table][‪$uid])) {
1334  if (is_array($this->dat['records'][$table . ':' . ‪$uid]['rels'] ?? null)) {
1335  $actualUid = BackendUtility::wsMapId($table, $this->importMapId[$table][‪$uid]);
1336  foreach ($this->dat['records'][$table . ':' . ‪$uid]['rels'] as $field => $relation) {
1337  // Field "configuration" of sys_file_storage needs no update because it has not been removed
1338  // and has no relations.
1339  // @see Import::addSingle()
1340  if (isset($relation['type']) && !($table === 'sys_file_storage' && $field === 'configuration')) {
1341  switch ($relation['type']) {
1342  case 'flex':
1343  // Re-insert temporarily removed original FlexForm data as fallback
1344  // @see Import::addSingle()
1345  $updateData[$table][$actualUid][$field] = $this->dat['records'][$table . ':' . ‪$uid]['data'][$field];
1346 
1347  if (!empty($relation['flexFormRels']['db']) || !empty($relation['flexFormRels']['file'])) {
1348  $actualRecord = BackendUtility::getRecord($table, $actualUid, '*');
1349  $fieldTca = &‪$GLOBALS['TCA'][$table]['columns'][$field];
1350  if (is_array($actualRecord) && is_array($fieldTca['config'] ?? null) && $fieldTca['config']['type'] === 'flex') {
1351  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1352  $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
1353  $fieldTca,
1354  $table,
1355  $field,
1356  $actualRecord
1357  );
1358  $dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
1359  $flexFormData = (new ‪Typo3XmlParser())->decodeWithReturningExceptionAsString(
1360  (string)($this->dat['records'][$table . ':' . ‪$uid]['data'][$field] ?? ''),
1363  ])
1364  );
1365  if (is_array($flexFormData['data'] ?? null)) {
1366  $flexFormIterator = GeneralUtility::makeInstance(DataHandler::class);
1367  $flexFormIterator->callBackObj = $this;
1368  $flexFormData['data'] = $flexFormIterator->checkValue_flex_procInData(
1369  $flexFormData['data'],
1370  [],
1371  $dataStructure,
1372  [$relation],
1373  'remapRelationsOfFlexFormCallBack'
1374  );
1375  }
1376  if (is_array($flexFormData['data'] ?? null)) {
1377  $updateData[$table][$actualUid][$field] = $flexFormData;
1378  }
1379  }
1380  }
1381  break;
1382  }
1383  }
1384  }
1385  } else {
1386  $this->‪addError(sprintf('Error: This record does not appear to have a relation array! (%s:%s)', $table, ‪$uid));
1387  }
1388  } else {
1389  $this->‪addError(sprintf('Error: This record does not appear to have been created! (%s:%s)', $table, ‪$uid));
1390  }
1391  }
1392 
1393  if (!empty($updateData)) {
1394  $dataHandler = $this->‪createDataHandler();
1395  $dataHandler->isImporting = true;
1396  $this->‪callHook('before_setFlexFormRelations', [
1397  'tce' => &$dataHandler,
1398  'data' => &$updateData,
1399  ]);
1400  $dataHandler->start($updateData, []);
1401  $dataHandler->process_datamap();
1402  $this->‪callHook('after_setFlexFormRelations', [
1403  'tce' => &$dataHandler,
1404  ]);
1405  }
1406  }
1407 
1420  public function ‪remapRelationsOfFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path): array
1421  {
1422  [$relation] = $pParams;
1423  // In case the $path is used as index without a trailing slash we will remove that
1424  if (!is_array($relation['flexFormRels']['db'][$path] ?? null) && is_array($relation['flexFormRels']['db'][rtrim($path, '/')] ?? false)) {
1425  $path = rtrim($path, '/');
1426  }
1427  if (is_array($relation['flexFormRels']['db'][$path] ?? null)) {
1428  $actualRelations = $this->‪remapRelationsOfField($relation['flexFormRels']['db'][$path], $dsConf);
1429  $dataValue = implode(',', $actualRelations);
1430  }
1431  if (is_array($relation['flexFormRels']['file'][$path] ?? null)) {
1432  $temporaryFiles = $this->‪writeFilesToTemporaryFolder($relation['flexFormRels']['file'][$path]);
1433  $dataValue = implode(',', $temporaryFiles);
1434  }
1435  return ['value' => $dataValue];
1436  }
1438  /**************************
1439  * Import soft references
1440  *************************/
1441 
1445  protected function ‪processSoftReferences(): void
1446  {
1447  $updateData = [];
1448 
1449  if (is_array($this->dat['header']['records'] ?? null)) {
1450  foreach ($this->dat['header']['records'] as $table => $records) {
1451  if (isset(‪$GLOBALS['TCA'][$table])) {
1452  foreach ($records as ‪$uid => ‪$record) {
1453  if (is_array(‪$record['softrefs'] ?? null)) {
1454  $actualUid = BackendUtility::wsMapId($table, $this->importMapId[$table][‪$uid] ?? 0);
1455  // First, group soft references by record field ...
1456  // (this could probably also have been done with $this->dat['records'] instead of $this->dat['header'])
1457  $softrefs = [];
1458  foreach (‪$record['softrefs'] as $softref) {
1459  if ($softref['field'] && is_array($softref['subst'] ?? null) && $softref['subst']['tokenID']) {
1460  $softrefs[$softref['field']][$softref['subst']['tokenID']] = $softref;
1461  }
1462  }
1463  // ... then process only fields which require substitution.
1464  foreach ($softrefs as $field => $softrefsByField) {
1465  if (is_array(‪$GLOBALS['TCA'][$table]['columns'][$field] ?? null)) {
1466  $fieldTca = &‪$GLOBALS['TCA'][$table]['columns'][$field];
1467  if ($fieldTca['config']['type'] === 'flex') {
1468  $actualRecord = BackendUtility::getRecord($table, $actualUid, '*');
1469  if (is_array($actualRecord)) {
1470  $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
1471  $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
1472  $fieldTca,
1473  $table,
1474  $field,
1475  $actualRecord
1476  );
1477  $dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
1478  $flexFormData = (new ‪Typo3XmlParser())->decodeWithReturningExceptionAsString(
1479  (string)($actualRecord[$field] ?? ''),
1482  ])
1483  );
1484  if (is_array($flexFormData['data'] ?? null)) {
1485  $flexFormIterator = GeneralUtility::makeInstance(DataHandler::class);
1486  $flexFormIterator->callBackObj = $this;
1487  $flexFormData['data'] = $flexFormIterator->checkValue_flex_procInData(
1488  $flexFormData['data'],
1489  [],
1490  $dataStructure,
1491  [$table, ‪$uid, $field, $softrefsByField],
1492  'processSoftReferencesFlexFormCallBack'
1493  );
1494  }
1495  if (is_array($flexFormData['data'] ?? null)) {
1496  $updateData[$table][$actualUid][$field] = $flexFormData;
1497  }
1498  }
1499  } else {
1500  // Get tokenizedContent string and proceed only if that is not blank:
1501  $tokenizedContent = $this->dat['records'][$table . ':' . ‪$uid]['rels'][$field]['softrefs']['tokenizedContent'];
1502  if (strlen($tokenizedContent) && is_array($softrefsByField)) {
1503  $updateData[$table][$actualUid][$field] = $this->‪processSoftReferencesSubstTokens($tokenizedContent, $softrefsByField, $table, (string)‪$uid);
1504  }
1505  }
1506  }
1507  }
1508  }
1509  }
1510  }
1511  }
1512  }
1513 
1514  // Update soft references in the database
1515  $dataHandler = $this->‪createDataHandler();
1516  $dataHandler->isImporting = true;
1517  $this->‪callHook('before_processSoftReferences', [
1518  'tce' => $dataHandler,
1519  'data' => &$updateData,
1520  ]);
1521  $dataHandler->enableLogging = true;
1522  $dataHandler->start($updateData, []);
1523  $dataHandler->process_datamap();
1524  $this->‪callHook('after_processSoftReferences', [
1525  'tce' => $dataHandler,
1526  ]);
1527  }
1528 
1540  public function ‪processSoftReferencesFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path): array
1541  {
1542  [$table, $origUid, $field, $softrefs] = $pParams;
1543  if (is_array($softrefs)) {
1544  // Filter for soft references of this path ...
1545  $softrefsByPath = [];
1546  foreach ($softrefs as $tokenID => $softref) {
1547  if ($softref['structurePath'] === $path) {
1548  $softrefsByPath[$tokenID] = $softref;
1549  }
1550  }
1551  // ... and perform the processing.
1552  if (!empty($softrefsByPath)) {
1553  $tokenizedContent = $this->dat['records'][$table . ':' . $origUid]['rels'][$field]['flexFormRels']['softrefs'][$path]['tokenizedContent'];
1554  if (strlen($tokenizedContent)) {
1555  $dataValue = $this->‪processSoftReferencesSubstTokens($tokenizedContent, $softrefsByPath, $table, (string)$origUid);
1556  }
1557  }
1558  }
1559  return ['value' => $dataValue];
1560  }
1561 
1571  protected function ‪processSoftReferencesSubstTokens(string $tokenizedContent, array $softrefs, string $table, string ‪$uid): string
1572  {
1573  foreach ($softrefs as &$softref) {
1574  $tokenID = $softref['subst']['tokenID'];
1575  $insertValue = $softref['subst']['tokenValue'];
1576  switch ((string)($this->softrefCfg[$tokenID]['mode'] ?? '')) {
1578  // This is the same as handling static relations:
1579  // Do not create or update the related file or record and do not change the link in any way,
1580  // but use the link as it was when exported.
1581  break;
1583  // This is the same as "exclude" with the option to manually edit the link before importing.
1584  $insertValue = $this->softrefInputValues[$tokenID];
1585  break;
1586  default:
1587  // This is almost the same as handling relations:
1588  // - Creating or updating related files and adjusting the file reference to link to the new file.
1589  // - Adjusting the record reference to link to the already imported record - if any.
1590  switch ((string)$softref['subst']['type']) {
1591  case 'file':
1592  $insertValue = $this->‪processSoftReferencesSaveFile($softref['subst']['relFileName'], $softref, $table, ‪$uid);
1593  break;
1594  case 'db':
1595  default:
1596  [$tempTable, $tempUid] = explode(':', (string)($softref['subst']['recordRef'] ?? ':'));
1597  if (isset($this->importMapId[$tempTable][$tempUid])) {
1598  $insertValue = BackendUtility::wsMapId($tempTable, $this->importMapId[$tempTable][$tempUid]);
1599  $tokenValue = (string)$softref['subst']['tokenValue'];
1600  if (str_contains($tokenValue, ':')) {
1601  [$tokenKey] = explode(':', $tokenValue);
1602  $insertValue = $tokenKey . ':' . $insertValue;
1603  }
1604  }
1605  }
1606  }
1607  // Finally, replace the soft reference token in tokenized content
1608  $tokenizedContent = str_replace('{softref:' . $tokenID . '}', (string)$insertValue, $tokenizedContent);
1609  }
1610  return $tokenizedContent;
1611  }
1612 
1622  protected function ‪processSoftReferencesSaveFile(string $relFileName, array $softref, string $table, string ‪$uid): string
1623  {
1624  if ($this->dat['header']['files'][$softref['file_ID']]) {
1625  // Initialize; Get directory prefix for file and find possible RTE filename
1626  $dirPrefix = ‪PathUtility::dirname($relFileName) . '/';
1627  if (str_starts_with($dirPrefix, $this->‪getFileadminFolderName() . '/')) {
1628  // File in fileadmin/ folder:
1629  // Create file (and possible resources)
1630  $newFileName = $this->‪processSoftReferencesSaveFileCreateRelFile($dirPrefix, ‪PathUtility::basename($relFileName), $softref['file_ID'], $table, ‪$uid) ?: '';
1631  if (strlen($newFileName)) {
1632  $relFileName = $newFileName;
1633  } else {
1634  $this->‪addError('ERROR: No new file created for "' . $relFileName . '"');
1635  }
1636  } else {
1637  $this->‪addError('ERROR: Sorry, cannot operate on non-RTE files which are outside the fileadmin folder.');
1638  }
1639  } else {
1640  $this->‪addError('ERROR: Could not find file ID in header.');
1641  }
1642  // Return (new) filename relative to public web path
1643  return $relFileName;
1644  }
1645 
1656  protected function ‪processSoftReferencesSaveFileCreateRelFile(string $origDirPrefix, string $fileName, string $fileID, string $table, string ‪$uid): ?string
1657  {
1658  // If the fileID map contains an entry for this fileID then just return the relative filename of that entry;
1659  // we don't want to write another unique filename for this one!
1660  if (isset($this->fileIdMap[$fileID])) {
1661  return ‪PathUtility::stripPathSitePrefix($this->fileIdMap[$fileID]);
1662  }
1663  // Verify FileMount access to dir-prefix. Returns the best alternative relative path if any
1664  $dirPrefix = $this->‪resolveStoragePath($origDirPrefix);
1665  if ($dirPrefix !== null && (!$this->update || $origDirPrefix === $dirPrefix) && $this->‪checkOrCreateDir($dirPrefix)) {
1666  $fileHeaderInfo = $this->dat['header']['files'][$fileID];
1667  $updMode = $this->update && $this->importMapId[$table][‪$uid] === ‪$uid && ($this->importMode[$table . ':' . ‪$uid] ?? '') !== self::IMPORT_MODE_AS_NEW;
1668  // Create new name for file:
1669  // Must have same ID in map array (just for security, is not really needed) and NOT be set "as_new".
1670 
1671  // Write main file:
1672  if ($updMode) {
1673  $newName = ‪Environment::getPublicPath() . '/' . $dirPrefix . $fileName;
1674  } else {
1675  // Create unique filename:
1677  $newName = (string)‪$fileProcObj->‪getUniqueName($fileName, ‪Environment::getPublicPath() . '/' . $dirPrefix);
1678  }
1679  if ($this->‪writeFileVerify($newName, $fileID)) {
1680  // If the resource was an HTML/CSS file with resources attached, we will write those as well!
1681  if (is_array($fileHeaderInfo['EXT_RES_ID'] ?? null)) {
1682  $tokenizedContent = $this->dat['files'][$fileID]['tokenizedContent'];
1683  $tokenSubstituted = false;
1685  if ($updMode) {
1686  foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
1687  if ($this->dat['files'][$res_fileID]['filename']) {
1688  // Resolve original filename:
1689  $relResourceFileName = $this->dat['files'][$res_fileID]['parentRelFileName'];
1690  $absResourceFileName = GeneralUtility::resolveBackPath(‪Environment::getPublicPath() . '/' . $origDirPrefix . $relResourceFileName);
1691  $absResourceFileName = GeneralUtility::getFileAbsFileName($absResourceFileName);
1692  if ($absResourceFileName && str_starts_with($absResourceFileName, ‪Environment::getPublicPath() . '/' . $this->‪getFileadminFolderName() . '/')) {
1693  $destDir = ‪PathUtility::stripPathSitePrefix(‪PathUtility::dirname($absResourceFileName) . '/');
1694  if ($this->‪resolveStoragePath($destDir, false) !== null && $this->‪checkOrCreateDir($destDir)) {
1695  $this->‪writeFileVerify($absResourceFileName, $res_fileID);
1696  } else {
1697  $this->‪addError('ERROR: Could not create file in directory "' . $destDir . '"');
1698  }
1699  } else {
1700  $this->‪addError('ERROR: Could not resolve path for "' . $relResourceFileName . '"');
1701  }
1702  $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent);
1703  $tokenSubstituted = true;
1704  }
1705  }
1706  } else {
1707  // Create the ressource's directory name (filename without extension, suffixed "_FILES")
1708  $resourceDir = ‪PathUtility::dirname($newName) . '/' . preg_replace('/\\.[^.]*$/', '', ‪PathUtility::basename($newName)) . '_FILES';
1709  if (‪GeneralUtility::mkdir($resourceDir)) {
1710  foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
1711  if ($this->dat['files'][$res_fileID]['filename']) {
1712  $absResourceFileName = (string)‪$fileProcObj->‪getUniqueName($this->dat['files'][$res_fileID]['filename'], $resourceDir);
1713  $relResourceFileName = substr($absResourceFileName, strlen(‪PathUtility::dirname($resourceDir)) + 1);
1714  $this->‪writeFileVerify($absResourceFileName, $res_fileID);
1715  $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent);
1716  $tokenSubstituted = true;
1717  }
1718  }
1719  }
1720  }
1721  // If substitutions has been made, write the content to the file again:
1722  if ($tokenSubstituted) {
1723  ‪GeneralUtility::writeFile($newName, $tokenizedContent);
1724  }
1725  }
1726  return ‪PathUtility::stripPathSitePrefix($newName);
1727  }
1728  }
1729  return null;
1730  }
1731 
1740  protected function ‪writeFileVerify(string $fileName, string $fileID, bool $bypassMountCheck = false): bool
1741  {
1743  if (!‪$fileProcObj->actionPerms['addFile']) {
1744  $this->‪addError('ERROR: You did not have sufficient permissions to write the file "' . $fileName . '"');
1745  return false;
1746  }
1747  // Just for security, check again. Should actually not be necessary.
1748  if (!$bypassMountCheck) {
1749  try {
1750  GeneralUtility::makeInstance(ResourceFactory::class)->getFolderObjectFromCombinedIdentifier(‪PathUtility::dirname($fileName));
1751  } catch (InsufficientFolderAccessPermissionsException $e) {
1752  $this->‪addError('ERROR: Filename "' . $fileName . '" was not allowed in destination path!');
1753  return false;
1754  }
1755  }
1756  $pathInfo = GeneralUtility::split_fileref($fileName);
1757  if (!GeneralUtility::makeInstance(FileNameValidator::class)->isValid($pathInfo['file'])) {
1758  $this->‪addError('ERROR: Filename "' . $fileName . '" failed against extension check or deny-pattern!');
1759  return false;
1760  }
1761  if (!GeneralUtility::getFileAbsFileName($fileName)) {
1762  $this->‪addError('ERROR: Filename "' . $fileName . '" was not a valid relative file path!');
1763  return false;
1764  }
1765  if (!$this->dat['files'][$fileID]) {
1766  $this->‪addError('ERROR: File ID "' . $fileID . '" could not be found');
1767  return false;
1768  }
1769  ‪GeneralUtility::writeFile($fileName, $this->dat['files'][$fileID]['content']);
1770  $this->fileIdMap[$fileID] = $fileName;
1771  if (hash_equals(md5((string)file_get_contents($fileName)), $this->dat['files'][$fileID]['content_md5'])) {
1772  return true;
1773  }
1774  $this->‪addError('ERROR: File content "' . $fileName . '" was corrupted');
1775  return false;
1776  }
1777 
1784  protected function ‪checkOrCreateDir(string $dirPrefix): bool
1785  {
1786  // Split dir path and remove first directory (which should be "fileadmin")
1787  $filePathParts = explode('/', $dirPrefix);
1788  $firstDir = array_shift($filePathParts);
1789  if ($firstDir === $this->‪getFileadminFolderName() && GeneralUtility::getFileAbsFileName($dirPrefix)) {
1790  $pathAcc = '';
1791  foreach ($filePathParts as $dirname) {
1792  $pathAcc .= '/' . $dirname;
1793  if (strlen($dirname)) {
1794  if (!@is_dir(‪Environment::getPublicPath() . '/' . $this->‪getFileadminFolderName() . $pathAcc)) {
1796  $this->‪addError('ERROR: Directory could not be created....B');
1797  return false;
1798  }
1799  }
1800  } elseif ($dirPrefix === $this->‪getFileadminFolderName() . $pathAcc) {
1801  return true;
1802  } else {
1803  $this->‪addError('ERROR: Directory could not be created....A');
1804  }
1805  }
1806  }
1807  return false;
1808  }
1809 
1816  protected function ‪callHook(string $name, array $params): void
1817  {
1818  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name] ?? [] as $hook) {
1819  GeneralUtility::callUserFunction($hook, $params, $this);
1820  }
1821  }
1822 
1823  public function ‪isEnableLogging(): bool
1824  {
1826  }
1827 
1828  public function ‪setEnableLogging(bool ‪$enableLogging): void
1829  {
1830  $this->enableLogging = ‪$enableLogging;
1831  }
1832 
1833  public function ‪isDecompressionAvailable(): bool
1834  {
1836  }
1837 }
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:100
‪TYPO3\CMS\Impexp\Import\$enableLogging
‪bool $enableLogging
Definition: Import.php:75
‪TYPO3\CMS\Impexp\Import\addSingle
‪addSingle(array &$importData, string $table, int $uid, $pid)
Definition: Import.php:949
‪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:1614
‪TYPO3\CMS\Impexp\Import\setEnableLogging
‪setEnableLogging(bool $enableLogging)
Definition: Import.php:1820
‪TYPO3\CMS\Impexp\ImportExport\getFileadminFolderName
‪getFileadminFolderName()
Definition: ImportExport.php:1107
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:46
‪TYPO3\CMS\Impexp\ImportExport\addGeneralErrorsByTable
‪addGeneralErrorsByTable(string $table)
Definition: ImportExport.php:456
‪TYPO3\CMS\Impexp\ImportExport\addError
‪addError(string $message)
Definition: ImportExport.php:1432
‪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:1278
‪TYPO3\CMS\Impexp\Import\writePages
‪writePages()
Definition: Import.php:714
‪TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
Definition: InsufficientFolderAccessPermissionsException.php:24
‪TYPO3\CMS\Impexp\Import\processSoftReferencesSubstTokens
‪string processSoftReferencesSubstTokens(string $tokenizedContent, array $softrefs, string $table, string $uid)
Definition: Import.php:1563
‪TYPO3\CMS\Impexp\Import\isEnableLogging
‪isEnableLogging()
Definition: Import.php:1815
‪TYPO3\CMS\Core\Resource\ResourceStorage\getDriverType
‪string getDriverType()
Definition: ResourceStorage.php:2821
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:22
‪TYPO3\CMS\Impexp\Import\doRespectPid
‪bool doRespectPid(string $table, int $uid)
Definition: Import.php:816
‪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:1415
‪TYPO3\CMS\Impexp\Exception\PrerequisitesNotMetException
Definition: PrerequisitesNotMetException.php:28
‪TYPO3\CMS\Impexp\Import\setRelations
‪setRelations()
Definition: Import.php:1172
‪TYPO3\CMS\Impexp\Import\loadInit
‪loadInit()
Definition: Import.php:282
‪TYPO3\CMS\Impexp\ImportExport\fetchStorages
‪fetchStorages()
Definition: ImportExport.php:251
‪TYPO3\CMS\Impexp\ImportExport\$pid
‪int $pid
Definition: ImportExport.php:63
‪TYPO3\CMS\Impexp\Import\remapRelationsOfFlexFormCallBack
‪array remapRelationsOfFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path)
Definition: Import.php:1412
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static getPublicPath()
Definition: Environment.php:187
‪TYPO3\CMS\Impexp\ImportExport\isTableStatic
‪bool isTableStatic(string $table)
Definition: ImportExport.php:1243
‪TYPO3\CMS\Core\Exception
‪TYPO3\CMS\Impexp\Import\getNextFilePart
‪array string null getNextFilePart($fd, bool $unserialize=false, string $name='')
Definition: Import.php:239
‪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:28
‪TYPO3\CMS\Impexp\Import\writeRecords
‪writeRecords()
Definition: Import.php:827
‪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:1648
‪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 basename(string $path)
Definition: PathUtility.php:219
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static isLoaded(string $key)
Definition: ExtensionManagementUtility.php:93
‪TYPO3\CMS\Impexp\Import\writePagesOrder
‪writePagesOrder()
Definition: Import.php:768
‪TYPO3\CMS\Impexp\Import\addToMapId
‪addToMapId(array $importData, array $substNEWwithIDs)
Definition: Import.php:1119
‪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:40
‪TYPO3\CMS\Impexp\Import\isDecompressionAvailable
‪isDecompressionAvailable()
Definition: Import.php:1825
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪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:1285
‪TYPO3\CMS\Impexp\Import\writeSysFileStorageRecords
‪writeSysFileStorageRecords()
Definition: Import.php:422
‪TYPO3\CMS\Impexp\Import\$suggestedInsertUids
‪array $suggestedInsertUids
Definition: Import.php:69
‪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:452
‪TYPO3\CMS\Impexp\ImportExport\getOrCreateTemporaryFolderName
‪getOrCreateTemporaryFolderName()
Definition: ImportExport.php:1119
‪TYPO3\CMS\Impexp\Import\removeSysFileReferenceRecordsWithRelationToMissingFile
‪removeSysFileReferenceRecordsWithRelationToMissingFile()
Definition: Import.php:686
‪TYPO3\CMS\Impexp\Import\processSoftReferencesFlexFormCallBack
‪array processSoftReferencesFlexFormCallBack(array $pParams, array $dsConf, string $dataValue, $dataValue_ext1, string $path)
Definition: Import.php:1532
‪TYPO3\CMS\Impexp\Import
Definition: Import.php:50
‪TYPO3\CMS\Impexp\ImportExport\$fileProcObj
‪ExtendedFileUtility $fileProcObj
Definition: ImportExport.php:178
‪TYPO3\CMS\Impexp\Import\writeSysFileRecords
‪writeSysFileRecords()
Definition: Import.php:511
‪TYPO3\CMS\Impexp\Import\writeRecordsOrder
‪writeRecordsOrder()
Definition: Import.php:888
‪TYPO3\CMS\Impexp\Import\getReferenceDefaultValue
‪getReferenceDefaultValue(array $configuration)
Definition: Import.php:1071
‪TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools
Definition: FlexFormTools.php:57
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:41
‪TYPO3\CMS\Impexp\Import\callHook
‪callHook(string $name, array $params)
Definition: Import.php:1808
‪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\createDataHandler
‪DataHandler createDataHandler()
Definition: Import.php:1152
‪TYPO3\CMS\Impexp\Import\initializeImport
‪initializeImport()
Definition: Import.php:411
‪TYPO3\CMS\Core\Resource\ResourceStorage\getConfiguration
‪array getConfiguration()
Definition: ResourceStorage.php:277
‪TYPO3\CMS\Impexp\Import\IMPORT_MODE_RESPECT_PID
‪const IMPORT_MODE_RESPECT_PID
Definition: Import.php:55
‪TYPO3\CMS\Impexp\Import\getSupportedFileExtensions
‪getSupportedFileExtensions()
Definition: Import.php:216
‪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:227
‪TYPO3\CMS\Impexp\ImportExport
Definition: ImportExport.php:46
‪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:86
‪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:666
‪TYPO3\CMS\Impexp\Exception\ImportFailedException
Definition: ImportFailedException.php:28
‪TYPO3\CMS\Impexp\Import\checkOrCreateDir
‪bool checkOrCreateDir(string $dirPrefix)
Definition: Import.php:1776
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:35
‪TYPO3\CMS\Impexp\ImportExport\flatInversePageTree
‪flatInversePageTree(array $pageTree, array &$list, int $pid=-1)
Definition: ImportExport.php:1225
‪TYPO3\CMS\Core\Resource\ResourceStorage
Definition: ResourceStorage.php:127
‪TYPO3\CMS\Impexp\ImportExport\hasErrors
‪hasErrors()
Definition: ImportExport.php:1437
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Impexp\Import\writeFileVerify
‪bool writeFileVerify(string $fileName, string $fileID, bool $bypassMountCheck=false)
Definition: Import.php:1732
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Impexp\ImportExport\resolveStoragePath
‪string resolveStoragePath(string $dirPrefix, bool $checkAlternatives=true)
Definition: ImportExport.php:1198
‪TYPO3\CMS\Impexp\Import\$isFilesSavedOutsideImportFile
‪bool $isFilesSavedOutsideImportFile
Definition: Import.php:101
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Impexp\Import\$importNewIdPids
‪array $importNewIdPids
Definition: Import.php:89
‪TYPO3\CMS\Impexp\ImportExport\getFileProcObj
‪ExtendedFileUtility getFileProcObj()
Definition: ImportExport.php:1401
‪TYPO3\CMS\Impexp\Import\setFlexFormRelations
‪setFlexFormRelations()
Definition: Import.php:1317
‪TYPO3\CMS\Impexp\Import\remapRelationsOfField
‪array remapRelationsOfField(array &$fieldRelations, array $fieldConfig)
Definition: Import.php:1238
‪TYPO3\CMS\Core\Resource\ResourceStorage\isWritable
‪bool isWritable()
Definition: ResourceStorage.php:391
‪TYPO3\CMS\Impexp\ImportExport\$dat
‪array $dat
Definition: ImportExport.php:172
‪TYPO3\CMS\Impexp\Import\isEquivalentStorage
‪bool isEquivalentStorage(ResourceStorage &$storageObject, array &$storageRecord)
Definition: Import.php:490
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:51
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪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\Core\Utility\GeneralUtility\mkdir
‪static bool mkdir($newFolder)
Definition: GeneralUtility.php:1621
‪TYPO3\CMS\Impexp\ImportExport\removeTemporaryFolderName
‪removeTemporaryFolderName()
Definition: ImportExport.php:1142
‪TYPO3\CMS\Core\Utility\GeneralUtility\writeFile
‪static bool writeFile($file, $content, $changePermissions=false)
Definition: GeneralUtility.php:1452
‪TYPO3\CMS\Impexp\Import\getSysFileMetaDataFromDatabase
‪getSysFileMetaDataFromDatabase(int $pid, int $file, int $sysLanguageUid)
Definition: Import.php:1085
‪TYPO3\CMS\Core\Resource\StorageRepository\findByUid
‪findByUid(int $uid)
Definition: StorageRepository.php:84
‪TYPO3\CMS\Impexp\ImportExport\isRecordExcluded
‪bool isRecordExcluded(string $table, int $uid)
Definition: ImportExport.php:1260
‪TYPO3\CMS\Impexp
‪TYPO3\CMS\Impexp\Import\processSoftReferences
‪processSoftReferences()
Definition: Import.php:1437
‪TYPO3\CMS\Impexp\Import\getMetaData
‪getMetaData()
Definition: Import.php:289
‪TYPO3\CMS\Impexp\Import\importData
‪importData()
Definition: Import.php:378
‪TYPO3\CMS\Impexp\ImportExport\getBackendUser
‪getBackendUser()
Definition: ImportExport.php:1442
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57
‪TYPO3\CMS\Impexp\Import\$decompressionAvailable
‪bool $decompressionAvailable
Definition: Import.php:93
‪TYPO3\CMS\Impexp\Import\checkImportPrerequisites
‪checkImportPrerequisites()
Definition: Import.php:303
‪TYPO3\CMS\Impexp\Import\$supportedFileExtensions
‪array $supportedFileExtensions
Definition: Import.php:97