TYPO3 CMS  TYPO3_6-2
ImportExport.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Impexp;
3 
21 
56 class ImportExport {
57 
63  public $showStaticRelations = FALSE;
64 
70  public $fileadminFolderName = '';
71 
77  public $mode = '';
78 
84  public $update = FALSE;
85 
91  public $doesImport = FALSE;
92 
101 
107  public $suggestedInsertUids = array();
108 
114  public $import_mode = array();
115 
121  public $global_ignore_pid = FALSE;
122 
128  public $force_all_UIDS = FALSE;
129 
135  public $showDiff = FALSE;
136 
142  public $allowPHPScripts = FALSE;
143 
149  public $enableLogging = FALSE;
150 
156  public $softrefInputValues = array();
157 
163  public $fileIDMap = array();
164 
170  public $maxFileSize = 1000000;
171 
177  public $maxRecordSize = 1000000;
178 
184  public $maxExportSize = 10000000;
185 
191  public $relOnlyTables = array();
192 
198  public $relStaticTables = array();
199 
205  public $excludeMap = array();
206 
212  public $softrefCfg = array();
213 
219  public $extensionDependencies = array();
220 
226  public $dontCompress = 0;
227 
234 
240  public $extFileResourceExtensions = 'html,htm,css';
241 
247  public $import_mapId = array();
248 
255  public $import_newId = array();
256 
262  public $import_newId_pids = array();
263 
269  public $import_data = array();
270 
276  public $errorLog = array();
277 
283  public $cache_getRecordPath = array();
284 
290  public $checkPID_cache = array();
291 
297  public $compress = 0;
298 
304  public $dat = array();
305 
312  public $fileProcObj = '';
313 
320  protected $recordTypesIncludeFields = array();
321 
327  protected $defaultRecordIncludeFields = array('uid', 'pid');
328 
334  protected $storageObjects = array();
335 
341  protected $legacyImport = FALSE;
342 
346  protected $legacyImportFolder = NULL;
347 
353  protected $legacyImportTargetPath = '_imported/';
354 
360  protected $legacyImportMigrationTables = array(
361  'tt_content' => array(
362  'image' => array(
363  'titleTexts' => 'titleText',
364  'description' => 'imagecaption',
365  'links' => 'image_link',
366  'alternativeTexts' => 'altText'
367  ),
368  'media' => array(
369  'description' => 'imagecaption',
370  )
371  ),
372  'pages' => array(
373  'media' => array()
374  ),
375  'pages_language_overlay' => array(
376  'media' => array()
377  )
378  );
379 
380 
387  protected $legacyImportMigrationRecords = array();
388 
389 
390  /**************************
391  * Initialize
392  *************************/
393 
402  public function init($dontCompress = 0, $mode = '') {
403  $this->compress = function_exists('gzcompress');
404  $this->dontCompress = $dontCompress;
405  $this->mode = $mode;
406  $this->fileadminFolderName = !empty($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) ? rtrim($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') : 'fileadmin';
407  }
408 
409  /**************************
410  * Export / Init + Meta Data
411  *************************/
412 
419  public function setHeaderBasics() {
420  // Initializing:
421  if (is_array($this->softrefCfg)) {
422  foreach ($this->softrefCfg as $key => $value) {
423  if (!strlen($value['mode'])) {
424  unset($this->softrefCfg[$key]);
425  }
426  }
427  }
428  // Setting in header memory:
429  // Version of file format
430  $this->dat['header']['XMLversion'] = '1.0';
431  // Initialize meta data array (to put it in top of file)
432  $this->dat['header']['meta'] = array();
433  // Add list of tables to consider static
434  $this->dat['header']['relStaticTables'] = $this->relStaticTables;
435  // The list of excluded records
436  $this->dat['header']['excludeMap'] = $this->excludeMap;
437  // Soft Reference mode for elements
438  $this->dat['header']['softrefCfg'] = $this->softrefCfg;
439  // List of extensions the import depends on.
440  $this->dat['header']['extensionDependencies'] = $this->extensionDependencies;
441  }
442 
450  public function setCharset($charset) {
451  $this->dat['header']['charset'] = $charset;
452  }
453 
466  public function setMetaData($title, $description, $notes, $packager_username, $packager_name, $packager_email) {
467  $this->dat['header']['meta'] = array(
468  'title' => $title,
469  'description' => $description,
470  'notes' => $notes,
471  'packager_username' => $packager_username,
472  'packager_name' => $packager_name,
473  'packager_email' => $packager_email,
474  'TYPO3_version' => TYPO3_version,
475  'created' => strftime('%A %e. %B %Y', $GLOBALS['EXEC_TIME'])
476  );
477  }
478 
486  public function addThumbnail($imgFilepath) {
487  if (@is_file($imgFilepath)) {
488  $imgInfo = @getimagesize($imgFilepath);
489  if (is_array($imgInfo)) {
490  $fileContent = GeneralUtility::getUrl($imgFilepath);
491  $this->dat['header']['thumbnail'] = array(
492  'imgInfo' => $imgInfo,
493  'content' => $fileContent,
494  'filesize' => strlen($fileContent),
495  'filemtime' => filemtime($imgFilepath),
496  'filename' => PathUtility::basename($imgFilepath)
497  );
498  }
499  }
500  }
501 
502  /**************************
503  * Export / Init Page tree
504  *************************/
505 
513  public function setPageTree($idH) {
514  $this->dat['header']['pagetree'] = $this->unsetExcludedSections($idH);
515  return $this->flatInversePageTree($this->dat['header']['pagetree']);
516  }
517 
527  public function unsetExcludedSections($idH) {
528  if (is_array($idH)) {
529  foreach ($idH as $k => $v) {
530  if ($this->excludeMap['pages:' . $idH[$k]['uid']]) {
531  unset($idH[$k]);
532  } elseif (is_array($idH[$k]['subrow'])) {
533  $idH[$k]['subrow'] = $this->unsetExcludedSections($idH[$k]['subrow']);
534  }
535  }
536  }
537  return $idH;
538  }
539 
549  public function flatInversePageTree($idH, $a = array()) {
550  if (is_array($idH)) {
551  $idH = array_reverse($idH);
552  foreach ($idH as $k => $v) {
553  $a[$v['uid']] = $v['uid'];
554  if (is_array($v['subrow'])) {
555  $a = $this->flatInversePageTree($v['subrow'], $a);
556  }
557  }
558  }
559  return $a;
560  }
561 
572  public function flatInversePageTree_pid($idH, $a = array(), $pid = -1) {
573  if (is_array($idH)) {
574  $idH = array_reverse($idH);
575  foreach ($idH as $k => $v) {
576  $a[$v['uid']] = $pid;
577  if (is_array($v['subrow'])) {
578  $a = $this->flatInversePageTree_pid($v['subrow'], $a, $v['uid']);
579  }
580  }
581  }
582  return $a;
583  }
584 
585  /**************************
586  * Export
587  *************************/
588 
597  foreach ($recordTypesIncludeFields as $table => $fields) {
598  if (!is_array($fields)) {
599  throw new \TYPO3\CMS\Core\Exception('The include fields for record type ' . htmlspecialchars($table) . ' are not defined by an array.', 1391440658);
600  }
601  $this->setRecordTypeIncludeFields($table, $fields);
602  }
603  }
604 
612  public function setRecordTypeIncludeFields($table, array $fields) {
613  $this->recordTypesIncludeFields[$table] = $fields;
614  }
615 
626  public function export_addRecord($table, $row, $relationLevel = 0) {
627  BackendUtility::workspaceOL($table, $row);
628  if ((string)$table !== '' && is_array($row) && $row['uid'] > 0 && !$this->excludeMap[($table . ':' . $row['uid'])]) {
629  if ($this->checkPID($table === 'pages' ? $row['uid'] : $row['pid'])) {
630  if (!isset($this->dat['records'][($table . ':' . $row['uid'])])) {
631  // Prepare header info:
632  $row = $this->filterRecordFields($table, $row);
633  $headerInfo = array();
634  $headerInfo['uid'] = $row['uid'];
635  $headerInfo['pid'] = $row['pid'];
636  $headerInfo['title'] = GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $row), 40);
637  $headerInfo['size'] = strlen(serialize($row));
638  if ($relationLevel) {
639  $headerInfo['relationLevel'] = $relationLevel;
640  }
641  // If record content is not too large in size, set the header content and add the rest:
642  if ($headerInfo['size'] < $this->maxRecordSize) {
643  // Set the header summary:
644  $this->dat['header']['records'][$table][$row['uid']] = $headerInfo;
645  // Create entry in the PID lookup:
646  $this->dat['header']['pid_lookup'][$row['pid']][$table][$row['uid']] = 1;
647  // Initialize reference index object:
648  $refIndexObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\ReferenceIndex');
649  // Yes to workspace overlays for exporting....
650  $refIndexObj->WSOL = TRUE;
651  $relations = $refIndexObj->getRelations($table, $row);
652  $relations = $this->fixFileIDsInRelations($relations);
653  $relations = $this->removeSoftrefsHavingTheSameDatabaseRelation($relations);
654  // Data:
655  $this->dat['records'][$table . ':' . $row['uid']] = array();
656  $this->dat['records'][$table . ':' . $row['uid']]['data'] = $row;
657  $this->dat['records'][$table . ':' . $row['uid']]['rels'] = $relations;
658  $this->errorLog = array_merge($this->errorLog, $refIndexObj->errorLog);
659  // Merge error logs.
660  // Add information about the relations in the record in the header:
661  $this->dat['header']['records'][$table][$row['uid']]['rels'] = $this->flatDBrels($this->dat['records'][$table . ':' . $row['uid']]['rels']);
662  // Add information about the softrefs to header:
663  $this->dat['header']['records'][$table][$row['uid']]['softrefs'] = $this->flatSoftRefs($this->dat['records'][$table . ':' . $row['uid']]['rels']);
664  } else {
665  $this->error('Record ' . $table . ':' . $row['uid'] . ' was larger than maxRecordSize (' . GeneralUtility::formatSize($this->maxRecordSize) . ')');
666  }
667  } else {
668  $this->error('Record ' . $table . ':' . $row['uid'] . ' already added.');
669  }
670  } else {
671  $this->error('Record ' . $table . ':' . $row['uid'] . ' was outside your DB mounts!');
672  }
673  }
674  }
675 
683  protected function fixFileIDsInRelations(array $relations) {
684  foreach ($relations as $field => $relation) {
685  if (isset($relation['type']) && $relation['type'] === 'file') {
686  foreach ($relation['newValueFiles'] as $key => $fileRelationData) {
687  $absoluteFilePath = $fileRelationData['ID_absFile'];
688  if (GeneralUtility::isFirstPartOfStr($absoluteFilePath, PATH_site)) {
689  $relatedFilePath = \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($absoluteFilePath);
690  $relations[$field]['newValueFiles'][$key]['ID'] = md5($relatedFilePath);
691  }
692  }
693  }
694  if ($relation['type'] == 'flex') {
695  if (is_array($relation['flexFormRels']['file'])) {
696  foreach ($relation['flexFormRels']['file'] as $key => $subList) {
697  foreach ($subList as $subKey => $fileRelationData) {
698  $absoluteFilePath = $fileRelationData['ID_absFile'];
699  if (GeneralUtility::isFirstPartOfStr($absoluteFilePath, PATH_site)) {
700  $relatedFilePath = \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($absoluteFilePath);
701  $relations[$field]['flexFormRels']['file'][$key][$subKey]['ID'] = md5($relatedFilePath);
702  }
703  }
704  }
705  }
706  }
707  }
708  return $relations;
709  }
710 
720  protected function removeSoftrefsHavingTheSameDatabaseRelation($relations) {
721  $fixedRelations = array();
722  foreach ($relations as $field => $relation) {
723  $newRelation = $relation;
724  if (isset($newRelation['type']) && $newRelation['type'] === 'db') {
725  foreach ($newRelation['itemArray'] as $key => $dbRelationData) {
726  if ($dbRelationData['table'] === 'sys_file') {
727  if (isset($newRelation['softrefs']['keys']['typolink'])) {
728  foreach ($newRelation['softrefs']['keys']['typolink'] as $softrefKey => $softRefData) {
729  if ($softRefData['subst']['type'] === 'file') {
730  $file = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->retrieveFileOrFolderObject($softRefData['subst']['relFileName']);
731  if ($file instanceof \TYPO3\CMS\Core\Resource\File) {
732  if ($file->getUid() == $dbRelationData['id']) {
733  unset($newRelation['softrefs']['keys']['typolink'][$softrefKey]);
734  }
735  }
736  }
737  }
738  if (empty($newRelation['softrefs']['keys']['typolink'])) {
739  unset($newRelation['softrefs']);
740  }
741  }
742  }
743  }
744  }
745  $fixedRelations[$field] = $newRelation;
746  }
747  return $fixedRelations;
748  }
749 
750 
761  public function export_addDBRelations($relationLevel = 0) {
762  // Initialize:
763  $addR = array();
764  // Traverse all "rels" registered for "records"
765  if (is_array($this->dat['records'])) {
766  foreach ($this->dat['records'] as $k => $value) {
767  if (is_array($this->dat['records'][$k])) {
768  foreach ($this->dat['records'][$k]['rels'] as $fieldname => $vR) {
769  // For all DB types of relations:
770  if ($vR['type'] == 'db') {
771  foreach ($vR['itemArray'] as $fI) {
772  $this->export_addDBRelations_registerRelation($fI, $addR);
773  }
774  }
775  // For all flex/db types of relations:
776  if ($vR['type'] == 'flex') {
777  // DB relations in flex form fields:
778  if (is_array($vR['flexFormRels']['db'])) {
779  foreach ($vR['flexFormRels']['db'] as $subList) {
780  foreach ($subList as $fI) {
781  $this->export_addDBRelations_registerRelation($fI, $addR);
782  }
783  }
784  }
785  // DB oriented soft references in flex form fields:
786  if (is_array($vR['flexFormRels']['softrefs'])) {
787  foreach ($vR['flexFormRels']['softrefs'] as $subList) {
788  foreach ($subList['keys'] as $spKey => $elements) {
789  foreach ($elements as $el) {
790  if ($el['subst']['type'] === 'db' && $this->includeSoftref($el['subst']['tokenID'])) {
791  list($tempTable, $tempUid) = explode(':', $el['subst']['recordRef']);
792  $fI = array(
793  'table' => $tempTable,
794  'id' => $tempUid
795  );
796  $this->export_addDBRelations_registerRelation($fI, $addR, $el['subst']['tokenID']);
797  }
798  }
799  }
800  }
801  }
802  }
803  // In any case, if there are soft refs:
804  if (is_array($vR['softrefs']['keys'])) {
805  foreach ($vR['softrefs']['keys'] as $spKey => $elements) {
806  foreach ($elements as $el) {
807  if ($el['subst']['type'] === 'db' && $this->includeSoftref($el['subst']['tokenID'])) {
808  list($tempTable, $tempUid) = explode(':', $el['subst']['recordRef']);
809  $fI = array(
810  'table' => $tempTable,
811  'id' => $tempUid
812  );
813  $this->export_addDBRelations_registerRelation($fI, $addR, $el['subst']['tokenID']);
814  }
815  }
816  }
817  }
818  }
819  }
820  }
821  } else {
822  $this->error('There were no records available.');
823  }
824  // Now, if there were new records to add, do so:
825  if (count($addR)) {
826  foreach ($addR as $fI) {
827  // Get and set record:
828  $row = BackendUtility::getRecord($fI['table'], $fI['id']);
829  if (is_array($row)) {
830  $this->export_addRecord($fI['table'], $row, $relationLevel + 1);
831  }
832  // Set status message
833  // Relation pointers always larger than zero except certain "select" types with negative values pointing to uids - but that is not supported here.
834  if ($fI['id'] > 0) {
835  $rId = $fI['table'] . ':' . $fI['id'];
836  if (!isset($this->dat['records'][$rId])) {
837  $this->dat['records'][$rId] = 'NOT_FOUND';
838  $this->error('Relation record ' . $rId . ' was not found!');
839  }
840  }
841  }
842  }
843  // Return overview of relations found and added
844  return $addR;
845  }
846 
857  public function export_addDBRelations_registerRelation($fI, &$addR, $tokenID = '') {
858  $rId = $fI['table'] . ':' . $fI['id'];
859  if (isset($GLOBALS['TCA'][$fI['table']]) && !$this->isTableStatic($fI['table']) && !$this->isExcluded($fI['table'], $fI['id']) && (!$tokenID || $this->includeSoftref($tokenID)) && $this->inclRelation($fI['table'])) {
860  if (!isset($this->dat['records'][$rId])) {
861  // Set this record to be included since it is not already.
862  $addR[$rId] = $fI;
863  }
864  }
865  }
866 
875  public function export_addFilesFromRelations() {
876  // Traverse all "rels" registered for "records"
877  if (is_array($this->dat['records'])) {
878  foreach ($this->dat['records'] as $k => $value) {
879  if (isset($this->dat['records'][$k]['rels']) && is_array($this->dat['records'][$k]['rels'])) {
880  foreach ($this->dat['records'][$k]['rels'] as $fieldname => $vR) {
881  // For all file type relations:
882  if ($vR['type'] == 'file') {
883  foreach ($vR['newValueFiles'] as $key => $fI) {
884  $this->export_addFile($fI, $k, $fieldname);
885  // Remove the absolute reference to the file so it doesn't expose absolute paths from source server:
886  unset($this->dat['records'][$k]['rels'][$fieldname]['newValueFiles'][$key]['ID_absFile']);
887  }
888  }
889  // For all flex type relations:
890  if ($vR['type'] == 'flex') {
891  if (is_array($vR['flexFormRels']['file'])) {
892  foreach ($vR['flexFormRels']['file'] as $key => $subList) {
893  foreach ($subList as $subKey => $fI) {
894  $this->export_addFile($fI, $k, $fieldname);
895  // Remove the absolute reference to the file so it doesn't expose absolute paths from source server:
896  unset($this->dat['records'][$k]['rels'][$fieldname]['flexFormRels']['file'][$key][$subKey]['ID_absFile']);
897  }
898  }
899  }
900  // DB oriented soft references in flex form fields:
901  if (is_array($vR['flexFormRels']['softrefs'])) {
902  foreach ($vR['flexFormRels']['softrefs'] as $key => $subList) {
903  foreach ($subList['keys'] as $spKey => $elements) {
904  foreach ($elements as $subKey => $el) {
905  if ($el['subst']['type'] === 'file' && $this->includeSoftref($el['subst']['tokenID'])) {
906  // Create abs path and ID for file:
907  $ID_absFile = GeneralUtility::getFileAbsFileName(PATH_site . $el['subst']['relFileName']);
908  $ID = md5($el['subst']['relFileName']);
909  if ($ID_absFile) {
910  if (!$this->dat['files'][$ID]) {
911  $fI = array(
912  'filename' => PathUtility::basename($ID_absFile),
913  'ID_absFile' => $ID_absFile,
914  'ID' => $ID,
915  'relFileName' => $el['subst']['relFileName']
916  );
917  $this->export_addFile($fI, '_SOFTREF_');
918  }
919  $this->dat['records'][$k]['rels'][$fieldname]['flexFormRels']['softrefs'][$key]['keys'][$spKey][$subKey]['file_ID'] = $ID;
920  }
921  }
922  }
923  }
924  }
925  }
926  }
927  // In any case, if there are soft refs:
928  if (is_array($vR['softrefs']['keys'])) {
929  foreach ($vR['softrefs']['keys'] as $spKey => $elements) {
930  foreach ($elements as $subKey => $el) {
931  if ($el['subst']['type'] === 'file' && $this->includeSoftref($el['subst']['tokenID'])) {
932  // Create abs path and ID for file:
933  $ID_absFile = GeneralUtility::getFileAbsFileName(PATH_site . $el['subst']['relFileName']);
934  $ID = md5($el['subst']['relFileName']);
935  if ($ID_absFile) {
936  if (!$this->dat['files'][$ID]) {
937  $fI = array(
938  'filename' => PathUtility::basename($ID_absFile),
939  'ID_absFile' => $ID_absFile,
940  'ID' => $ID,
941  'relFileName' => $el['subst']['relFileName']
942  );
943  $this->export_addFile($fI, '_SOFTREF_');
944  }
945  $this->dat['records'][$k]['rels'][$fieldname]['softrefs']['keys'][$spKey][$subKey]['file_ID'] = $ID;
946  }
947  }
948  }
949  }
950  }
951  }
952  }
953  }
954  } else {
955  $this->error('There were no records available.');
956  }
957  }
958 
965  if (!isset($this->dat['header']['records']['sys_file']) || !is_array($this->dat['header']['records']['sys_file'])) {
966  return;
967  }
968  foreach ($this->dat['header']['records']['sys_file'] as $sysFileUid => $_) {
969  $recordData = $this->dat['records']['sys_file:' . $sysFileUid]['data'];
970  $file = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->createFileObject($recordData);
971  $this->export_addSysFile($file);
972  }
973  }
974 
981  public function export_addSysFile(\TYPO3\CMS\Core\Resource\File $file) {
982  if ($file->getProperty('size') >= $this->maxFileSize) {
983  $this->error('File ' . $file->getPublicUrl() . ' was larger (' . GeneralUtility::formatSize($file->getProperty('size')) . ') than the maxFileSize (' . GeneralUtility::formatSize($this->maxFileSize) . ')! Skipping.');
984  return;
985  }
986  try {
987  $fileContent = $file->getContents();
988  } catch (\Exception $e) {
989  $this->error('Error when trying to add file ' . $file->getCombinedIdentifier() . ': ' . $e->getMessage());
990  return;
991  }
992  $fileUid = $file->getUid();
993  $fileInfo = $file->getStorage()->getFileInfo($file);
994  // we sadly have to cast it to string here, because the size property is also returning a string
995  $fileSize = (string)$fileInfo['size'];
996  if ($fileSize !== $file->getProperty('size')) {
997  $this->error('File size of ' . $file->getCombinedIdentifier() . ' is not up-to-date in index! File added with current size.');
998  $this->dat['records']['sys_file:' . $fileUid]['data']['size'] = $fileSize;
999  }
1000  $fileSha1 = $file->getStorage()->hashFile($file, 'sha1');
1001  if ($fileSha1 !== $file->getProperty('sha1')) {
1002  $this->error('File sha1 hash of ' . $file->getCombinedIdentifier() . ' is not up-to-date in index! File added on current sha1.');
1003  $this->dat['records']['sys_file:' . $fileUid]['data']['sha1'] = $fileSha1;
1004  }
1005 
1006  $fileRec = array();
1007  $fileRec['filesize'] = $fileSize;
1008  $fileRec['filename'] = $file->getProperty('name');
1009  $fileRec['filemtime'] = $file->getProperty('modification_date');
1010 
1011  // build unique id based on the storage and the file identifier
1012  $fileId = md5($file->getStorage()->getUid() . ':' . $file->getProperty('identifier_hash'));
1013 
1014  // Setting this data in the header
1015  $this->dat['header']['files_fal'][$fileId] = $fileRec;
1016 
1017  // ... and finally add the heavy stuff:
1018  $fileRec['content'] = $fileContent;
1019  $fileRec['content_sha1'] = $fileSha1;
1020 
1021  $this->dat['files_fal'][$fileId] = $fileRec;
1022  }
1023 
1024 
1034  public function export_addFile($fI, $recordRef = '', $fieldname = '') {
1035  if (@is_file($fI['ID_absFile'])) {
1036  if (filesize($fI['ID_absFile']) < $this->maxFileSize) {
1037  $fileInfo = stat($fI['ID_absFile']);
1038  $fileRec = array();
1039  $fileRec['filesize'] = $fileInfo['size'];
1040  $fileRec['filename'] = PathUtility::basename($fI['ID_absFile']);
1041  $fileRec['filemtime'] = $fileInfo['mtime'];
1042  //for internal type file_reference
1043  $fileRec['relFileRef'] = \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($fI['ID_absFile']);
1044  if ($recordRef) {
1045  $fileRec['record_ref'] = $recordRef . '/' . $fieldname;
1046  }
1047  if ($fI['relFileName']) {
1048  $fileRec['relFileName'] = $fI['relFileName'];
1049  }
1050  // Setting this data in the header
1051  $this->dat['header']['files'][$fI['ID']] = $fileRec;
1052  // ... and for the recordlisting, why not let us know WHICH relations there was...
1053  if ($recordRef && $recordRef !== '_SOFTREF_') {
1054  $refParts = explode(':', $recordRef, 2);
1055  if (!is_array($this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'])) {
1056  $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'] = array();
1057  }
1058  $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'][] = $fI['ID'];
1059  }
1060  // ... and finally add the heavy stuff:
1061  $fileRec['content'] = GeneralUtility::getUrl($fI['ID_absFile']);
1062  $fileRec['content_md5'] = md5($fileRec['content']);
1063  $this->dat['files'][$fI['ID']] = $fileRec;
1064  // For soft references, do further processing:
1065  if ($recordRef === '_SOFTREF_') {
1066  // RTE files?
1067  if ($RTEoriginal = $this->getRTEoriginalFilename(PathUtility::basename($fI['ID_absFile']))) {
1068  $RTEoriginal_absPath = PathUtility::dirname($fI['ID_absFile']) . '/' . $RTEoriginal;
1069  if (@is_file($RTEoriginal_absPath)) {
1070  $RTEoriginal_ID = md5($RTEoriginal_absPath);
1071  $fileInfo = stat($RTEoriginal_absPath);
1072  $fileRec = array();
1073  $fileRec['filesize'] = $fileInfo['size'];
1074  $fileRec['filename'] = PathUtility::basename($RTEoriginal_absPath);
1075  $fileRec['filemtime'] = $fileInfo['mtime'];
1076  $fileRec['record_ref'] = '_RTE_COPY_ID:' . $fI['ID'];
1077  $this->dat['header']['files'][$fI['ID']]['RTE_ORIG_ID'] = $RTEoriginal_ID;
1078  // Setting this data in the header
1079  $this->dat['header']['files'][$RTEoriginal_ID] = $fileRec;
1080  // ... and finally add the heavy stuff:
1081  $fileRec['content'] = GeneralUtility::getUrl($RTEoriginal_absPath);
1082  $fileRec['content_md5'] = md5($fileRec['content']);
1083  $this->dat['files'][$RTEoriginal_ID] = $fileRec;
1084  } else {
1085  $this->error('RTE original file "' . \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($RTEoriginal_absPath) . '" was not found!');
1086  }
1087  }
1088  // Files with external media?
1089  // This is only done with files grabbed by a softreference parser since it is deemed improbable that hard-referenced files should undergo this treatment.
1090  $html_fI = pathinfo(PathUtility::basename($fI['ID_absFile']));
1091  if ($this->includeExtFileResources && GeneralUtility::inList($this->extFileResourceExtensions, strtolower($html_fI['extension']))) {
1092  $uniquePrefix = '###' . md5($GLOBALS['EXEC_TIME']) . '###';
1093  if (strtolower($html_fI['extension']) === 'css') {
1094  $prefixedMedias = explode($uniquePrefix, preg_replace('/(url[[:space:]]*\\([[:space:]]*["\']?)([^"\')]*)(["\']?[[:space:]]*\\))/i', '\\1' . $uniquePrefix . '\\2' . $uniquePrefix . '\\3', $fileRec['content']));
1095  } else {
1096  // html, htm:
1097  $htmlParser = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Html\\HtmlParser');
1098  $prefixedMedias = explode($uniquePrefix, $htmlParser->prefixResourcePath($uniquePrefix, $fileRec['content'], array(), $uniquePrefix));
1099  }
1100  $htmlResourceCaptured = FALSE;
1101  foreach ($prefixedMedias as $k => $v) {
1102  if ($k % 2) {
1103  $EXTres_absPath = GeneralUtility::resolveBackPath(PathUtility::dirname($fI['ID_absFile']) . '/' . $v);
1104  $EXTres_absPath = GeneralUtility::getFileAbsFileName($EXTres_absPath);
1105  if ($EXTres_absPath && GeneralUtility::isFirstPartOfStr($EXTres_absPath, PATH_site . $this->fileadminFolderName . '/') && @is_file($EXTres_absPath)) {
1106  $htmlResourceCaptured = TRUE;
1107  $EXTres_ID = md5($EXTres_absPath);
1108  $this->dat['header']['files'][$fI['ID']]['EXT_RES_ID'][] = $EXTres_ID;
1109  $prefixedMedias[$k] = '{EXT_RES_ID:' . $EXTres_ID . '}';
1110  // Add file to memory if it is not set already:
1111  if (!isset($this->dat['header']['files'][$EXTres_ID])) {
1112  $fileInfo = stat($EXTres_absPath);
1113  $fileRec = array();
1114  $fileRec['filesize'] = $fileInfo['size'];
1115  $fileRec['filename'] = PathUtility::basename($EXTres_absPath);
1116  $fileRec['filemtime'] = $fileInfo['mtime'];
1117  $fileRec['record_ref'] = '_EXT_PARENT_:' . $fI['ID'];
1118  // Media relative to the HTML file.
1119  $fileRec['parentRelFileName'] = $v;
1120  // Setting this data in the header
1121  $this->dat['header']['files'][$EXTres_ID] = $fileRec;
1122  // ... and finally add the heavy stuff:
1123  $fileRec['content'] = GeneralUtility::getUrl($EXTres_absPath);
1124  $fileRec['content_md5'] = md5($fileRec['content']);
1125  $this->dat['files'][$EXTres_ID] = $fileRec;
1126  }
1127  }
1128  }
1129  }
1130  if ($htmlResourceCaptured) {
1131  $this->dat['files'][$fI['ID']]['tokenizedContent'] = implode('', $prefixedMedias);
1132  }
1133  }
1134  }
1135  } else {
1136  $this->error($fI['ID_absFile'] . ' was larger (' . GeneralUtility::formatSize(filesize($fI['ID_absFile'])) . ') than the maxFileSize (' . GeneralUtility::formatSize($this->maxFileSize) . ')! Skipping.');
1137  }
1138  } else {
1139  $this->error($fI['ID_absFile'] . ' was not a file! Skipping.');
1140  }
1141  }
1142 
1151  public function flatDBrels($dbrels) {
1152  $list = array();
1153  foreach ($dbrels as $dat) {
1154  if ($dat['type'] == 'db') {
1155  foreach ($dat['itemArray'] as $i) {
1156  $list[$i['table'] . ':' . $i['id']] = $i;
1157  }
1158  }
1159  if ($dat['type'] == 'flex' && is_array($dat['flexFormRels']['db'])) {
1160  foreach ($dat['flexFormRels']['db'] as $subList) {
1161  foreach ($subList as $i) {
1162  $list[$i['table'] . ':' . $i['id']] = $i;
1163  }
1164  }
1165  }
1166  }
1167  return $list;
1168  }
1169 
1177  public function flatSoftRefs($dbrels) {
1178  $list = array();
1179  foreach ($dbrels as $field => $dat) {
1180  if (is_array($dat['softrefs']['keys'])) {
1181  foreach ($dat['softrefs']['keys'] as $spKey => $elements) {
1182  if (is_array($elements)) {
1183  foreach ($elements as $subKey => $el) {
1184  $lKey = $field . ':' . $spKey . ':' . $subKey;
1185  $list[$lKey] = array_merge(array('field' => $field, 'spKey' => $spKey), $el);
1186  // Add file_ID key to header - slightly "risky" way of doing this because if the calculation changes for the same value in $this->records[...] this will not work anymore!
1187  if ($el['subst'] && $el['subst']['relFileName']) {
1188  $list[$lKey]['file_ID'] = md5(PATH_site . $el['subst']['relFileName']);
1189  }
1190  }
1191  }
1192  }
1193  }
1194  if ($dat['type'] == 'flex' && is_array($dat['flexFormRels']['softrefs'])) {
1195  foreach ($dat['flexFormRels']['softrefs'] as $structurePath => $subSoftrefs) {
1196  if (is_array($subSoftrefs['keys'])) {
1197  foreach ($subSoftrefs['keys'] as $spKey => $elements) {
1198  foreach ($elements as $subKey => $el) {
1199  $lKey = $field . ':' . $structurePath . ':' . $spKey . ':' . $subKey;
1200  $list[$lKey] = array_merge(array('field' => $field, 'spKey' => $spKey, 'structurePath' => $structurePath), $el);
1201  // Add file_ID key to header - slightly "risky" way of doing this because if the calculation changes for the same value in $this->records[...] this will not work anymore!
1202  if ($el['subst'] && $el['subst']['relFileName']) {
1203  $list[$lKey]['file_ID'] = md5(PATH_site . $el['subst']['relFileName']);
1204  }
1205  }
1206  }
1207  }
1208  }
1209  }
1210  }
1211  return $list;
1212  }
1213 
1222  protected function filterRecordFields($table, array $row) {
1223  if (isset($this->recordTypesIncludeFields[$table])) {
1224  $includeFields = array_unique(array_merge(
1225  $this->recordTypesIncludeFields[$table],
1226  $this->defaultRecordIncludeFields
1227  ));
1228  $newRow = array();
1229  foreach ($row as $key => $value) {
1230  if (in_array($key, $includeFields)) {
1231  $newRow[$key] = $value;
1232  }
1233  }
1234  } else {
1235  $newRow = $row;
1236  }
1237  return $newRow;
1238  }
1239 
1240 
1241  /**************************
1242  * File Output
1243  *************************/
1244 
1252  public function compileMemoryToFileContent($type = '') {
1253  if ($type == 'xml') {
1254  $out = $this->createXML();
1255  } else {
1256  $compress = $this->doOutputCompress();
1257  $out = '';
1258  // adding header:
1259  $out .= $this->addFilePart(serialize($this->dat['header']), $compress);
1260  // adding records:
1261  $out .= $this->addFilePart(serialize($this->dat['records']), $compress);
1262  // adding files:
1263  $out .= $this->addFilePart(serialize($this->dat['files']), $compress);
1264  // adding files_fal:
1265  $out .= $this->addFilePart(serialize($this->dat['files_fal']), $compress);
1266  }
1267  return $out;
1268  }
1269 
1276  public function createXML() {
1277  // Options:
1278  $options = array(
1279  'alt_options' => array(
1280  '/header' => array(
1281  'disableTypeAttrib' => TRUE,
1282  'clearStackPath' => TRUE,
1283  'parentTagMap' => array(
1284  'files' => 'file',
1285  'files_fal' => 'file',
1286  'records' => 'table',
1287  'table' => 'rec',
1288  'rec:rels' => 'relations',
1289  'relations' => 'element',
1290  'filerefs' => 'file',
1291  'pid_lookup' => 'page_contents',
1292  'header:relStaticTables' => 'static_tables',
1293  'static_tables' => 'tablename',
1294  'excludeMap' => 'item',
1295  'softrefCfg' => 'softrefExportMode',
1296  'extensionDependencies' => 'extkey',
1297  'softrefs' => 'softref_element'
1298  ),
1299  'alt_options' => array(
1300  '/pagetree' => array(
1301  'disableTypeAttrib' => TRUE,
1302  'useIndexTagForNum' => 'node',
1303  'parentTagMap' => array(
1304  'node:subrow' => 'node'
1305  )
1306  ),
1307  '/pid_lookup/page_contents' => array(
1308  'disableTypeAttrib' => TRUE,
1309  'parentTagMap' => array(
1310  'page_contents' => 'table'
1311  ),
1312  'grandParentTagMap' => array(
1313  'page_contents/table' => 'item'
1314  )
1315  )
1316  )
1317  ),
1318  '/records' => array(
1319  'disableTypeAttrib' => TRUE,
1320  'parentTagMap' => array(
1321  'records' => 'tablerow',
1322  'tablerow:data' => 'fieldlist',
1323  'tablerow:rels' => 'related',
1324  'related' => 'field',
1325  'field:itemArray' => 'relations',
1326  'field:newValueFiles' => 'filerefs',
1327  'field:flexFormRels' => 'flexform',
1328  'relations' => 'element',
1329  'filerefs' => 'file',
1330  'flexform:db' => 'db_relations',
1331  'flexform:file' => 'file_relations',
1332  'flexform:softrefs' => 'softref_relations',
1333  'softref_relations' => 'structurePath',
1334  'db_relations' => 'path',
1335  'file_relations' => 'path',
1336  'path' => 'element',
1337  'keys' => 'softref_key',
1338  'softref_key' => 'softref_element'
1339  ),
1340  'alt_options' => array(
1341  '/records/tablerow/fieldlist' => array(
1342  'useIndexTagForAssoc' => 'field'
1343  )
1344  )
1345  ),
1346  '/files' => array(
1347  'disableTypeAttrib' => TRUE,
1348  'parentTagMap' => array(
1349  'files' => 'file'
1350  )
1351  ),
1352  '/files_fal' => array(
1353  'disableTypeAttrib' => TRUE,
1354  'parentTagMap' => array(
1355  'files_fal' => 'file'
1356  )
1357  )
1358  )
1359  );
1360  // Creating XML file from $outputArray:
1361  $charset = $this->dat['header']['charset'] ?: 'utf-8';
1362  $XML = '<?xml version="1.0" encoding="' . $charset . '" standalone="yes" ?>' . LF;
1363  $XML .= GeneralUtility::array2xml($this->dat, '', 0, 'T3RecordDocument', 0, $options);
1364  return $XML;
1365  }
1366 
1373  public function doOutputCompress() {
1374  return $this->compress && !$this->dontCompress;
1375  }
1376 
1385  public function addFilePart($data, $compress = FALSE) {
1386  if ($compress) {
1387  $data = gzcompress($data);
1388  }
1389  return md5($data) . ':' . ($compress ? '1' : '0') . ':' . str_pad(strlen($data), 10, '0', STR_PAD_LEFT) . ':' . $data . ':';
1390  }
1391 
1392  /***********************
1393  * Import
1394  ***********************/
1395 
1401  protected function initializeImport() {
1402  // Set this flag to indicate that an import is being/has been done.
1403  $this->doesImport = 1;
1404  // Initialize:
1405  // These vars MUST last for the whole section not being cleared. They are used by the method setRelations() which are called at the end of the import session.
1406  $this->import_mapId = array();
1407  $this->import_newId = array();
1408  $this->import_newId_pids = array();
1409  // Temporary files stack initialized:
1410  $this->unlinkFiles = array();
1411  $this->alternativeFileName = array();
1412  $this->alternativeFilePath = array();
1413 
1414  $this->initializeStorageObjects();
1415  }
1416 
1422  protected function initializeStorageObjects() {
1424  $storageRepository = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\StorageRepository');
1425  $this->storageObjects = $storageRepository->findAll();
1426  }
1427 
1428 
1435  public function importData($pid) {
1436 
1437  $this->initializeImport();
1438 
1439  // Write sys_file_storages first
1440  $this->writeSysFileStorageRecords();
1441  // Write sys_file records and write the binary file data
1442  $this->writeSysFileRecords();
1443  // Write records, first pages, then the rest
1444  // Fields with "hard" relations to database, files and flexform fields are kept empty during this run
1445  $this->writeRecords_pages($pid);
1446  $this->writeRecords_records($pid);
1447  // Finally all the file and DB record references must be fixed. This is done after all records have supposedly been written to database:
1448  // $this->import_mapId will indicate two things: 1) that a record WAS written to db and 2) that it has got a new id-number.
1449  $this->setRelations();
1450  // And when all DB relations are in place, we can fix file and DB relations in flexform fields (since data structures often depends on relations to a DS record):
1451  $this->setFlexFormRelations();
1452  // Unlink temporary files:
1453  $this->unlinkTempFiles();
1454  // Finally, traverse all records and process softreferences with substitution attributes.
1455  $this->processSoftReferences();
1456  // After all migrate records using sys_file_reference now
1457  if ($this->legacyImport) {
1458  $this->migrateLegacyImportRecords();
1459  }
1460  }
1461 
1467  protected function writeSysFileStorageRecords() {
1468  if (!isset($this->dat['header']['records']['sys_file_storage'])) {
1469  return;
1470  }
1471  $sysFileStorageUidsToBeResetToDefaultStorage = array();
1472  foreach ($this->dat['header']['records']['sys_file_storage'] as $sysFileStorageUid => $_) {
1473  $storageRecord = $this->dat['records']['sys_file_storage:' . $sysFileStorageUid]['data'];
1474  // continue with Local, writable and online storage only
1475  if ($storageRecord['driver'] === 'Local' && $storageRecord['is_writable'] && $storageRecord['is_online']) {
1476  $useThisStorageUidInsteadOfTheOneInImport = 0;
1478  foreach ($this->storageObjects as $localStorage) {
1479  // check the available storage for Local, writable and online ones
1480  if ($localStorage->getDriverType() === 'Local' && $localStorage->isWritable() && $localStorage->isOnline()) {
1481  // check if there is already a identical storage present (same pathType and basePath)
1482  $storageRecordConfiguration = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->convertFlexFormDataToConfigurationArray($storageRecord['configuration']);
1483  $localStorageRecordConfiguration = $localStorage->getConfiguration();
1484  if ($storageRecordConfiguration['pathType'] == $localStorageRecordConfiguration['pathType'] && $storageRecordConfiguration['basePath'] == $localStorageRecordConfiguration['basePath']) {
1485  // same storage is already present
1486  $useThisStorageUidInsteadOfTheOneInImport = $localStorage->getUid();
1487  break;
1488  }
1489  }
1490  }
1491  if ($useThisStorageUidInsteadOfTheOneInImport > 0) {
1492  // same storage is already present; map the to be imported one to the present one
1493  $this->import_mapId['sys_file_storage'][$sysFileStorageUid] = $useThisStorageUidInsteadOfTheOneInImport;
1494  } else {
1495  // Local, writable and online storage. Is allowed to be used to later write files in.
1496  $this->addSingle('sys_file_storage', $sysFileStorageUid, 0);
1497  }
1498  } else {
1499  // Storage with non Local drivers could be imported but must not be used to saves files in, because you
1500  // could not be sure, that this is supported. The default storage will be used in this case.
1501  // It could happen that non writable and non online storage will be created as dupes because you could not
1502  // check the detailed configuration options at this point
1503  $this->addSingle('sys_file_storage', $sysFileStorageUid, 0);
1504  $sysFileStorageUidsToBeResetToDefaultStorage[] = $sysFileStorageUid;
1505  }
1506 
1507  }
1508 
1509  // Importing the added ones
1510  $tce = $this->getNewTCE();
1511  // Because all records are being submitted in their correct order with positive pid numbers - and so we should reverse submission order internally.
1512  $tce->reverseOrder = 1;
1513  $tce->isImporting = TRUE;
1514  $tce->start($this->import_data, array());
1515  $tce->process_datamap();
1516  $this->addToMapId($tce->substNEWwithIDs);
1517 
1518  $defaultStorageUid = NULL;
1519  // get default storage
1520  $defaultStorage = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getDefaultStorage();
1521  if ($defaultStorage !== NULL) {
1522  $defaultStorageUid = $defaultStorage->getUid();
1523  }
1524  foreach ($sysFileStorageUidsToBeResetToDefaultStorage as $sysFileStorageUidToBeResetToDefaultStorage) {
1525  $this->import_mapId['sys_file_storage'][$sysFileStorageUidToBeResetToDefaultStorage] = $defaultStorageUid;
1526  }
1527 
1528  // unset the sys_file_storage records to prevent a import in writeRecords_records
1529  unset($this->dat['header']['records']['sys_file_storage']);
1530  }
1531 
1537  protected function writeSysFileRecords() {
1538  if (!isset($this->dat['header']['records']['sys_file'])) {
1539  return;
1540  }
1541  $this->addGeneralErrorsByTable('sys_file');
1542 
1543  // fetch fresh storage records from database
1544  $storageRecords = $this->fetchStorageRecords();
1545 
1546  $defaultStorage = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getDefaultStorage();
1547 
1548  $sanitizedFolderMappings = array();
1549 
1550  foreach ($this->dat['header']['records']['sys_file'] as $sysFileUid => $_) {
1551  $fileRecord = $this->dat['records']['sys_file:' . $sysFileUid]['data'];
1552 
1553  // save file to disk
1554  $fileId = md5($fileRecord['storage'] . ':' . $fileRecord['identifier_hash']);
1555  $temporaryFile = $this->writeTemporaryFileFromData($fileId);
1556  if ($temporaryFile === NULL) {
1557  // error on writing the file. Error message was already added
1558  continue;
1559  }
1560 
1561  $originalStorageUid = $fileRecord['storage'];
1562  $useStorageFromStorageRecords = FALSE;
1563 
1564  // replace storage id, if a alternative one was registered
1565  if (isset($this->import_mapId['sys_file_storage'][$fileRecord['storage']])) {
1566  $fileRecord['storage'] = $this->import_mapId['sys_file_storage'][$fileRecord['storage']];
1567  $useStorageFromStorageRecords = TRUE;
1568  }
1569 
1570  if (empty($fileRecord['storage']) && !$this->isFallbackStorage($fileRecord['storage'])) {
1571  // no storage for the file is defined, mostly because of a missing default storage.
1572  $this->error('Error: No storage for the file "' . $fileRecord['identifier'] . '" with storage uid "' . $originalStorageUid . '"');
1573  continue;
1574  }
1575 
1576  // using a storage from the local storage is only allowed, if the uid is present in the
1577  // mapping. Only in this case we could be sure, that it's a local, online and writable storage.
1578  if ($useStorageFromStorageRecords && isset($storageRecords[$fileRecord['storage']])) {
1580  $storage = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getStorageObject($fileRecord['storage'], $storageRecords[$fileRecord['storage']]);
1581  } elseif ($this->isFallbackStorage($fileRecord['storage'])) {
1582  $storage = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getStorageObject(0);
1583  } elseif ($defaultStorage !== NULL) {
1584  $storage = $defaultStorage;
1585  } else {
1586  $this->error('Error: No storage available for the file "' . $fileRecord['identifier'] . '" with storage uid "' . $fileRecord['storage'] . '"');
1587  continue;
1588  }
1589 
1590  $newFile = NULL;
1591 
1592  // check, if there is a identical file
1593  try {
1594  if ($storage->hasFile($fileRecord['identifier'])) {
1595  $file = $storage->getFile($fileRecord['identifier']);
1596  if ($file->getSha1() === $fileRecord['sha1']) {
1597  $newFile = $file;
1598  }
1599  }
1600  } catch (Exception $e) {}
1601 
1602  if ($newFile === NULL) {
1603 
1604  $folderName = PathUtility::dirname(ltrim($fileRecord['identifier'], '/'));
1605  if (in_array($folderName, $sanitizedFolderMappings)) {
1606  $folderName = $sanitizedFolderMappings[$folderName];
1607  }
1608  if (!$storage->hasFolder($folderName)) {
1609  try {
1610  $importFolder = $storage->createFolder($folderName);
1611  if ($importFolder->getIdentifier() !== $folderName && !in_array($folderName, $sanitizedFolderMappings)) {
1612  $sanitizedFolderMappings[$folderName] = $importFolder->getIdentifier();
1613  }
1614  } catch (Exception $e) {
1615  $this->error('Error: Folder could not be created for file "' . $fileRecord['identifier'] . '" with storage uid "' . $fileRecord['storage'] . '"');
1616  continue;
1617  }
1618  } else {
1619  $importFolder = $storage->getFolder($folderName);
1620  }
1621 
1622  try {
1624  $newFile = $storage->addFile($temporaryFile, $importFolder, $fileRecord['name']);
1625  } catch (Exception $e) {
1626  $this->error('Error: File could not be added to the storage: "' . $fileRecord['identifier'] . '" with storage uid "' . $fileRecord['storage'] . '"');
1627  continue;
1628  }
1629 
1630  if ($newFile->getSha1() !== $fileRecord['sha1']) {
1631  $this->error('Error: The hash of the written file is not identical to the import data! File could be corrupted! File: "' . $fileRecord['identifier'] . '" with storage uid "' . $fileRecord['storage'] . '"');
1632  }
1633  }
1634 
1635  // save the new uid in the import id map
1636  $this->import_mapId['sys_file'][$fileRecord['uid']] = $newFile->getUid();
1637  $this->fixUidLocalInSysFileReferenceRecords($fileRecord['uid'], $newFile->getUid());
1638 
1639  }
1640 
1641  // unset the sys_file records to prevent a import in writeRecords_records
1642  unset($this->dat['header']['records']['sys_file']);
1643  // remove all sys_file_reference records that point to file records which are unknown
1644  // in the system to prevent exceptions
1646  }
1647 
1655  {
1656  if (!isset($this->dat['header']['records']['sys_file_reference'])) {
1657  return;
1658  }
1659 
1660  foreach ($this->dat['header']['records']['sys_file_reference'] as $sysFileReferenceUid => $_) {
1661  $fileReferenceRecord = $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data'];
1662  if (!in_array($fileReferenceRecord['uid_local'], $this->import_mapId['sys_file'])) {
1663  unset($this->dat['header']['records']['sys_file_reference'][$sysFileReferenceUid]);
1664  unset($this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]);
1665  $this->error('Error: sys_file_reference record ' . (int)$sysFileReferenceUid
1666  . ' with relation to sys_file record ' . (int)$fileReferenceRecord['uid_local']
1667  . ', which is not part of the import data, was not imported.'
1668  );
1669  }
1670  }
1671  }
1672 
1679  protected function isFallbackStorage($storageId) {
1680  return $storageId === 0 || $storageId === '0';
1681  }
1682 
1699  protected function fixUidLocalInSysFileReferenceRecords($oldFileUid, $newFileUid) {
1700  if (!isset($this->dat['header']['records']['sys_file_reference'])) {
1701  return;
1702  }
1703 
1704  foreach ($this->dat['header']['records']['sys_file_reference'] as $sysFileReferenceUid => $_) {
1705  $fileReferenceRecord = $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data'];
1706  if ($fileReferenceRecord['uid_local'] == $oldFileUid) {
1707  $fileReferenceRecord['uid_local'] = $newFileUid;
1708  $this->dat['records']['sys_file_reference:' . $sysFileReferenceUid]['data'] = $fileReferenceRecord;
1709  }
1710  }
1711  }
1712 
1718  protected function initializeLegacyImportFolder() {
1720  $folder = $GLOBALS['BE_USER']->getDefaultUploadFolder();
1721  if ($folder === FALSE) {
1722  $this->error('Error: the backend users default upload folder is missing! No files will be imported!');
1723  }
1724  if (!$folder->hasFolder($this->legacyImportTargetPath)) {
1725  try {
1726  $this->legacyImportFolder = $folder->createFolder($this->legacyImportTargetPath);
1727  } catch (\TYPO3\CMS\Core\Exception $e) {
1728  $this->error('Error: the import folder in the default upload folder could not be created! No files will be imported!');
1729  }
1730  } else {
1731  $this->legacyImportFolder = $folder->getSubfolder($this->legacyImportTargetPath);
1732  }
1733 
1734  }
1735 
1742  protected function fetchStorageRecords() {
1743  $whereClause = \TYPO3\CMS\Backend\Utility\BackendUtility::BEenableFields('sys_file_storage');
1744  $whereClause .= \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause('sys_file_storage');
1745 
1746  $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1747  '*',
1748  'sys_file_storage',
1749  '1=1' . $whereClause,
1750  '',
1751  '',
1752  '',
1753  'uid'
1754  );
1755 
1756  return $rows;
1757  }
1758 
1766  protected function writeTemporaryFileFromData($fileId, $dataKey = 'files_fal') {
1767  $temporaryFilePath = NULL;
1768  if (is_array($this->dat[$dataKey][$fileId])) {
1769  $temporaryFilePathInternal = GeneralUtility::tempnam('import_temp_');
1770  GeneralUtility::writeFile($temporaryFilePathInternal, $this->dat[$dataKey][$fileId]['content']);
1771  clearstatcache();
1772  if (@is_file($temporaryFilePathInternal)) {
1773  $this->unlinkFiles[] = $temporaryFilePathInternal;
1774  if (filesize($temporaryFilePathInternal) == $this->dat[$dataKey][$fileId]['filesize']) {
1775  $temporaryFilePath = $temporaryFilePathInternal;
1776  } else {
1777  $this->error('Error: temporary file ' . $temporaryFilePathInternal . ' had a size (' . filesize($temporaryFilePathInternal) . ') different from the original (' . $this->dat[$dataKey][$fileId]['filesize'] . ')', 1);
1778  }
1779  } else {
1780  $this->error('Error: temporary file ' . $temporaryFilePathInternal . ' was not written as it should have been!', 1);
1781  }
1782  } else {
1783  $this->error('Error: No file found for ID ' . $fileId, 1);
1784  }
1785  return $temporaryFilePath;
1786  }
1787 
1796  public function writeRecords_pages($pid) {
1797  // First, write page structure if any:
1798  if (is_array($this->dat['header']['records']['pages'])) {
1799  $this->addGeneralErrorsByTable('pages');
1800  // $pageRecords is a copy of the pages array in the imported file. Records here are unset one by one when the addSingle function is called.
1801  $pageRecords = $this->dat['header']['records']['pages'];
1802  $this->import_data = array();
1803  // First add page tree if any
1804  if (is_array($this->dat['header']['pagetree'])) {
1805  $pagesFromTree = $this->flatInversePageTree($this->dat['header']['pagetree']);
1806  foreach ($pagesFromTree as $uid) {
1807  $thisRec = $this->dat['header']['records']['pages'][$uid];
1808  // PID: Set the main $pid, unless a NEW-id is found
1809  $setPid = isset($this->import_newId_pids[$thisRec['pid']]) ? $this->import_newId_pids[$thisRec['pid']] : $pid;
1810  $this->addSingle('pages', $uid, $setPid);
1811  unset($pageRecords[$uid]);
1812  }
1813  }
1814  // Then add all remaining pages not in tree on root level:
1815  if (count($pageRecords)) {
1816  $remainingPageUids = array_keys($pageRecords);
1817  foreach ($remainingPageUids as $pUid) {
1818  $this->addSingle('pages', $pUid, $pid);
1819  }
1820  }
1821  // Now write to database:
1822  $tce = $this->getNewTCE();
1823  $tce->isImporting = TRUE;
1824  $this->callHook('before_writeRecordsPages', array(
1825  'tce' => &$tce,
1826  'data' => &$this->import_data
1827  ));
1828  $tce->suggestedInsertUids = $this->suggestedInsertUids;
1829  $tce->start($this->import_data, array());
1830  $tce->process_datamap();
1831  $this->callHook('after_writeRecordsPages', array(
1832  'tce' => &$tce
1833  ));
1834  // post-processing: Registering new ids (end all tcemain sessions with this)
1835  $this->addToMapId($tce->substNEWwithIDs);
1836  // In case of an update, order pages from the page tree correctly:
1837  if ($this->update && is_array($this->dat['header']['pagetree'])) {
1838  $this->writeRecords_pages_order($pid);
1839  }
1840  }
1841  }
1842 
1853  public function writeRecords_pages_order($pid) {
1854  $cmd_data = array();
1855  // Get uid-pid relations and traverse them in order to map to possible new IDs
1856  $pidsFromTree = $this->flatInversePageTree_pid($this->dat['header']['pagetree']);
1857  foreach ($pidsFromTree as $origPid => $newPid) {
1858  if ($newPid >= 0 && $this->dontIgnorePid('pages', $origPid)) {
1859  // If the page had a new id (because it was created) use that instead!
1860  if (substr($this->import_newId_pids[$origPid], 0, 3) === 'NEW') {
1861  if ($this->import_mapId['pages'][$origPid]) {
1862  $mappedPid = $this->import_mapId['pages'][$origPid];
1863  $cmd_data['pages'][$mappedPid]['move'] = $newPid;
1864  }
1865  } else {
1866  $cmd_data['pages'][$origPid]['move'] = $newPid;
1867  }
1868  }
1869  }
1870  // Execute the move commands if any:
1871  if (count($cmd_data)) {
1872  $tce = $this->getNewTCE();
1873  $this->callHook('before_writeRecordsPagesOrder', array(
1874  'tce' => &$tce,
1875  'data' => &$cmd_data
1876  ));
1877  $tce->start(array(), $cmd_data);
1878  $tce->process_cmdmap();
1879  $this->callHook('after_writeRecordsPagesOrder', array(
1880  'tce' => &$tce
1881  ));
1882  }
1883  }
1884 
1893  public function writeRecords_records($pid) {
1894  // Write the rest of the records
1895  $this->import_data = array();
1896  if (is_array($this->dat['header']['records'])) {
1897  foreach ($this->dat['header']['records'] as $table => $recs) {
1898  $this->addGeneralErrorsByTable($table);
1899  if ($table != 'pages') {
1900  foreach ($recs as $uid => $thisRec) {
1901  // PID: Set the main $pid, unless a NEW-id is found
1902  $setPid = isset($this->import_mapId['pages'][$thisRec['pid']])
1903  ? (int)$this->import_mapId['pages'][$thisRec['pid']]
1904  : (int)$pid;
1905  if (is_array($GLOBALS['TCA'][$table]) && isset($GLOBALS['TCA'][$table]['ctrl']['rootLevel'])) {
1906  $rootLevelSetting = (int)$GLOBALS['TCA'][$table]['ctrl']['rootLevel'];
1907  if ($rootLevelSetting === 1) {
1908  $setPid = 0;
1909  } elseif ($rootLevelSetting === 0 && $setPid === 0) {
1910  $this->error('Error: Record type ' . $table . ' is not allowed on pid 0');
1911  continue;
1912  }
1913  }
1914  // Add record:
1915  $this->addSingle($table, $uid, $setPid);
1916  }
1917  }
1918  }
1919  } else {
1920  $this->error('Error: No records defined in internal data array.');
1921  }
1922  // Now write to database:
1923  $tce = $this->getNewTCE();
1924  $this->callHook('before_writeRecordsRecords', array(
1925  'tce' => &$tce,
1926  'data' => &$this->import_data
1927  ));
1928  $tce->suggestedInsertUids = $this->suggestedInsertUids;
1929  // Because all records are being submitted in their correct order with positive pid numbers - and so we should reverse submission order internally.
1930  $tce->reverseOrder = 1;
1931  $tce->isImporting = TRUE;
1932  $tce->start($this->import_data, array());
1933  $tce->process_datamap();
1934  $this->callHook('after_writeRecordsRecords', array(
1935  'tce' => &$tce
1936  ));
1937  // post-processing: Removing files and registering new ids (end all tcemain sessions with this)
1938  $this->addToMapId($tce->substNEWwithIDs);
1939  // In case of an update, order pages from the page tree correctly:
1940  if ($this->update) {
1941  $this->writeRecords_records_order($pid);
1942  }
1943  }
1944 
1955  public function writeRecords_records_order($mainPid) {
1956  $cmd_data = array();
1957  if (is_array($this->dat['header']['pagetree'])) {
1958  $pagesFromTree = $this->flatInversePageTree($this->dat['header']['pagetree']);
1959  } else {
1960  $pagesFromTree = array();
1961  }
1962  if (is_array($this->dat['header']['pid_lookup'])) {
1963  foreach ($this->dat['header']['pid_lookup'] as $pid => $recList) {
1964  $newPid = isset($this->import_mapId['pages'][$pid]) ? $this->import_mapId['pages'][$pid] : $mainPid;
1965  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($newPid)) {
1966  foreach ($recList as $tableName => $uidList) {
1967  // If $mainPid===$newPid then we are on root level and we can consider to move pages as well!
1968  // (they will not be in the page tree!)
1969  if (($tableName != 'pages' || !$pagesFromTree[$pid]) && is_array($uidList)) {
1970  $uidList = array_reverse(array_keys($uidList));
1971  foreach ($uidList as $uid) {
1972  if ($this->dontIgnorePid($tableName, $uid)) {
1973  $cmd_data[$tableName][$uid]['move'] = $newPid;
1974  } else {
1975 
1976  }
1977  }
1978  }
1979  }
1980  }
1981  }
1982  }
1983  // Execute the move commands if any:
1984  if (count($cmd_data)) {
1985  $tce = $this->getNewTCE();
1986  $this->callHook('before_writeRecordsRecordsOrder', array(
1987  'tce' => &$tce,
1988  'data' => &$cmd_data
1989  ));
1990  $tce->start(array(), $cmd_data);
1991  $tce->process_cmdmap();
1992  $this->callHook('after_writeRecordsRecordsOrder', array(
1993  'tce' => &$tce
1994  ));
1995  }
1996  }
1997 
2009  public function addSingle($table, $uid, $pid) {
2010  if ($this->import_mode[$table . ':' . $uid] !== 'exclude') {
2011  $record = $this->dat['records'][$table . ':' . $uid]['data'];
2012  if (is_array($record)) {
2013  if ($this->update && $this->doesRecordExist($table, $uid) && $this->import_mode[$table . ':' . $uid] !== 'as_new') {
2014  $ID = $uid;
2015  } elseif ($table === 'sys_file_metadata' && $record['sys_language_uid'] == '0' && $this->import_mapId['sys_file'][$record['file']]) {
2016  // on adding sys_file records the belonging sys_file_metadata record was also created
2017  // if there is one the record need to be overwritten instead of creating a new one.
2018  $recordInDatabase = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
2019  'uid',
2020  'sys_file_metadata',
2021  'file = ' . $this->import_mapId['sys_file'][$record['file']] . ' AND sys_language_uid = 0 AND pid = 0'
2022  );
2023  // if no record could be found, $this->import_mapId['sys_file'][$record['file']] is pointing
2024  // to a file, that was already there, thus a new metadata record should be created
2025  if (is_array($recordInDatabase)) {
2026  $this->import_mapId['sys_file_metadata'][$record['uid']] = $recordInDatabase['uid'];
2027  $ID = $recordInDatabase['uid'];
2028  } else {
2029  $ID = uniqid('NEW', TRUE);
2030  }
2031 
2032  } else {
2033  $ID = uniqid('NEW', TRUE);
2034  }
2035  $this->import_newId[$table . ':' . $ID] = array('table' => $table, 'uid' => $uid);
2036  if ($table == 'pages') {
2037  $this->import_newId_pids[$uid] = $ID;
2038  }
2039  // Set main record data:
2040  $this->import_data[$table][$ID] = $record;
2041  $this->import_data[$table][$ID]['tx_impexp_origuid'] = $this->import_data[$table][$ID]['uid'];
2042  // Reset permission data:
2043  if ($table === 'pages') {
2044  // Have to reset the user/group IDs so pages are owned by importing user. Otherwise strange things may happen for non-admins!
2045  unset($this->import_data[$table][$ID]['perms_userid']);
2046  unset($this->import_data[$table][$ID]['perms_groupid']);
2047  }
2048  // PID and UID:
2049  unset($this->import_data[$table][$ID]['uid']);
2050  // Updates:
2051  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($ID)) {
2052  unset($this->import_data[$table][$ID]['pid']);
2053  } else {
2054  // Inserts:
2055  $this->import_data[$table][$ID]['pid'] = $pid;
2056  if (($this->import_mode[$table . ':' . $uid] === 'force_uid' && $this->update || $this->force_all_UIDS) && $GLOBALS['BE_USER']->isAdmin()) {
2057  $this->import_data[$table][$ID]['uid'] = $uid;
2058  $this->suggestedInsertUids[$table . ':' . $uid] = 'DELETE';
2059  }
2060  }
2061  // Setting db/file blank:
2062  foreach ($this->dat['records'][$table . ':' . $uid]['rels'] as $field => $config) {
2063  switch ((string) $config['type']) {
2064  case 'db':
2065 
2066  case 'file':
2067  // Fixed later in ->setRelations() [because we need to know ALL newly created IDs before we can map relations!]
2068  // In the meantime we set NO values for relations.
2069  //
2070  // BUT for field uid_local of table sys_file_reference the relation MUST not be cleared here,
2071  // because the value is already the uid of the right imported sys_file record.
2072  // @see fixUidLocalInSysFileReferenceRecords()
2073  // If it's empty or a uid to another record the FileExtensionFilter will throw an exception or
2074  // delete the reference record if the file extension of the related record doesn't match.
2075  if ($table !== 'sys_file_reference' && $field !== 'uid_local') {
2076  $this->import_data[$table][$ID][$field] = '';
2077  }
2078  break;
2079  case 'flex':
2080  // Fixed later in setFlexFormRelations()
2081  // In the meantime we set NO value for flexforms - this is mainly because file references inside will not be processed properly; In fact references will point to no file or existing files (in which case there will be double-references which is a big problem of course!)
2082  $this->import_data[$table][$ID][$field] = '';
2083  break;
2084  }
2085  }
2086  } elseif ($table . ':' . $uid != 'pages:0') {
2087  // On root level we don't want this error message.
2088  $this->error('Error: no record was found in data array!', 1);
2089  }
2090  }
2091  }
2092 
2101  public function addToMapId($substNEWwithIDs) {
2102  foreach ($this->import_data as $table => $recs) {
2103  foreach ($recs as $id => $value) {
2104  $old_uid = $this->import_newId[$table . ':' . $id]['uid'];
2105  if (isset($substNEWwithIDs[$id])) {
2106  $this->import_mapId[$table][$old_uid] = $substNEWwithIDs[$id];
2107  } elseif ($this->update) {
2108  // Map same ID to same ID....
2109  $this->import_mapId[$table][$old_uid] = $id;
2110  } else {
2111  // if $this->import_mapId contains already the right mapping, skip the error msg. See special handling of sys_file_metadata in addSingle() => nothing to do
2112  if (!($table === 'sys_file_metadata' && isset($this->import_mapId[$table][$old_uid]) && $this->import_mapId[$table][$old_uid] == $id)) {
2113  $this->error('Possible error: ' . $table . ':' . $old_uid . ' had no new id assigned to it. This indicates that the record was not added to database during import. Please check changelog!', 1);
2114  }
2115 
2116  }
2117  }
2118  }
2119  }
2120 
2127  public function getNewTCE() {
2128  $tce = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
2129  $tce->stripslashes_values = 0;
2130  $tce->dontProcessTransformations = 1;
2131  $tce->enableLogging = $this->enableLogging;
2132  $tce->alternativeFileName = $this->alternativeFileName;
2133  $tce->alternativeFilePath = $this->alternativeFilePath;
2134  return $tce;
2135  }
2136 
2143  public function unlinkTempFiles() {
2144  foreach ($this->unlinkFiles as $fileName) {
2145  if (GeneralUtility::isFirstPartOfStr($fileName, PATH_site . 'typo3temp/')) {
2147  clearstatcache();
2148  if (is_file($fileName)) {
2149  $this->error('Error: ' . $fileName . ' was NOT unlinked as it should have been!', 1);
2150  }
2151  } else {
2152  $this->error('Error: ' . $fileName . ' was not in temp-path. Not removed!', 1);
2153  }
2154  }
2155  $this->unlinkFiles = array();
2156  }
2157 
2158  /***************************
2159  * Import / Relations setting
2160  ***************************/
2161 
2171  public function setRelations() {
2172  $updateData = array();
2173  // import_newId contains a register of all records that was in the import memorys "records" key
2174  foreach ($this->import_newId as $nId => $dat) {
2175  $table = $dat['table'];
2176  $uid = $dat['uid'];
2177  // original UID - NOT the new one!
2178  // If the record has been written and received a new id, then proceed:
2179  if (is_array($this->import_mapId[$table]) && isset($this->import_mapId[$table][$uid])) {
2180  $thisNewUid = BackendUtility::wsMapId($table, $this->import_mapId[$table][$uid]);
2181  if (is_array($this->dat['records'][$table . ':' . $uid]['rels'])) {
2182  if ($this->legacyImport) {
2183  if ($table != 'pages') {
2184  $oldPid = $this->dat['records'][$table . ':' . $uid]['data']['pid'];
2185  $thisNewPageUid = \TYPO3\CMS\Backend\Utility\BackendUtility::wsMapId($table, $this->import_mapId['pages'][$oldPid]);
2186  } else {
2187  $thisNewPageUid = $thisNewUid;
2188  }
2189  }
2190  // Traverse relation fields of each record
2191  foreach ($this->dat['records'][$table . ':' . $uid]['rels'] as $field => $config) {
2192  // uid_local of sys_file_reference needs no update because the correct reference uid was already written
2193  // @see ImportExport::fixUidLocalInSysFileReferenceRecords()
2194  if ($table === 'sys_file_reference' && $field === 'uid_local') {
2195  continue;
2196  }
2197  switch ((string) $config['type']) {
2198  case 'db':
2199  if (is_array($config['itemArray']) && count($config['itemArray'])) {
2200  $itemConfig = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
2201  $valArray = $this->setRelations_db($config['itemArray'], $itemConfig);
2202  $updateData[$table][$thisNewUid][$field] = implode(',', $valArray);
2203  }
2204  break;
2205  case 'file':
2206  if (is_array($config['newValueFiles']) && count($config['newValueFiles'])) {
2207  $valArr = array();
2208  foreach ($config['newValueFiles'] as $fI) {
2209  $valArr[] = $this->import_addFileNameToBeCopied($fI);
2210  }
2211  if ($this->legacyImport && $this->legacyImportFolder === NULL && isset($this->legacyImportMigrationTables[$table][$field])) {
2212  // Do nothing - the legacy import folder is missing
2213  } elseif ($this->legacyImport && $this->legacyImportFolder !== NULL && isset($this->legacyImportMigrationTables[$table][$field])) {
2214  $refIds = array();
2215  foreach ($valArr as $tempFile) {
2216  $fileName = $this->alternativeFileName[$tempFile];
2217  $fileObject = NULL;
2218 
2219  try {
2220  // check, if there is alreay the same file in the folder
2221  if ($this->legacyImportFolder->hasFile($fileName)) {
2222  $fileStorage = $this->legacyImportFolder->getStorage();
2223  $file = $fileStorage->getFile($this->legacyImportFolder->getIdentifier() . $fileName);
2224  if ($file->getSha1() === sha1_file($tempFile)) {
2225  $fileObject = $file;
2226  }
2227  }
2228  } catch (Exception $e) {}
2229 
2230  if ($fileObject === NULL) {
2231  try {
2232  $fileObject = $this->legacyImportFolder->addFile($tempFile, $fileName, 'changeName');
2233  } catch (\TYPO3\CMS\Core\Exception $e) {
2234  $this->error('Error: no file could be added to the storage for file name' . $this->alternativeFileName[$tempFile]);
2235  }
2236  }
2237  if ($fileObject !== NULL) {
2238  $refId = uniqid('NEW', TRUE);
2239  $refIds[] = $refId;
2240  $updateData['sys_file_reference'][$refId] = array(
2241  'uid_local' => $fileObject->getUid(),
2242  'uid_foreign' => $thisNewUid, // uid of your content record
2243  'tablenames' => $table,
2244  'fieldname' => $field,
2245  'pid' => $thisNewPageUid, // parent id of the parent page
2246  'table_local' => 'sys_file',
2247  );
2248  }
2249  }
2250  $updateData[$table][$thisNewUid][$field] = implode(',', $refIds);
2251  if (!empty($this->legacyImportMigrationTables[$table][$field])) {
2252  $this->legacyImportMigrationRecords[$table][$thisNewUid][$field] = $refIds;
2253  }
2254  } else {
2255  $updateData[$table][$thisNewUid][$field] = implode(',', $valArr);
2256  }
2257  }
2258  break;
2259  }
2260  }
2261  } else {
2262  $this->error('Error: no record was found in data array!', 1);
2263  }
2264  } else {
2265  $this->error('Error: this records is NOT created it seems! (' . $table . ':' . $uid . ')', 1);
2266  }
2267  }
2268  if (count($updateData)) {
2269  $tce = $this->getNewTCE();
2270  $tce->isImporting = TRUE;
2271  $this->callHook('before_setRelation', array(
2272  'tce' => &$tce,
2273  'data' => &$updateData
2274  ));
2275  $tce->start($updateData, array());
2276  $tce->process_datamap();
2277  // Replace the temporary "NEW" ids with the final ones.
2278  foreach ($this->legacyImportMigrationRecords as $table => $records) {
2279  foreach ($records as $uid => $fields) {
2280  foreach ($fields as $field => $referenceIds) {
2281  foreach ($referenceIds as $key => $referenceId) {
2282  $this->legacyImportMigrationRecords[$table][$uid][$field][$key] = $tce->substNEWwithIDs[$referenceId];
2283  }
2284  }
2285  }
2286  }
2287  $this->callHook('after_setRelations', array(
2288  'tce' => &$tce
2289  ));
2290  }
2291  }
2292 
2301  public function setRelations_db($itemArray, $itemConfig) {
2302  $valArray = array();
2303  foreach ($itemArray as $relDat) {
2304  if (is_array($this->import_mapId[$relDat['table']]) && isset($this->import_mapId[$relDat['table']][$relDat['id']])) {
2305  // Since non FAL file relation type group internal_type file_reference are handled as reference to
2306  // sys_file records Datahandler requires the value as uid of the the related sys_file record only
2307  if ($itemConfig['type'] === 'group' && $itemConfig['internal_type'] === 'file_reference') {
2308  $value = $this->import_mapId[$relDat['table']][$relDat['id']];
2309  } elseif ($itemConfig['type'] === 'input' && isset($itemConfig['wizards']['link'])) {
2310  // If an input field has a relation to a sys_file record this need to be converted back to
2311  // the public path. But use getPublicUrl here, because could normally only be a local file path.
2312  $fileUid = $this->import_mapId[$relDat['table']][$relDat['id']];
2313  // Fallback value
2314  $value = 'file:' . $fileUid;
2315  try {
2316  $file = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->retrieveFileOrFolderObject($fileUid);
2317  } catch (\Exception $e) {}
2318  if ($file instanceof \TYPO3\CMS\Core\Resource\FileInterface) {
2319  $value = $file->getPublicUrl();
2320  }
2321  } else {
2322  $value = $relDat['table'] . '_' . $this->import_mapId[$relDat['table']][$relDat['id']];
2323  }
2324  $valArray[] = $value;
2325  } elseif ($this->isTableStatic($relDat['table']) || $this->isExcluded($relDat['table'], $relDat['id']) || $relDat['id'] < 0) {
2326  // Checking for less than zero because some select types could contain negative values, eg. fe_groups (-1, -2) and sys_language (-1 = ALL languages). This must be handled on both export and import.
2327  $valArray[] = $relDat['table'] . '_' . $relDat['id'];
2328  } else {
2329  $this->error('Lost relation: ' . $relDat['table'] . ':' . $relDat['id'], 1);
2330  }
2331  }
2332  return $valArray;
2333  }
2334 
2342  public function import_addFileNameToBeCopied($fI) {
2343  if (is_array($this->dat['files'][$fI['ID']])) {
2344  $tmpFile = GeneralUtility::tempnam('import_temp_');
2345  GeneralUtility::writeFile($tmpFile, $this->dat['files'][$fI['ID']]['content']);
2346  clearstatcache();
2347  if (@is_file($tmpFile)) {
2348  $this->unlinkFiles[] = $tmpFile;
2349  if (filesize($tmpFile) == $this->dat['files'][$fI['ID']]['filesize']) {
2350  $this->alternativeFileName[$tmpFile] = $fI['filename'];
2351  $this->alternativeFilePath[$tmpFile] = $this->dat['files'][$fI['ID']]['relFileRef'];
2352  return $tmpFile;
2353  } else {
2354  $this->error('Error: temporary file ' . $tmpFile . ' had a size (' . filesize($tmpFile) . ') different from the original (' . $this->dat['files'][$fI['ID']]['filesize'] . ')', 1);
2355  }
2356  } else {
2357  $this->error('Error: temporary file ' . $tmpFile . ' was not written as it should have been!', 1);
2358  }
2359  } else {
2360  $this->error('Error: No file found for ID ' . $fI['ID'], 1);
2361  }
2362  }
2363 
2372  public function setFlexFormRelations() {
2373  $updateData = array();
2374  // import_newId contains a register of all records that was in the import memorys "records" key
2375  foreach ($this->import_newId as $nId => $dat) {
2376  $table = $dat['table'];
2377  $uid = $dat['uid'];
2378  // original UID - NOT the new one!
2379  // If the record has been written and received a new id, then proceed:
2380  if (is_array($this->import_mapId[$table]) && isset($this->import_mapId[$table][$uid])) {
2381  $thisNewUid = BackendUtility::wsMapId($table, $this->import_mapId[$table][$uid]);
2382  if (is_array($this->dat['records'][$table . ':' . $uid]['rels'])) {
2383  // Traverse relation fields of each record
2384  foreach ($this->dat['records'][$table . ':' . $uid]['rels'] as $field => $config) {
2385  switch ((string) $config['type']) {
2386  case 'flex':
2387  // Get XML content and set as default value (string, non-processed):
2388  $updateData[$table][$thisNewUid][$field] = $this->dat['records'][$table . ':' . $uid]['data'][$field];
2389  // If there has been registered relations inside the flex form field, run processing on the content:
2390  if (count($config['flexFormRels']['db']) || count($config['flexFormRels']['file'])) {
2391  $origRecordRow = BackendUtility::getRecord($table, $thisNewUid, '*');
2392  // This will fetch the new row for the element (which should be updated with any references to data structures etc.)
2393  $conf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
2394  if (is_array($origRecordRow) && is_array($conf) && $conf['type'] === 'flex') {
2395  // Get current data structure and value array:
2396  $dataStructArray = BackendUtility::getFlexFormDS($conf, $origRecordRow, $table, $field);
2397  $currentValueArray = GeneralUtility::xml2array($updateData[$table][$thisNewUid][$field]);
2398  // Do recursive processing of the XML data:
2399  $iteratorObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
2400  $iteratorObj->callBackObj = $this;
2401  $currentValueArray['data'] = $iteratorObj->checkValue_flex_procInData($currentValueArray['data'], array(), array(), $dataStructArray, array($table, $thisNewUid, $field, $config), 'remapListedDBRecords_flexFormCallBack');
2402  // The return value is set as an array which means it will be processed by tcemain for file and DB references!
2403  if (is_array($currentValueArray['data'])) {
2404  $updateData[$table][$thisNewUid][$field] = $currentValueArray;
2405  }
2406  }
2407  }
2408  break;
2409  }
2410  }
2411  } else {
2412  $this->error('Error: no record was found in data array!', 1);
2413  }
2414  } else {
2415  $this->error('Error: this records is NOT created it seems! (' . $table . ':' . $uid . ')', 1);
2416  }
2417  }
2418  if (count($updateData)) {
2419  $tce = $this->getNewTCE();
2420  $tce->isImporting = TRUE;
2421  $this->callHook('before_setFlexFormRelations', array(
2422  'tce' => &$tce,
2423  'data' => &$updateData
2424  ));
2425  $tce->start($updateData, array());
2426  $tce->process_datamap();
2427  $this->callHook('after_setFlexFormRelations', array(
2428  'tce' => &$tce
2429  ));
2430  }
2431  }
2432 
2446  public function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) {
2447  // Extract parameters:
2448  list($table, $uid, $field, $config) = $pParams;
2449  // In case the $path is used as index without a trailing slash we will remove that
2450  if (!is_array($config['flexFormRels']['db'][$path]) && is_array($config['flexFormRels']['db'][rtrim($path, '/')])) {
2451  $path = rtrim($path, '/');
2452  }
2453  if (is_array($config['flexFormRels']['db'][$path])) {
2454  $valArray = $this->setRelations_db($config['flexFormRels']['db'][$path], $dsConf);
2455  $dataValue = implode(',', $valArray);
2456  }
2457  if (is_array($config['flexFormRels']['file'][$path])) {
2458  foreach ($config['flexFormRels']['file'][$path] as $fI) {
2459  $valArr[] = $this->import_addFileNameToBeCopied($fI);
2460  }
2461  $dataValue = implode(',', $valArr);
2462  }
2463  return array('value' => $dataValue);
2464  }
2465 
2466  /**************************
2467  * Import / Soft References
2468  *************************/
2469 
2476  public function processSoftReferences() {
2477  // Initialize:
2478  $inData = array();
2479  // Traverse records:
2480  if (is_array($this->dat['header']['records'])) {
2481  foreach ($this->dat['header']['records'] as $table => $recs) {
2482  foreach ($recs as $uid => $thisRec) {
2483  // If there are soft references defined, traverse those:
2484  if (isset($GLOBALS['TCA'][$table]) && is_array($thisRec['softrefs'])) {
2485  // First traversal is to collect softref configuration and split them up based on fields. This could probably also have been done with the "records" key instead of the header.
2486  $fieldsIndex = array();
2487  foreach ($thisRec['softrefs'] as $softrefDef) {
2488  // If a substitution token is set:
2489  if ($softrefDef['field'] && is_array($softrefDef['subst']) && $softrefDef['subst']['tokenID']) {
2490  $fieldsIndex[$softrefDef['field']][$softrefDef['subst']['tokenID']] = $softrefDef;
2491  }
2492  }
2493  // The new id:
2494  $thisNewUid = BackendUtility::wsMapId($table, $this->import_mapId[$table][$uid]);
2495  // Now, if there are any fields that require substitution to be done, lets go for that:
2496  foreach ($fieldsIndex as $field => $softRefCfgs) {
2497  if (is_array($GLOBALS['TCA'][$table]['columns'][$field])) {
2498  $conf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
2499  if ($conf['type'] === 'flex') {
2500  // This will fetch the new row for the element (which should be updated with any references to data structures etc.)
2501  $origRecordRow = BackendUtility::getRecord($table, $thisNewUid, '*');
2502  if (is_array($origRecordRow)) {
2503  // Get current data structure and value array:
2504  $dataStructArray = BackendUtility::getFlexFormDS($conf, $origRecordRow, $table, $field);
2505  $currentValueArray = GeneralUtility::xml2array($origRecordRow[$field]);
2506  // Do recursive processing of the XML data:
2508  $iteratorObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
2509  $iteratorObj->callBackObj = $this;
2510  $currentValueArray['data'] = $iteratorObj->checkValue_flex_procInData($currentValueArray['data'], array(), array(), $dataStructArray, array($table, $uid, $field, $softRefCfgs), 'processSoftReferences_flexFormCallBack');
2511  // The return value is set as an array which means it will be processed by tcemain for file and DB references!
2512  if (is_array($currentValueArray['data'])) {
2513  $inData[$table][$thisNewUid][$field] = $currentValueArray;
2514  }
2515  }
2516  } else {
2517  // Get tokenizedContent string and proceed only if that is not blank:
2518  $tokenizedContent = $this->dat['records'][$table . ':' . $uid]['rels'][$field]['softrefs']['tokenizedContent'];
2519  if (strlen($tokenizedContent) && is_array($softRefCfgs)) {
2520  $inData[$table][$thisNewUid][$field] = $this->processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid);
2521  }
2522  }
2523  }
2524  }
2525  }
2526  }
2527  }
2528  }
2529  // Now write to database:
2530  $tce = $this->getNewTCE();
2531  $tce->isImporting = TRUE;
2532  $this->callHook('before_processSoftReferences', array(
2533  'tce' => &$tce,
2534  'data' => &$inData
2535  ));
2536  $tce->enableLogging = TRUE;
2537  $tce->start($inData, array());
2538  $tce->process_datamap();
2539  $this->callHook('after_processSoftReferences', array(
2540  'tce' => &$tce
2541  ));
2542  }
2543 
2557  public function processSoftReferences_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) {
2558  // Extract parameters:
2559  list($table, $origUid, $field, $softRefCfgs) = $pParams;
2560  if (is_array($softRefCfgs)) {
2561  // First, find all soft reference configurations for this structure path (they are listed flat in the header):
2562  $thisSoftRefCfgList = array();
2563  foreach ($softRefCfgs as $sK => $sV) {
2564  if ($sV['structurePath'] === $path) {
2565  $thisSoftRefCfgList[$sK] = $sV;
2566  }
2567  }
2568  // If any was found, do processing:
2569  if (count($thisSoftRefCfgList)) {
2570  // Get tokenizedContent string and proceed only if that is not blank:
2571  $tokenizedContent = $this->dat['records'][$table . ':' . $origUid]['rels'][$field]['flexFormRels']['softrefs'][$path]['tokenizedContent'];
2572  if (strlen($tokenizedContent)) {
2573  $dataValue = $this->processSoftReferences_substTokens($tokenizedContent, $thisSoftRefCfgList, $table, $origUid);
2574  }
2575  }
2576  }
2577  // Return
2578  return array('value' => $dataValue);
2579  }
2580 
2591  public function processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid) {
2592  // traverse each softref type for this field:
2593  foreach ($softRefCfgs as $cfg) {
2594  // Get token ID:
2595  $tokenID = $cfg['subst']['tokenID'];
2596  // Default is current token value:
2597  $insertValue = $cfg['subst']['tokenValue'];
2598  // Based on mode:
2599  switch ((string) $this->softrefCfg[$tokenID]['mode']) {
2600  case 'exclude':
2601  // Exclude is a simple passthrough of the value
2602  break;
2603  case 'editable':
2604  // Editable always picks up the value from this input array:
2605  $insertValue = $this->softrefInputValues[$tokenID];
2606  break;
2607  default:
2608  // Mapping IDs/creating files: Based on type, look up new value:
2609  switch ((string) $cfg['subst']['type']) {
2610  case 'file':
2611  // Create / Overwrite file:
2612  $insertValue = $this->processSoftReferences_saveFile($cfg['subst']['relFileName'], $cfg, $table, $uid);
2613  break;
2614  case 'db':
2615  default:
2616  // Trying to map database element if found in the mapID array:
2617  list($tempTable, $tempUid) = explode(':', $cfg['subst']['recordRef']);
2618  if (isset($this->import_mapId[$tempTable][$tempUid])) {
2619  $insertValue = BackendUtility::wsMapId($tempTable, $this->import_mapId[$tempTable][$tempUid]);
2620  // Look if reference is to a page and the original token value was NOT an integer - then we assume is was an alias and try to look up the new one!
2621  if ($tempTable === 'pages' && !\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($cfg['subst']['tokenValue'])) {
2622  $recWithUniqueValue = BackendUtility::getRecord($tempTable, $insertValue, 'alias');
2623  if ($recWithUniqueValue['alias']) {
2624  $insertValue = $recWithUniqueValue['alias'];
2625  }
2626  } elseif (strpos($cfg['subst']['tokenValue'], ':') !== FALSE) {
2627  list($tokenKey, $tokenId) = explode(':', $cfg['subst']['tokenValue']);
2628  $insertValue = $tokenKey . ':' . $insertValue;
2629  }
2630  }
2631  }
2632  }
2633  // Finally, swap the soft reference token in tokenized content with the insert value:
2634  $tokenizedContent = str_replace('{softref:' . $tokenID . '}', $insertValue, $tokenizedContent);
2635  }
2636  return $tokenizedContent;
2637  }
2638 
2649  public function processSoftReferences_saveFile($relFileName, $cfg, $table, $uid) {
2650  if ($fileHeaderInfo = $this->dat['header']['files'][$cfg['file_ID']]) {
2651  // Initialize; Get directory prefix for file and find possible RTE filename
2652  $dirPrefix = PathUtility::dirname($relFileName) . '/';
2653  $rteOrigName = $this->getRTEoriginalFilename(PathUtility::basename($relFileName));
2654  // If filename looks like an RTE file, and the directory is in "uploads/", then process as a RTE file!
2655  if ($rteOrigName && GeneralUtility::isFirstPartOfStr($dirPrefix, 'uploads/')) {
2656  // RTE:
2657  // First, find unique RTE file name:
2658  if (@is_dir((PATH_site . $dirPrefix))) {
2659  // From the "original" RTE filename, produce a new "original" destination filename which is unused. Even if updated, the image should be unique. Currently the problem with this is that it leaves a lot of unused RTE images...
2660  $fileProcObj = $this->getFileProcObj();
2661  $origDestName = $fileProcObj->getUniqueName($rteOrigName, PATH_site . $dirPrefix);
2662  // Create copy file name:
2663  $pI = pathinfo($relFileName);
2664  $copyDestName = PathUtility::dirname($origDestName) . '/RTEmagicC_' . substr(PathUtility::basename($origDestName), 10) . '.' . $pI['extension'];
2665  if (!@is_file($copyDestName) && !@is_file($origDestName) && $origDestName === GeneralUtility::getFileAbsFileName($origDestName) && $copyDestName === GeneralUtility::getFileAbsFileName($copyDestName)) {
2666  if ($this->dat['header']['files'][$fileHeaderInfo['RTE_ORIG_ID']]) {
2667  if ($this->legacyImport) {
2668  $fileName = PathUtility::basename($copyDestName);
2669  $this->writeSysFileResourceForLegacyImport($fileName, $cfg['file_ID']);
2670  $relFileName = $this->filePathMap[$cfg['file_ID']] . '" data-htmlarea-file-uid="' . $fileName . '" data-htmlarea-file-table="sys_file';
2671  // Also save the original file
2672  $originalFileName = PathUtility::basename($origDestName);
2673  $this->writeSysFileResourceForLegacyImport($originalFileName, $fileHeaderInfo['RTE_ORIG_ID']);
2674  } else {
2675  // Write the copy and original RTE file to the respective filenames:
2676  $this->writeFileVerify($copyDestName, $cfg['file_ID'], TRUE);
2677  $this->writeFileVerify($origDestName, $fileHeaderInfo['RTE_ORIG_ID'], TRUE);
2678  // Return the relative path of the copy file name:
2679  return \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($copyDestName);
2680  }
2681  } else {
2682  $this->error('ERROR: Could not find original file ID');
2683  }
2684  } else {
2685  $this->error('ERROR: The destination filenames "' . $copyDestName . '" and "' . $origDestName . '" either existed or have non-valid names');
2686  }
2687  } else {
2688  $this->error('ERROR: "' . PATH_site . $dirPrefix . '" was not a directory, so could not process file "' . $relFileName . '"');
2689  }
2690  } elseif (GeneralUtility::isFirstPartOfStr($dirPrefix, $this->fileadminFolderName . '/')) {
2691  // File in fileadmin/ folder:
2692  // Create file (and possible resources)
2693  $newFileName = $this->processSoftReferences_saveFile_createRelFile($dirPrefix, PathUtility::basename($relFileName), $cfg['file_ID'], $table, $uid);
2694  if (strlen($newFileName)) {
2695  $relFileName = $newFileName;
2696  } else {
2697  $this->error('ERROR: No new file created for "' . $relFileName . '"');
2698  }
2699  } else {
2700  $this->error('ERROR: Sorry, cannot operate on non-RTE files which are outside the fileadmin folder.');
2701  }
2702  } else {
2703  $this->error('ERROR: Could not find file ID in header.');
2704  }
2705  // Return (new) filename relative to PATH_site:
2706  return $relFileName;
2707  }
2708 
2720  public function processSoftReferences_saveFile_createRelFile($origDirPrefix, $fileName, $fileID, $table, $uid) {
2721  // If the fileID map contains an entry for this fileID then just return the relative filename of that entry; we don't want to write another unique filename for this one!
2722  if (isset($this->fileIDMap[$fileID])) {
2723  return \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($this->fileIDMap[$fileID]);
2724  }
2725  if ($this->legacyImport) {
2726  // set dirPrefix to fileadmin because the right target folder is set and checked for permissions later
2727  $dirPrefix = $this->fileadminFolderName . '/';
2728  } else {
2729  // Verify FileMount access to dir-prefix. Returns the best alternative relative path if any
2730  $dirPrefix = $this->verifyFolderAccess($origDirPrefix);
2731  }
2732  if ($dirPrefix && (!$this->update || $origDirPrefix === $dirPrefix) && $this->checkOrCreateDir($dirPrefix)) {
2733  $fileHeaderInfo = $this->dat['header']['files'][$fileID];
2734  $updMode = $this->update && $this->import_mapId[$table][$uid] === $uid && $this->import_mode[$table . ':' . $uid] !== 'as_new';
2735  // Create new name for file:
2736  // Must have same ID in map array (just for security, is not really needed) and NOT be set "as_new".
2737 
2738  // Write main file:
2739  if ($this->legacyImport) {
2740  $fileWritten = $this->writeSysFileResourceForLegacyImport($fileName, $fileID);
2741  if ($fileWritten) {
2742  $newName = 'file:' . $fileName;
2743  return $newName;
2744  // no support for HTML/CSS file resources attached ATM - see below
2745  }
2746  } else {
2747  if ($updMode) {
2748  $newName = PATH_site . $dirPrefix . $fileName;
2749  } else {
2750  // Create unique filename:
2751  $fileProcObj = $this->getFileProcObj();
2752  $newName = $fileProcObj->getUniqueName($fileName, PATH_site . $dirPrefix);
2753  }
2754  if ($this->writeFileVerify($newName, $fileID)) {
2755  // If the resource was an HTML/CSS file with resources attached, we will write those as well!
2756  if (is_array($fileHeaderInfo['EXT_RES_ID'])) {
2757  $tokenizedContent = $this->dat['files'][$fileID]['tokenizedContent'];
2758  $tokenSubstituted = FALSE;
2759  $fileProcObj = $this->getFileProcObj();
2760  if ($updMode) {
2761  foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
2762  if ($this->dat['files'][$res_fileID]['filename']) {
2763  // Resolve original filename:
2764  $relResourceFileName = $this->dat['files'][$res_fileID]['parentRelFileName'];
2765  $absResourceFileName = GeneralUtility::resolveBackPath(PATH_site . $origDirPrefix . $relResourceFileName);
2766  $absResourceFileName = GeneralUtility::getFileAbsFileName($absResourceFileName);
2767  if ($absResourceFileName && GeneralUtility::isFirstPartOfStr($absResourceFileName, PATH_site . $this->fileadminFolderName . '/')) {
2769  if ($this->verifyFolderAccess($destDir, TRUE) && $this->checkOrCreateDir($destDir)) {
2770  $this->writeFileVerify($absResourceFileName, $res_fileID);
2771  } else {
2772  $this->error('ERROR: Could not create file in directory "' . $destDir . '"');
2773  }
2774  } else {
2775  $this->error('ERROR: Could not resolve path for "' . $relResourceFileName . '"');
2776  }
2777  $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent);
2778  $tokenSubstituted = TRUE;
2779  }
2780  }
2781  } else {
2782  // Create the resouces directory name (filename without extension, suffixed "_FILES")
2783  $resourceDir = PathUtility::dirname($newName) . '/' . preg_replace('/\\.[^.]*$/', '', PathUtility::basename($newName)) . '_FILES';
2784  if (GeneralUtility::mkdir($resourceDir)) {
2785  foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
2786  if ($this->dat['files'][$res_fileID]['filename']) {
2787  $absResourceFileName = $fileProcObj->getUniqueName($this->dat['files'][$res_fileID]['filename'], $resourceDir);
2788  $relResourceFileName = substr($absResourceFileName, strlen(PathUtility::dirname($resourceDir)) + 1);
2789  $this->writeFileVerify($absResourceFileName, $res_fileID);
2790  $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent);
2791  $tokenSubstituted = TRUE;
2792  }
2793  }
2794  }
2795  }
2796  // If substitutions has been made, write the content to the file again:
2797  if ($tokenSubstituted) {
2798  GeneralUtility::writeFile($newName, $tokenizedContent);
2799  }
2800  }
2801  return \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($newName);
2802  }
2803  }
2804  }
2805  }
2806 
2816  public function writeFileVerify($fileName, $fileID, $bypassMountCheck = FALSE) {
2817  $fileProcObj = $this->getFileProcObj();
2818  if ($fileProcObj->actionPerms['addFile']) {
2819  // Just for security, check again. Should actually not be necessary.
2820  if ($fileProcObj->checkPathAgainstMounts($fileName) || $bypassMountCheck) {
2821  $fI = GeneralUtility::split_fileref($fileName);
2822  if ($fileProcObj->checkIfAllowed($fI['fileext'], $fI['path'], $fI['file']) || $this->allowPHPScripts && $GLOBALS['BE_USER']->isAdmin()) {
2823  if (GeneralUtility::getFileAbsFileName($fileName)) {
2824  if ($this->dat['files'][$fileID]) {
2825  GeneralUtility::writeFile($fileName, $this->dat['files'][$fileID]['content']);
2826  $this->fileIDMap[$fileID] = $fileName;
2827  if (md5(GeneralUtility::getUrl($fileName)) == $this->dat['files'][$fileID]['content_md5']) {
2828  return TRUE;
2829  } else {
2830  $this->error('ERROR: File content "' . $fileName . '" was corrupted');
2831  }
2832  } else {
2833  $this->error('ERROR: File ID "' . $fileID . '" could not be found');
2834  }
2835  } else {
2836  $this->error('ERROR: Filename "' . $fileName . '" was not a valid relative file path!');
2837  }
2838  } else {
2839  $this->error('ERROR: Filename "' . $fileName . '" failed against extension check or deny-pattern!');
2840  }
2841  } else {
2842  $this->error('ERROR: Filename "' . $fileName . '" was not allowed in destination path!');
2843  }
2844  } else {
2845  $this->error('ERROR: You did not have sufficient permissions to write the file "' . $fileName . '"');
2846  }
2847  }
2848 
2849 
2859  protected function writeSysFileResourceForLegacyImport(&$fileName, $fileId) {
2860  if ($this->legacyImportFolder === NULL) {
2861  return FALSE;
2862  }
2863 
2864  if (!isset($this->dat['files'][$fileId])) {
2865  $this->error('ERROR: File ID "' . $fileId . '" could not be found');
2866  return FALSE;
2867  }
2868 
2869  $temporaryFile = $this->writeTemporaryFileFromData($fileId, 'files');
2870  if ($temporaryFile === NULL) {
2871  // error on writing the file. Error message was already added
2872  return FALSE;
2873  }
2874 
2875  $importFolder = $this->legacyImportFolder;
2876 
2877  if (isset($this->dat['files'][$fileId]['relFileName'])) {
2878  $relativeFilePath = PathUtility::dirname($this->dat['files'][$fileId]['relFileName']);
2879 
2880  if (!$this->legacyImportFolder->hasFolder($relativeFilePath)) {
2881  $this->legacyImportFolder->createFolder($relativeFilePath);
2882  }
2883  $importFolder = $this->legacyImportFolder->getSubfolder($relativeFilePath);
2884  }
2885 
2886  $fileObject = NULL;
2887 
2888  try {
2889  // check, if there is alreay the same file in the folder
2890  if ($importFolder->hasFile($fileName)) {
2891  $fileStorage = $importFolder->getStorage();
2892  $file = $fileStorage->getFile($importFolder->getIdentifier() . $fileName);
2893  if ($file->getSha1() === sha1_file($temporaryFile)) {
2894  $fileObject = $file;
2895  }
2896  }
2897  } catch (Exception $e) {}
2898 
2899  if ($fileObject === NULL) {
2900  try {
2901  $fileObject = $importFolder->addFile($temporaryFile, $fileName, 'changeName');
2902  } catch (\TYPO3\CMS\Core\Exception $e) {
2903  $this->error('Error: no file could be added to the storage for file name ' . $this->alternativeFileName[$temporaryFile]);
2904  }
2905  }
2906 
2907  if (md5_file(PATH_site . $fileObject->getPublicUrl()) == $this->dat['files'][$fileId]['content_md5']) {
2908  $fileName = $fileObject->getUid();
2909  $this->fileIDMap[$fileId] = $fileName;
2910  $this->filePathMap[$fileId] = $fileObject->getPublicUrl();
2911  return TRUE;
2912  } else {
2913  $this->error('ERROR: File content "' . $this->dat['files'][$fileId]['relFileName'] . '" was corrupted');
2914  }
2915 
2916  return FALSE;
2917  }
2918 
2924  protected function migrateLegacyImportRecords() {
2925  $updateData= array();
2926 
2927  foreach ($this->legacyImportMigrationRecords as $table => $records) {
2928  foreach ($records as $uid => $fields) {
2929 
2930  $row = BackendUtility::getRecord($table, $uid);
2931  if (empty($row)) {
2932  continue;
2933  }
2934 
2935  foreach ($fields as $field => $referenceIds) {
2936 
2937  $fieldConfiguration = $this->legacyImportMigrationTables[$table][$field];
2938 
2939  if (isset($fieldConfiguration['titleTexts'])) {
2940  $titleTextField = $fieldConfiguration['titleTexts'];
2941  if ($row[$titleTextField] !== '') {
2942  $titleTextContents = explode(LF, $row[$titleTextField]);
2943  $updateData[$table][$uid][$titleTextField] = '';
2944  }
2945  }
2946 
2947  if (isset($fieldConfiguration['alternativeTexts'])) {
2948  $alternativeTextField = $fieldConfiguration['alternativeTexts'];
2949  if ($row[$alternativeTextField] !== '') {
2950  $alternativeTextContents = explode(LF, $row[$alternativeTextField]);
2951  $updateData[$table][$uid][$alternativeTextField] = '';
2952  }
2953  }
2954  if (isset($fieldConfiguration['description'])) {
2955  $descriptionField = $fieldConfiguration['description'];
2956  if ($row[$descriptionField] !== '') {
2957  $descriptionContents = explode(LF, $row[$descriptionField]);
2958  $updateData[$table][$uid][$descriptionField] = '';
2959  }
2960  }
2961  if (isset($fieldConfiguration['links'])) {
2962  $linkField = $fieldConfiguration['links'];
2963  if ($row[$linkField] !== '') {
2964  $linkContents = explode(LF, $row[$linkField]);
2965  $updateData[$table][$uid][$linkField] = '';
2966  }
2967  }
2968 
2969  foreach ($referenceIds as $key => $referenceId) {
2970  if (isset($titleTextContents[$key])) {
2971  $updateData['sys_file_reference'][$referenceId]['title'] = trim($titleTextContents[$key]);
2972  }
2973  if (isset($alternativeTextContents[$key])) {
2974  $updateData['sys_file_reference'][$referenceId]['alternative'] = trim($alternativeTextContents[$key]);
2975  }
2976  if (isset($descriptionContents[$key])) {
2977  $updateData['sys_file_reference'][$referenceId]['description'] = trim($descriptionContents[$key]);
2978  }
2979  if (isset($linkContents[$key])) {
2980  $updateData['sys_file_reference'][$referenceId]['link'] = trim($linkContents[$key]);
2981  }
2982  }
2983  }
2984  }
2985  }
2986 
2987  // update
2988  $tce = $this->getNewTCE();
2989  $tce->isImporting = TRUE;
2990  $tce->start($updateData, array());
2991  $tce->process_datamap();
2992 
2993  }
2994 
3002  public function checkOrCreateDir($dirPrefix) {
3003  // Split dir path and remove first directory (which should be "fileadmin")
3004  $filePathParts = explode('/', $dirPrefix);
3005  $firstDir = array_shift($filePathParts);
3006  if ($firstDir === $this->fileadminFolderName && GeneralUtility::getFileAbsFileName($dirPrefix)) {
3007  $pathAcc = '';
3008  foreach ($filePathParts as $dirname) {
3009  $pathAcc .= '/' . $dirname;
3010  if (strlen($dirname)) {
3011  if (!@is_dir((PATH_site . $this->fileadminFolderName . $pathAcc))) {
3012  if (!GeneralUtility::mkdir((PATH_site . $this->fileadminFolderName . $pathAcc))) {
3013  $this->error('ERROR: Directory could not be created....B');
3014  return FALSE;
3015  }
3016  }
3017  } elseif ($dirPrefix === $this->fileadminFolderName . $pathAcc) {
3018  return TRUE;
3019  } else {
3020  $this->error('ERROR: Directory could not be created....A');
3021  }
3022  }
3023  }
3024  }
3025 
3035  public function verifyFolderAccess($dirPrefix, $noAlternative = FALSE) {
3036  $fileProcObj = $this->getFileProcObj();
3037  // Check, if dirPrefix is inside a valid Filemount for user:
3038  $result = $fileProcObj->checkPathAgainstMounts(PATH_site . $dirPrefix);
3039  // If not, try to find another relative filemount and use that instead:
3040  if (!$result) {
3041  if ($noAlternative) {
3042  return FALSE;
3043  }
3044  // Find first web folder:
3045  $result = $fileProcObj->findFirstWebFolder();
3046  // If that succeeded, return the path to it:
3047  if ($result) {
3048  // Remove the "fileadmin/" prefix of input path - and append the rest to the return value:
3049  if (GeneralUtility::isFirstPartOfStr($dirPrefix, $this->fileadminFolderName . '/')) {
3050  $dirPrefix = substr($dirPrefix, strlen($this->fileadminFolderName . '/'));
3051  }
3052  return \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($fileProcObj->mounts[$result]['path'] . $dirPrefix);
3053  }
3054  } else {
3055  return $dirPrefix;
3056  }
3057  }
3058 
3059  /**************************
3060  * File Input
3061  *************************/
3062 
3071  public function loadFile($filename, $all = 0) {
3072  if (@is_file($filename)) {
3073  $fI = pathinfo($filename);
3074  if (strtolower($fI['extension']) == 'xml') {
3075  // XML:
3076  $xmlContent = GeneralUtility::getUrl($filename);
3077  if (strlen($xmlContent)) {
3078  $this->dat = GeneralUtility::xml2array($xmlContent, '', TRUE);
3079  if (is_array($this->dat)) {
3080  if ($this->dat['_DOCUMENT_TAG'] === 'T3RecordDocument' && is_array($this->dat['header']) && is_array($this->dat['records'])) {
3081  $this->loadInit();
3082  return TRUE;
3083  } else {
3084  $this->error('XML file did not contain proper XML for TYPO3 Import');
3085  }
3086  } else {
3087  $this->error('XML could not be parsed: ' . $this->dat);
3088  }
3089  } else {
3090  $this->error('Error opening file: ' . $filename);
3091  }
3092  } else {
3093  // T3D
3094  if ($fd = fopen($filename, 'rb')) {
3095  $this->dat['header'] = $this->getNextFilePart($fd, 1, 'header');
3096  if ($all) {
3097  $this->dat['records'] = $this->getNextFilePart($fd, 1, 'records');
3098  $this->dat['files'] = $this->getNextFilePart($fd, 1, 'files');
3099  $this->dat['files_fal'] = $this->getNextFilePart($fd, 1, 'files_fal');
3100  }
3101  $this->loadInit();
3102  return TRUE;
3103  } else {
3104  $this->error('Error opening file: ' . $filename);
3105  }
3106  fclose($fd);
3107  }
3108  } else {
3109  $this->error('Filename not found: ' . $filename);
3110  }
3111  return FALSE;
3112  }
3113 
3125  public function getNextFilePart($fd, $unserialize = 0, $name = '') {
3126  $initStrLen = 32 + 1 + 1 + 1 + 10 + 1;
3127  // Getting header data
3128  $initStr = fread($fd, $initStrLen);
3129  if (empty($initStr)) {
3130  $this->error('File does not contain data for "' . $name . '"');
3131  return NULL;
3132  }
3133  $initStrDat = explode(':', $initStr);
3134  if (strstr($initStrDat[0], 'Warning') == FALSE) {
3135  if ((string)$initStrDat[3] === '') {
3136  $datString = fread($fd, (int)$initStrDat[2]);
3137  fread($fd, 1);
3138  if (md5($datString) === $initStrDat[0]) {
3139  if ($initStrDat[1]) {
3140  if ($this->compress) {
3141  $datString = gzuncompress($datString);
3142  } else {
3143  $this->error('Content read error: This file requires decompression, but this server does not offer gzcompress()/gzuncompress() functions.', 1);
3144  }
3145  }
3146  return $unserialize ? unserialize($datString) : $datString;
3147  } else {
3148  $this->error('MD5 check failed (' . $name . ')');
3149  }
3150  } else {
3151  $this->error('File read error: InitString had a wrong length. (' . $name . ')');
3152  }
3153  } else {
3154  $this->error('File read error: Warning message in file. (' . $initStr . fgets($fd) . ')');
3155  }
3156  }
3157 
3166  public function loadContent($filecontent) {
3167  $pointer = 0;
3168  $this->dat['header'] = $this->getNextContentPart($filecontent, $pointer, 1, 'header');
3169  $this->dat['records'] = $this->getNextContentPart($filecontent, $pointer, 1, 'records');
3170  $this->dat['files'] = $this->getNextContentPart($filecontent, $pointer, 1, 'files');
3171  $this->loadInit();
3172  }
3173 
3184  public function getNextContentPart($filecontent, &$pointer, $unserialize = 0, $name = '') {
3185  $initStrLen = 32 + 1 + 1 + 1 + 10 + 1;
3186  // getting header data
3187  $initStr = substr($filecontent, $pointer, $initStrLen);
3188  $pointer += $initStrLen;
3189  $initStrDat = explode(':', $initStr);
3190  if ((string)$initStrDat[3] === '') {
3191  $datString = substr($filecontent, $pointer, (int)$initStrDat[2]);
3192  $pointer += (int)$initStrDat[2] + 1;
3193  if (md5($datString) === $initStrDat[0]) {
3194  if ($initStrDat[1]) {
3195  if ($this->compress) {
3196  $datString = gzuncompress($datString);
3197  } else {
3198  $this->error('Content read error: This file requires decompression, but this server does not offer gzcompress()/gzuncompress() functions.', 1);
3199  }
3200  }
3201  return $unserialize ? unserialize($datString) : $datString;
3202  } else {
3203  $this->error('MD5 check failed (' . $name . ')');
3204  }
3205  } else {
3206  $this->error('Content read error: InitString had a wrong length. (' . $name . ')');
3207  }
3208  }
3209 
3216  public function loadInit() {
3217  $this->relStaticTables = (array) $this->dat['header']['relStaticTables'];
3218  $this->excludeMap = (array) $this->dat['header']['excludeMap'];
3219  $this->softrefCfg = (array) $this->dat['header']['softrefCfg'];
3220  $this->extensionDependencies = (array) $this->dat['header']['extensionDependencies'];
3221  $this->fixCharsets();
3222  if (isset($this->dat['header']['meta']['TYPO3_version']) && \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger($this->dat['header']['meta']['TYPO3_version']) < 6000000){
3223  $this->legacyImport = TRUE;
3224  $this->initializeLegacyImportFolder();
3225  }
3226  }
3227 
3235  public function fixCharsets() {
3236  global $LANG;
3237  $importCharset = $this->dat['header']['charset'];
3238  if ($importCharset) {
3239  if ($importCharset !== $LANG->charSet) {
3240  $this->error('CHARSET: Converting charset of input file (' . $importCharset . ') to the system charset (' . $LANG->charSet . ')');
3241  // Convert meta data:
3242  if (is_array($this->dat['header']['meta'])) {
3243  $LANG->csConvObj->convArray($this->dat['header']['meta'], $importCharset, $LANG->charSet);
3244  }
3245  // Convert record headers:
3246  if (is_array($this->dat['header']['records'])) {
3247  $LANG->csConvObj->convArray($this->dat['header']['records'], $importCharset, $LANG->charSet);
3248  }
3249  // Convert records themselves:
3250  if (is_array($this->dat['records'])) {
3251  $LANG->csConvObj->convArray($this->dat['records'], $importCharset, $LANG->charSet);
3252  }
3253  }
3254  } else {
3255  $this->error('CHARSET: No charset found in import file!');
3256  }
3257  }
3258 
3259  /********************************************************
3260  * Visual rendering of import/export memory, $this->dat
3261  ********************************************************/
3262 
3269  public function displayContentOverview() {
3270  global $LANG;
3271  // Check extension dependencies:
3272  if (is_array($this->dat['header']['extensionDependencies'])) {
3273  foreach ($this->dat['header']['extensionDependencies'] as $extKey) {
3274  if (!empty($extKey) && !\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded($extKey)) {
3275  $this->error('DEPENDENCY: The extension with key "' . $extKey . '" must be installed!');
3276  }
3277  }
3278  }
3279  // Probably this is done to save memory space?
3280  unset($this->dat['files']);
3281  // Traverse header:
3282  $this->remainHeader = $this->dat['header'];
3283  if (is_array($this->remainHeader)) {
3284  // If there is a page tree set, show that:
3285  if (is_array($this->dat['header']['pagetree'])) {
3286  reset($this->dat['header']['pagetree']);
3287  $lines = array();
3288  $this->traversePageTree($this->dat['header']['pagetree'], $lines);
3289  $rows = array();
3290  $rows[] = '
3291  <tr class="bgColor5 tableheader">
3292  <td>' . $LANG->getLL('impexpcore_displaycon_controls', TRUE) . '</td>
3293  <td>' . $LANG->getLL('impexpcore_displaycon_title', TRUE) . '</td>
3294  <td>' . $LANG->getLL('impexpcore_displaycon_size', TRUE) . '</td>
3295  <td>' . $LANG->getLL('impexpcore_displaycon_message', TRUE) . '</td>
3296  ' . ($this->update ? '<td>' . $LANG->getLL('impexpcore_displaycon_updateMode', TRUE) . '</td>' : '') . '
3297  ' . ($this->update ? '<td>' . $LANG->getLL('impexpcore_displaycon_currentPath', TRUE) . '</td>' : '') . '
3298  ' . ($this->showDiff ? '<td>' . $LANG->getLL('impexpcore_displaycon_result', TRUE) . '</td>' : '') . '
3299  </tr>';
3300  foreach ($lines as $r) {
3301  $rows[] = '
3302  <tr class="' . $r['class'] . '">
3303  <td>' . $this->renderControls($r) . '</td>
3304  <td nowrap="nowrap">' . $r['preCode'] . $r['title'] . '</td>
3305  <td nowrap="nowrap">' . GeneralUtility::formatSize($r['size']) . '</td>
3306  <td nowrap="nowrap">' . ($r['msg'] && !$this->doesImport ? '<span class="typo3-red">' . htmlspecialchars($r['msg']) . '</span>' : '') . '</td>
3307  ' . ($this->update ? '<td nowrap="nowrap">' . $r['updateMode'] . '</td>' : '') . '
3308  ' . ($this->update ? '<td nowrap="nowrap">' . $r['updatePath'] . '</td>' : '') . '
3309  ' . ($this->showDiff ? '<td>' . $r['showDiffContent'] . '</td>' : '') . '
3310  </tr>';
3311  }
3312  $out = '
3313  <strong>' . $LANG->getLL('impexpcore_displaycon_insidePagetree', TRUE) . '</strong>
3314  <br /><br />
3315  <table border="0" cellpadding="0" cellspacing="1">' . implode('', $rows) . '</table>
3316  <br /><br />';
3317  }
3318  // Print remaining records that were not contained inside the page tree:
3319  $lines = array();
3320  if (is_array($this->remainHeader['records'])) {
3321  if (is_array($this->remainHeader['records']['pages'])) {
3322  $this->traversePageRecords($this->remainHeader['records']['pages'], $lines);
3323  }
3324  $this->traverseAllRecords($this->remainHeader['records'], $lines);
3325  if (count($lines)) {
3326  $rows = array();
3327  $rows[] = '
3328  <tr class="bgColor5 tableheader">
3329  <td>' . $LANG->getLL('impexpcore_displaycon_controls', TRUE) . '</td>
3330  <td>' . $LANG->getLL('impexpcore_displaycon_title', TRUE) . '</td>
3331  <td>' . $LANG->getLL('impexpcore_displaycon_size', TRUE) . '</td>
3332  <td>' . $LANG->getLL('impexpcore_displaycon_message', TRUE) . '</td>
3333  ' . ($this->update ? '<td>' . $LANG->getLL('impexpcore_displaycon_updateMode', TRUE) . '</td>' : '') . '
3334  ' . ($this->update ? '<td>' . $LANG->getLL('impexpcore_displaycon_currentPath', TRUE) . '</td>' : '') . '
3335  ' . ($this->showDiff ? '<td>' . $LANG->getLL('impexpcore_displaycon_result', TRUE) . '</td>' : '') . '
3336  </tr>';
3337  foreach ($lines as $r) {
3338  $rows[] = '<tr class="' . $r['class'] . '">
3339  <td>' . $this->renderControls($r) . '</td>
3340  <td nowrap="nowrap">' . $r['preCode'] . $r['title'] . '</td>
3341  <td nowrap="nowrap">' . GeneralUtility::formatSize($r['size']) . '</td>
3342  <td nowrap="nowrap">' . ($r['msg'] && !$this->doesImport ? '<span class="typo3-red">' . htmlspecialchars($r['msg']) . '</span>' : '') . '</td>
3343  ' . ($this->update ? '<td nowrap="nowrap">' . $r['updateMode'] . '</td>' : '') . '
3344  ' . ($this->update ? '<td nowrap="nowrap">' . $r['updatePath'] . '</td>' : '') . '
3345  ' . ($this->showDiff ? '<td>' . $r['showDiffContent'] . '</td>' : '') . '
3346  </tr>';
3347  }
3348  $out .= '
3349  <strong>' . $LANG->getLL('impexpcore_singlereco_outsidePagetree', TRUE) . '</strong>
3350  <br /><br />
3351  <table border="0" cellpadding="0" cellspacing="1">' . implode('', $rows) . '</table>';
3352  }
3353  }
3354  }
3355  return $out;
3356  }
3357 
3367  public function traversePageTree($pT, &$lines, $preCode = '') {
3368  foreach ($pT as $k => $v) {
3369  // Add this page:
3370  $this->singleRecordLines('pages', $k, $lines, $preCode);
3371  // Subrecords:
3372  if (is_array($this->dat['header']['pid_lookup'][$k])) {
3373  foreach ($this->dat['header']['pid_lookup'][$k] as $t => $recUidArr) {
3374  if ($t != 'pages') {
3375  foreach ($recUidArr as $ruid => $value) {
3376  $this->singleRecordLines($t, $ruid, $lines, $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;');
3377  }
3378  }
3379  }
3380  unset($this->remainHeader['pid_lookup'][$k]);
3381  }
3382  // Subpages, called recursively:
3383  if (is_array($v['subrow'])) {
3384  $this->traversePageTree($v['subrow'], $lines, $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;');
3385  }
3386  }
3387  }
3388 
3397  public function traversePageRecords($pT, &$lines) {
3398  foreach ($pT as $k => $rHeader) {
3399  $this->singleRecordLines('pages', $k, $lines, '', 1);
3400  // Subrecords:
3401  if (is_array($this->dat['header']['pid_lookup'][$k])) {
3402  foreach ($this->dat['header']['pid_lookup'][$k] as $t => $recUidArr) {
3403  if ($t != 'pages') {
3404  foreach ($recUidArr as $ruid => $value) {
3405  $this->singleRecordLines($t, $ruid, $lines, '&nbsp;&nbsp;&nbsp;&nbsp;');
3406  }
3407  }
3408  }
3409  unset($this->remainHeader['pid_lookup'][$k]);
3410  }
3411  }
3412  }
3413 
3422  public function traverseAllRecords($pT, &$lines) {
3423  foreach ($pT as $t => $recUidArr) {
3424  $this->addGeneralErrorsByTable($t);
3425  if ($t != 'pages') {
3426  $preCode = '';
3427  foreach ($recUidArr as $ruid => $value) {
3428  $this->singleRecordLines($t, $ruid, $lines, $preCode, 1);
3429  }
3430  }
3431  }
3432  }
3433 
3440  protected function addGeneralErrorsByTable($table) {
3441  if ($this->update && $table === 'sys_file') {
3442  $this->error('Updating sys_file records is not supported! They will be imported as new records!');
3443  }
3444  if ($this->force_all_UIDS && $table === 'sys_file') {
3445  $this->error('Forcing uids of sys_file records is not supported! They will be imported as new records!');
3446  }
3447  }
3448 
3460  public function singleRecordLines($table, $uid, &$lines, $preCode, $checkImportInPidRecord = 0) {
3461  global $LANG;
3462  // Get record:
3463  $record = $this->dat['header']['records'][$table][$uid];
3464  unset($this->remainHeader['records'][$table][$uid]);
3465  if (!is_array($record) && !($table === 'pages' && !$uid)) {
3466  $this->error('MISSING RECORD: ' . $table . ':' . $uid, 1);
3467  }
3468  // Begin to create the line arrays information record, pInfo:
3469  $pInfo = array();
3470  $pInfo['ref'] = $table . ':' . $uid;
3471  // Unknown table name:
3472  if ($table === '_SOFTREF_') {
3473  $pInfo['preCode'] = $preCode;
3474  $pInfo['title'] = '<em>' . $GLOBALS['LANG']->getLL('impexpcore_singlereco_softReferencesFiles', TRUE) . '</em>';
3475  } elseif (!isset($GLOBALS['TCA'][$table])) {
3476  // Unknown table name:
3477  $pInfo['preCode'] = $preCode;
3478  $pInfo['msg'] = 'UNKNOWN TABLE \'' . $pInfo['ref'] . '\'';
3479  $pInfo['title'] = '<em>' . htmlspecialchars($record['title']) . '</em>';
3480  } else {
3481  // Otherwise, set table icon and title.
3482  // Import Validation (triggered by $this->display_import_pid_record) will show messages if import is not possible of various items.
3483  if (is_array($this->display_import_pid_record)) {
3484  if ($checkImportInPidRecord) {
3485  if (!$GLOBALS['BE_USER']->doesUserHaveAccess($this->display_import_pid_record, ($table === 'pages' ? 8 : 16))) {
3486  $pInfo['msg'] .= '\'' . $pInfo['ref'] . '\' cannot be INSERTED on this page! ';
3487  }
3488  if (!$this->checkDokType($table, $this->display_import_pid_record['doktype']) && !$GLOBALS['TCA'][$table]['ctrl']['rootLevel']) {
3489  $pInfo['msg'] .= '\'' . $table . '\' cannot be INSERTED on this page type (change page type to \'Folder\'.) ';
3490  }
3491  }
3492  if (!$GLOBALS['BE_USER']->check('tables_modify', $table)) {
3493  $pInfo['msg'] .= 'You are not allowed to CREATE \'' . $table . '\' tables! ';
3494  }
3495  if ($GLOBALS['TCA'][$table]['ctrl']['readOnly']) {
3496  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is READ ONLY! ';
3497  }
3498  if ($GLOBALS['TCA'][$table]['ctrl']['adminOnly'] && !$GLOBALS['BE_USER']->isAdmin()) {
3499  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is ADMIN ONLY! ';
3500  }
3501  if ($GLOBALS['TCA'][$table]['ctrl']['is_static']) {
3502  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is a STATIC TABLE! ';
3503  }
3504  if ((int)$GLOBALS['TCA'][$table]['ctrl']['rootLevel'] === 1) {
3505  $pInfo['msg'] .= 'TABLE \'' . $table . '\' will be inserted on ROOT LEVEL! ';
3506  }
3507  $diffInverse = FALSE;
3508  if ($this->update) {
3509  // In case of update-PREVIEW we swap the diff-sources.
3510  $diffInverse = TRUE;
3511  $recInf = $this->doesRecordExist($table, $uid, $this->showDiff ? '*' : '');
3512  $pInfo['updatePath'] = $recInf ? htmlspecialchars($this->getRecordPath($recInf['pid'])) : '<strong>NEW!</strong>';
3513  // Mode selector:
3514  $optValues = array();
3515  $optValues[] = $recInf ? $LANG->getLL('impexpcore_singlereco_update') : $LANG->getLL('impexpcore_singlereco_insert');
3516  if ($recInf) {
3517  $optValues['as_new'] = $LANG->getLL('impexpcore_singlereco_importAsNew');
3518  }
3519  if ($recInf) {
3520  if (!$this->global_ignore_pid) {
3521  $optValues['ignore_pid'] = $LANG->getLL('impexpcore_singlereco_ignorePid');
3522  } else {
3523  $optValues['respect_pid'] = $LANG->getLL('impexpcore_singlereco_respectPid');
3524  }
3525  }
3526  if (!$recInf && $GLOBALS['BE_USER']->isAdmin()) {
3527  $optValues['force_uid'] = sprintf($LANG->getLL('impexpcore_singlereco_forceUidSAdmin'), $uid);
3528  }
3529  $optValues['exclude'] = $LANG->getLL('impexpcore_singlereco_exclude');
3530  if ($table === 'sys_file') {
3531  $pInfo['updateMode'] = '';
3532  } else {
3533  $pInfo['updateMode'] = $this->renderSelectBox('tx_impexp[import_mode][' . $table . ':' . $uid . ']', $this->import_mode[$table . ':' . $uid], $optValues);
3534  }
3535  }
3536  // Diff vieiw:
3537  if ($this->showDiff) {
3538  // For IMPORTS, get new id:
3539  if ($newUid = $this->import_mapId[$table][$uid]) {
3540  $diffInverse = FALSE;
3541  $recInf = $this->doesRecordExist($table, $newUid, '*');
3542  BackendUtility::workspaceOL($table, $recInf);
3543  }
3544  if (is_array($recInf)) {
3545  $pInfo['showDiffContent'] = $this->compareRecords($recInf, $this->dat['records'][$table . ':' . $uid]['data'], $table, $diffInverse);
3546  }
3547  }
3548  }
3549  $pInfo['preCode'] = $preCode . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForRecord($table, (array) $this->dat['records'][($table . ':' . $uid)]['data'], array('title' => htmlspecialchars(($table . ':' . $uid))));
3550  $pInfo['title'] = htmlspecialchars($record['title']);
3551  // View page:
3552  if ($table === 'pages') {
3553  $viewID = $this->mode === 'export' ? $uid : ($this->doesImport ? $this->import_mapId['pages'][$uid] : 0);
3554  if ($viewID) {
3555  $pInfo['title'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($viewID, $GLOBALS['BACK_PATH'])) . 'return false;">' . $pInfo['title'] . '</a>';
3556  }
3557  }
3558  }
3559  $pInfo['class'] = $table == 'pages' ? 'bgColor4-20' : 'bgColor4';
3560  $pInfo['type'] = 'record';
3561  $pInfo['size'] = $record['size'];
3562  $lines[] = $pInfo;
3563  // File relations:
3564  if (is_array($record['filerefs'])) {
3565  $this->addFiles($record['filerefs'], $lines, $preCode);
3566  }
3567  // DB relations
3568  if (is_array($record['rels'])) {
3569  $this->addRelations($record['rels'], $lines, $preCode);
3570  }
3571  // Soft ref
3572  if (count($record['softrefs'])) {
3573  $preCode_A = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;';
3574  $preCode_B = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
3575  foreach ($record['softrefs'] as $info) {
3576  $pInfo = array();
3577  $pInfo['preCode'] = $preCode_A . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('status-status-reference-soft');
3578  $pInfo['title'] = '<em>' . $info['field'] . ', "' . $info['spKey'] . '" </em>: <span title="' . htmlspecialchars($info['matchString']) . '">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['matchString'], 60)) . '</span>';
3579  if ($info['subst']['type']) {
3580  if (strlen($info['subst']['title'])) {
3581  $pInfo['title'] .= '<br/>' . $preCode_B . '<strong>' . $LANG->getLL('impexpcore_singlereco_title', TRUE) . '</strong> ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['subst']['title'], 60));
3582  }
3583  if (strlen($info['subst']['description'])) {
3584  $pInfo['title'] .= '<br/>' . $preCode_B . '<strong>' . $LANG->getLL('impexpcore_singlereco_descr', TRUE) . '</strong> ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['subst']['description'], 60));
3585  }
3586  $pInfo['title'] .= '<br/>' . $preCode_B . ($info['subst']['type'] == 'file' ? $LANG->getLL('impexpcore_singlereco_filename', TRUE) . ' <strong>' . $info['subst']['relFileName'] . '</strong>' : '') . ($info['subst']['type'] == 'string' ? $LANG->getLL('impexpcore_singlereco_value', TRUE) . ' <strong>' . $info['subst']['tokenValue'] . '</strong>' : '') . ($info['subst']['type'] == 'db' ? $LANG->getLL('impexpcore_softrefsel_record', TRUE) . ' <strong>' . $info['subst']['recordRef'] . '</strong>' : '');
3587  }
3588  $pInfo['ref'] = 'SOFTREF';
3589  $pInfo['size'] = '';
3590  $pInfo['class'] = 'bgColor3';
3591  $pInfo['type'] = 'softref';
3592  $pInfo['_softRefInfo'] = $info;
3593  $pInfo['type'] = 'softref';
3594  if ($info['error'] && !GeneralUtility::inList('editable,exclude', $this->softrefCfg[$info['subst']['tokenID']]['mode'])) {
3595  $pInfo['msg'] .= $info['error'];
3596  }
3597  $lines[] = $pInfo;
3598  // Add relations:
3599  if ($info['subst']['type'] == 'db') {
3600  list($tempTable, $tempUid) = explode(':', $info['subst']['recordRef']);
3601  $this->addRelations(array(array('table' => $tempTable, 'id' => $tempUid, 'tokenID' => $info['subst']['tokenID'])), $lines, $preCode_B, array(), '');
3602  }
3603  // Add files:
3604  if ($info['subst']['type'] == 'file') {
3605  $this->addFiles(array($info['file_ID']), $lines, $preCode_B, '', $info['subst']['tokenID']);
3606  }
3607  }
3608  }
3609  }
3610 
3624  public function addRelations($rels, &$lines, $preCode, $recurCheck = array(), $htmlColorClass = '') {
3625  foreach ($rels as $dat) {
3626  $table = $dat['table'];
3627  $uid = $dat['id'];
3628  $pInfo = array();
3629  $pInfo['ref'] = $table . ':' . $uid;
3630  if (in_array($pInfo['ref'], $recurCheck)) {
3631  continue;
3632  }
3633  $Iprepend = '';
3634  $staticFixed = FALSE;
3635  $record = NULL;
3636  if ($uid > 0) {
3637  $record = $this->dat['header']['records'][$table][$uid];
3638  if (!is_array($record)) {
3639  if ($this->isTableStatic($table) || $this->isExcluded($table, $uid) || $dat['tokenID'] && !$this->includeSoftref($dat['tokenID'])) {
3640  $pInfo['title'] = htmlspecialchars('STATIC: ' . $pInfo['ref']);
3641  $Iprepend = '_static';
3642  $staticFixed = TRUE;
3643  } else {
3644  $doesRE = $this->doesRecordExist($table, $uid);
3645  $lostPath = $this->getRecordPath($table === 'pages' ? $doesRE['uid'] : $doesRE['pid']);
3646  $pInfo['title'] = htmlspecialchars($pInfo['ref']);
3647  $pInfo['title'] = '<span title="' . htmlspecialchars($lostPath) . '">' . $pInfo['title'] . '</span>';
3648  $pInfo['msg'] = 'LOST RELATION' . (!$doesRE ? ' (Record not found!)' : ' (Path: ' . $lostPath . ')');
3649  $Iprepend = '_lost';
3650  }
3651  } else {
3652  $pInfo['title'] = htmlspecialchars($record['title']);
3653  $pInfo['title'] = '<span title="' . htmlspecialchars($this->getRecordPath(($table === 'pages' ? $record['uid'] : $record['pid']))) . '">' . $pInfo['title'] . '</span>';
3654  }
3655  } else {
3656  // Negative values in relation fields. This is typically sys_language fields, fe_users fields etc. They are static values. They CAN theoretically be negative pointers to uids in other tables but this is so rarely used that it is not supported
3657  $pInfo['title'] = htmlspecialchars('FIXED: ' . $pInfo['ref']);
3658  $staticFixed = TRUE;
3659  }
3660  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;<img' . \TYPO3\CMS\Backend\Utility\IconUtility::skinImg($GLOBALS['BACK_PATH'], ('gfx/rel_db' . $Iprepend . '.gif'), 'width="13" height="12"') . ' align="top" title="' . htmlspecialchars($pInfo['ref']) . '" alt="" />';
3661  $pInfo['class'] = $htmlColorClass ?: 'bgColor3';
3662  $pInfo['type'] = 'rel';
3663  if (!$staticFixed || $this->showStaticRelations) {
3664  $lines[] = $pInfo;
3665  if (is_array($record) && is_array($record['rels'])) {
3666  $this->addRelations($record['rels'], $lines, $preCode . '&nbsp;&nbsp;', array_merge($recurCheck, array($pInfo['ref'])), $htmlColorClass);
3667  }
3668  }
3669  }
3670  }
3671 
3685  public function addFiles($rels, &$lines, $preCode, $htmlColorClass = '', $tokenID = '') {
3686  foreach ($rels as $ID) {
3687  // Process file:
3688  $pInfo = array();
3689  $fI = $this->dat['header']['files'][$ID];
3690  if (!is_array($fI)) {
3691  if (!$tokenID || $this->includeSoftref($tokenID)) {
3692  $pInfo['msg'] = 'MISSING FILE: ' . $ID;
3693  $this->error('MISSING FILE: ' . $ID, 1);
3694  } else {
3695  return;
3696  }
3697  }
3698  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('status-status-reference-hard');
3699  $pInfo['title'] = htmlspecialchars($fI['filename']);
3700  $pInfo['ref'] = 'FILE';
3701  $pInfo['size'] = $fI['filesize'];
3702  $pInfo['class'] = $htmlColorClass ?: 'bgColor3';
3703  $pInfo['type'] = 'file';
3704  // If import mode and there is a non-RTE softreference, check the destination directory:
3705  if ($this->mode === 'import' && $tokenID && !$fI['RTE_ORIG_ID']) {
3706  if (isset($fI['parentRelFileName'])) {
3707  $pInfo['msg'] = 'Seems like this file is already referenced from within an HTML/CSS file. That takes precedence. ';
3708  } else {
3709  $testDirPrefix = PathUtility::dirname($fI['relFileName']) . '/';
3710  $testDirPrefix2 = $this->verifyFolderAccess($testDirPrefix);
3711  if (!$testDirPrefix2) {
3712  $pInfo['msg'] = 'ERROR: There are no available filemounts to write file in! ';
3713  } elseif ($testDirPrefix !== $testDirPrefix2) {
3714  $pInfo['msg'] = 'File will be attempted written to "' . $testDirPrefix2 . '". ';
3715  }
3716  }
3717  // Check if file exists:
3718  if (file_exists(PATH_site . $fI['relFileName'])) {
3719  if ($this->update) {
3720  $pInfo['updatePath'] .= 'File exists.';
3721  } else {
3722  $pInfo['msg'] .= 'File already exists! ';
3723  }
3724  }
3725  // Check extension:
3726  $fileProcObj = $this->getFileProcObj();
3727  if ($fileProcObj->actionPerms['addFile']) {
3728  $testFI = GeneralUtility::split_fileref(PATH_site . $fI['relFileName']);
3729  if (!$this->allowPHPScripts && !$fileProcObj->checkIfAllowed($testFI['fileext'], $testFI['path'], $testFI['file'])) {
3730  $pInfo['msg'] .= 'File extension was not allowed!';
3731  }
3732  } else {
3733  $pInfo['msg'] = 'You user profile does not allow you to create files on the server!';
3734  }
3735  }
3736  $pInfo['showDiffContent'] = \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($this->fileIDMap[$ID]);
3737  $lines[] = $pInfo;
3738  unset($this->remainHeader['files'][$ID]);
3739  // RTE originals:
3740  if ($fI['RTE_ORIG_ID']) {
3741  $ID = $fI['RTE_ORIG_ID'];
3742  $pInfo = array();
3743  $fI = $this->dat['header']['files'][$ID];
3744  if (!is_array($fI)) {
3745  $pInfo['msg'] = 'MISSING RTE original FILE: ' . $ID;
3746  $this->error('MISSING RTE original FILE: ' . $ID, 1);
3747  }
3748  $pInfo['showDiffContent'] = \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($this->fileIDMap[$ID]);
3749  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-reference-file');
3750  $pInfo['title'] = htmlspecialchars($fI['filename']) . ' <em>(Original)</em>';
3751  $pInfo['ref'] = 'FILE';
3752  $pInfo['size'] = $fI['filesize'];
3753  $pInfo['class'] = $htmlColorClass ?: 'bgColor3';
3754  $pInfo['type'] = 'file';
3755  $lines[] = $pInfo;
3756  unset($this->remainHeader['files'][$ID]);
3757  }
3758  // External resources:
3759  if (is_array($fI['EXT_RES_ID'])) {
3760  foreach ($fI['EXT_RES_ID'] as $ID) {
3761  $pInfo = array();
3762  $fI = $this->dat['header']['files'][$ID];
3763  if (!is_array($fI)) {
3764  $pInfo['msg'] = 'MISSING External Resource FILE: ' . $ID;
3765  $this->error('MISSING External Resource FILE: ' . $ID, 1);
3766  } else {
3767  $pInfo['updatePath'] = $fI['parentRelFileName'];
3768  }
3769  $pInfo['showDiffContent'] = \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($this->fileIDMap[$ID]);
3770  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-insert-reference');
3771  $pInfo['title'] = htmlspecialchars($fI['filename']) . ' <em>(Resource)</em>';
3772  $pInfo['ref'] = 'FILE';
3773  $pInfo['size'] = $fI['filesize'];
3774  $pInfo['class'] = $htmlColorClass ?: 'bgColor3';
3775  $pInfo['type'] = 'file';
3776  $lines[] = $pInfo;
3777  unset($this->remainHeader['files'][$ID]);
3778  }
3779  }
3780  }
3781  }
3782 
3791  public function checkDokType($checkTable, $doktype) {
3792  global $PAGES_TYPES;
3793  $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables'];
3794  $allowedArray = GeneralUtility::trimExplode(',', $allowedTableList, TRUE);
3795  // If all tables or the table is listed as a allowed type, return TRUE
3796  if (strstr($allowedTableList, '*') || in_array($checkTable, $allowedArray)) {
3797  return TRUE;
3798  }
3799  }
3800 
3808  public function renderControls($r) {
3809  global $LANG;
3810  if ($this->mode === 'export') {
3811  return $r['type'] == 'record' ? '<input type="checkbox" name="tx_impexp[exclude][' . $r['ref'] . ']" id="checkExclude' . $r['ref'] . '" value="1" /> <label for="checkExclude' . $r['ref'] . '">' . $LANG->getLL('impexpcore_singlereco_exclude', TRUE) . '</label>' : ($r['type'] == 'softref' ? $this->softrefSelector($r['_softRefInfo']) : '');
3812  } else {
3813  // During import
3814  // For softreferences with editable fields:
3815  if ($r['type'] == 'softref' && is_array($r['_softRefInfo']['subst']) && $r['_softRefInfo']['subst']['tokenID']) {
3816  $tokenID = $r['_softRefInfo']['subst']['tokenID'];
3817  $cfg = $this->softrefCfg[$tokenID];
3818  if ($cfg['mode'] === 'editable') {
3819  return (strlen($cfg['title']) ? '<strong>' . htmlspecialchars($cfg['title']) . '</strong><br/>' : '') . htmlspecialchars($cfg['description']) . '<br/>
3820  <input type="text" name="tx_impexp[softrefInputValues][' . $tokenID . ']" value="' . htmlspecialchars((isset($this->softrefInputValues[$tokenID]) ? $this->softrefInputValues[$tokenID] : $cfg['defValue'])) . '" />';
3821  }
3822  }
3823  }
3824  }
3825 
3833  public function softrefSelector($cfg) {
3834  global $LANG;
3835  // Looking for file ID if any:
3836  $fI = $cfg['file_ID'] ? $this->dat['header']['files'][$cfg['file_ID']] : array();
3837  // Substitution scheme has to be around and RTE images MUST be exported.
3838  if (is_array($cfg['subst']) && $cfg['subst']['tokenID'] && !$fI['RTE_ORIG_ID']) {
3839  // Create options:
3840  $optValues = array();
3841  $optValues[''] = '';
3842  $optValues['editable'] = $LANG->getLL('impexpcore_softrefsel_editable');
3843  $optValues['exclude'] = $LANG->getLL('impexpcore_softrefsel_exclude');
3844  // Get current value:
3845  $value = $this->softrefCfg[$cfg['subst']['tokenID']]['mode'];
3846  // Render options selector:
3847  $selectorbox = $this->renderSelectBox(('tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][mode]'), $value, $optValues) . '<br/>';
3848  if ($value === 'editable') {
3849  $descriptionField = '';
3850  // Title:
3851  if (strlen($cfg['subst']['title'])) {
3852  $descriptionField .= '
3853  <input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][title]" value="' . htmlspecialchars($cfg['subst']['title']) . '" />
3854  <strong>' . htmlspecialchars($cfg['subst']['title']) . '</strong><br/>';
3855  }
3856  // Description:
3857  if (!strlen($cfg['subst']['description'])) {
3858  $descriptionField .= '
3859  ' . $LANG->getLL('impexpcore_printerror_description', TRUE) . '<br/>
3860  <input type="text" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][description]" value="' . htmlspecialchars($this->softrefCfg[$cfg['subst']['tokenID']]['description']) . '" />';
3861  } else {
3862  $descriptionField .= '
3863 
3864  <input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][description]" value="' . htmlspecialchars($cfg['subst']['description']) . '" />' . htmlspecialchars($cfg['subst']['description']);
3865  }
3866  // Default Value:
3867  $descriptionField .= '<input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][defValue]" value="' . htmlspecialchars($cfg['subst']['tokenValue']) . '" />';
3868  } else {
3869  $descriptionField = '';
3870  }
3871  return $selectorbox . $descriptionField;
3872  }
3873  }
3874 
3875  /*****************************
3876  * Helper functions of kinds
3877  *****************************/
3878 
3886  public function isTableStatic($table) {
3887  if (is_array($GLOBALS['TCA'][$table])) {
3888  return $GLOBALS['TCA'][$table]['ctrl']['is_static'] || in_array($table, $this->relStaticTables) || in_array('_ALL', $this->relStaticTables);
3889  }
3890  }
3891 
3899  public function inclRelation($table) {
3900  if (is_array($GLOBALS['TCA'][$table])) {
3901  return (in_array($table, $this->relOnlyTables) || in_array('_ALL', $this->relOnlyTables)) && $GLOBALS['BE_USER']->check('tables_select', $table);
3902  }
3903  }
3904 
3913  public function isExcluded($table, $uid) {
3914  return $this->excludeMap[$table . ':' . $uid] ? TRUE : FALSE;
3915  }
3916 
3924  public function includeSoftref($tokenID) {
3925  return $tokenID && !GeneralUtility::inList('exclude,editable', $this->softrefCfg[$tokenID]['mode']);
3926  }
3927 
3935  public function checkPID($pid) {
3936  if (!isset($this->checkPID_cache[$pid])) {
3937  $this->checkPID_cache[$pid] = (bool) $GLOBALS['BE_USER']->isInWebMount($pid);
3938  }
3939  return $this->checkPID_cache[$pid];
3940  }
3941 
3950  public function dontIgnorePid($table, $uid) {
3951  return $this->import_mode[$table . ':' . $uid] !== 'ignore_pid' && (!$this->global_ignore_pid || $this->import_mode[$table . ':' . $uid] === 'respect_pid');
3952  }
3953 
3963  public function doesRecordExist($table, $uid, $fields = '') {
3964  return BackendUtility::getRecord($table, $uid, $fields ? $fields : 'uid,pid');
3965  }
3966 
3974  public function getRecordPath($pid) {
3975  if (!isset($this->cache_getRecordPath[$pid])) {
3976  $clause = $GLOBALS['BE_USER']->getPagePermsClause(1);
3977  $this->cache_getRecordPath[$pid] = (string) BackendUtility::getRecordPath($pid, $clause, 20);
3978  }
3979  return $this->cache_getRecordPath[$pid];
3980  }
3981 
3991  public function renderSelectBox($prefix, $value, $optValues) {
3992  $opt = array();
3993  $isSelFlag = 0;
3994  foreach ($optValues as $k => $v) {
3995  $sel = (string)$k === (string)$value ? ' selected="selected"' : '';
3996  if ($sel) {
3997  $isSelFlag++;
3998  }
3999  $opt[] = '<option value="' . htmlspecialchars($k) . '"' . $sel . '>' . htmlspecialchars($v) . '</option>';
4000  }
4001  if (!$isSelFlag && (string)$value !== '') {
4002  $opt[] = '<option value="' . htmlspecialchars($value) . '" selected="selected">' . htmlspecialchars(('[\'' . $value . '\']')) . '</option>';
4003  }
4004  return '<select name="' . $prefix . '">' . implode('', $opt) . '</select>';
4005  }
4006 
4017  public function compareRecords($databaseRecord, $importRecord, $table, $inverseDiff = FALSE) {
4018  global $LANG;
4019  // Initialize:
4020  $output = array();
4021  $t3lib_diff_Obj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Utility\\DiffUtility');
4022  // Check if both inputs are records:
4023  if (is_array($databaseRecord) && is_array($importRecord)) {
4024  // Traverse based on database record
4025  foreach ($databaseRecord as $fN => $value) {
4026  if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type'] != 'passthrough') {
4027  if (isset($importRecord[$fN])) {
4028  if (trim($databaseRecord[$fN]) !== trim($importRecord[$fN])) {
4029  // Create diff-result:
4030  $output[$fN] = $t3lib_diff_Obj->makeDiffDisplay(BackendUtility::getProcessedValue($table, $fN, !$inverseDiff ? $importRecord[$fN] : $databaseRecord[$fN], 0, 1, 1), BackendUtility::getProcessedValue($table, $fN, !$inverseDiff ? $databaseRecord[$fN] : $importRecord[$fN], 0, 1, 1));
4031  }
4032  unset($importRecord[$fN]);
4033  }
4034  }
4035  }
4036  // Traverse remaining in import record:
4037  foreach ($importRecord as $fN => $value) {
4038  if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type'] !== 'passthrough') {
4039  $output[$fN] = '<strong>Field missing</strong> in database';
4040  }
4041  }
4042  // Create output:
4043  if (count($output)) {
4044  $tRows = array();
4045  foreach ($output as $fN => $state) {
4046  $tRows[] = '
4047  <tr>
4048  <td class="bgColor5">' . $GLOBALS['LANG']->sL($GLOBALS['TCA'][$table]['columns'][$fN]['label'], TRUE) . ' (' . htmlspecialchars($fN) . ')</td>
4049  <td class="bgColor4">' . $state . '</td>
4050  </tr>
4051  ';
4052  }
4053  $output = '<table border="0" cellpadding="0" cellspacing="1">' . implode('', $tRows) . '</table>';
4054  } else {
4055  $output = 'Match';
4056  }
4057  return '<strong class="nobr">[' . htmlspecialchars(($table . ':' . $importRecord['uid'] . ' => ' . $databaseRecord['uid'])) . ']:</strong> ' . $output;
4058  }
4059  return 'ERROR: One of the inputs were not an array!';
4060  }
4061 
4069  public function getRTEoriginalFilename($string) {
4070  // If "magic image":
4071  if (GeneralUtility::isFirstPartOfStr($string, 'RTEmagicC_')) {
4072  // Find original file:
4073  $pI = pathinfo(substr($string, strlen('RTEmagicC_')));
4074  $filename = substr($pI['basename'], 0, -strlen(('.' . $pI['extension'])));
4075  $origFilePath = 'RTEmagicP_' . $filename;
4076  return $origFilePath;
4077  }
4078  }
4079 
4086  public function getFileProcObj() {
4087  if (!is_object($this->fileProcObj)) {
4088  $this->fileProcObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Utility\\File\\ExtendedFileUtility');
4089  $this->fileProcObj->init(array(), $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']);
4090  $this->fileProcObj->setActionPermissions();
4091  }
4092  return $this->fileProcObj;
4093  }
4094 
4102  public function callHook($name, $params) {
4103  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name])) {
4104  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name] as $hook) {
4105  GeneralUtility::callUserFunction($hook, $params, $this);
4106  }
4107  }
4108  }
4109 
4110  /*****************************
4111  * Error handling
4112  *****************************/
4113 
4121  public function error($msg) {
4122  $this->errorLog[] = $msg;
4123  }
4124 
4131  public function printErrorLog() {
4132  return count($this->errorLog) ? \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($this->errorLog) : '';
4133  }
4134 
4135 }
addFilePart($data, $compress=FALSE)
static unlink_tempfile($uploadedTempFileName)
export_addFile($fI, $recordRef='', $fieldname='')
setRecordTypesIncludeFields(array $recordTypesIncludeFields)
static writeFile($file, $content, $changePermissions=FALSE)
addSingle($table, $uid, $pid)
static isFirstPartOfStr($str, $partStr)
flatInversePageTree_pid($idH, $a=array(), $pid=-1)
static workspaceOL($table, &$row, $wsid=-99, $unsetMovePointers=FALSE)
fixFileIDsInRelations(array $relations)
setRelations_db($itemArray, $itemConfig)
$uid
Definition: server.php:36
export_addSysFile(\TYPO3\CMS\Core\Resource\File $file)
fixUidLocalInSysFileReferenceRecords($oldFileUid, $newFileUid)
verifyFolderAccess($dirPrefix, $noAlternative=FALSE)
getNextFilePart($fd, $unserialize=0, $name='')
static getFlexFormDS($conf, $row, $table, $fieldName='', $WSOL=TRUE, $newRecordPidValue=0)
export_addDBRelations_registerRelation($fI, &$addR, $tokenID='')
processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid)
setMetaData($title, $description, $notes, $packager_username, $packager_name, $packager_email)
processSoftReferences_saveFile($relFileName, $cfg, $table, $uid)
static array2xml(array $array, $NSprefix='', $level=0, $docTag='phparray', $spaceInd=0, array $options=array(), array $stackData=array())
static getRecordTitle($table, $row, $prep=FALSE, $forceResult=TRUE)
writeTemporaryFileFromData($fileId, $dataKey='files_fal')
export_addRecord($table, $row, $relationLevel=0)
static split_fileref($fileNameWithPath)
traversePageTree($pT, &$lines, $preCode='')
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren&#39;t numeric.
static getUrl($url, $includeHeader=0, $requestHeaders=FALSE, &$report=NULL)
removeSoftrefsHavingTheSameDatabaseRelation($relations)
processSoftReferences_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path)
static tempnam($filePrefix, $fileSuffix='')
export_addDBRelations($relationLevel=0)
doesRecordExist($table, $uid, $fields='')
setRecordTypeIncludeFields($table, array $fields)
processSoftReferences_saveFile_createRelFile($origDirPrefix, $fileName, $fileID, $table, $uid)
static formatSize($sizeInBytes, $labels='')
static fixed_lgd_cs($string, $chars, $appendString='...')
remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path)
flatInversePageTree($idH, $a=array())
writeSysFileResourceForLegacyImport(&$fileName, $fileId)
singleRecordLines($table, $uid, &$lines, $preCode, $checkImportInPidRecord=0)
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
static xml2array($string, $NSprefix='', $reportDocTag=FALSE)
static getFileAbsFileName($filename, $onlyRelative=TRUE, $relToTYPO3_mainDir=FALSE)
writeFileVerify($fileName, $fileID, $bypassMountCheck=FALSE)
init($dontCompress=0, $mode='')
filterRecordFields($table, array $row)
removeSysFileReferenceRecordsFromImportDataWithRelationToMissingFile()
static deleteClause($table, $tableAlias='')
getNextContentPart($filecontent, &$pointer, $unserialize=0, $name='')