‪TYPO3CMS  9.5
ImportExport.php
Go to the documentation of this file.
1 <?php
2 namespace ‪TYPO3\CMS\Impexp;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
32 
67 abstract class ‪ImportExport
68 {
74  public ‪$showStaticRelations = false;
75 
81  public ‪$fileadminFolderName = '';
82 
88  public ‪$mode = '';
89 
95  public ‪$update = false;
96 
102  public ‪$doesImport = false;
103 
112 
118  public ‪$import_mode = [];
119 
125  public ‪$global_ignore_pid = false;
126 
132  public ‪$force_all_UIDS = false;
133 
139  public ‪$showDiff = false;
140 
146  public ‪$allowPHPScripts = false;
147 
153  public ‪$softrefInputValues = [];
154 
160  public ‪$fileIDMap = [];
161 
168  public ‪$relOnlyTables = [];
169 
176  public ‪$relStaticTables = [];
177 
183  public ‪$excludeMap = [];
184 
190  public ‪$softrefCfg = [];
191 
198 
204  public ‪$import_mapId = [];
205 
211  public ‪$errorLog = [];
212 
218  public ‪$cache_getRecordPath = [];
219 
225  public ‪$checkPID_cache = [];
226 
233  public ‪$compress = false;
234 
240  public ‪$dat = [];
241 
247  protected ‪$fileProcObj;
248 
252  protected ‪$remainHeader = [];
253 
257  protected ‪$iconFactory;
258 
265  protected ‪$excludeDisabledRecords = false;
266 
270  public function ‪__construct()
271  {
272  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
273  }
274 
275  /**************************
276  * Initialize
277  *************************/
278 
282  public function ‪init()
283  {
284  $this->compress = function_exists('gzcompress');
285  $this->fileadminFolderName = !empty(‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) ? rtrim(‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') : 'fileadmin';
286  }
287 
288  /********************************************************
289  * Visual rendering of import/export memory, $this->dat
290  ********************************************************/
291 
297  public function ‪displayContentOverview()
298  {
299  if (!isset($this->dat['header'])) {
300  return [];
301  }
302  // Check extension dependencies:
303  foreach ($this->dat['header']['extensionDependencies'] as $extKey) {
304  if (!empty($extKey) && !‪ExtensionManagementUtility::isLoaded($extKey)) {
305  $this->‪error('DEPENDENCY: The extension with key "' . $extKey . '" must be installed!');
306  }
307  }
308 
309  // Probably this is done to save memory space?
310  unset($this->dat['files']);
311 
312  $viewData = [];
313  // Traverse header:
314  $this->remainHeader = $this->dat['header'];
315  // If there is a page tree set, show that:
316  if (is_array($this->dat['header']['pagetree'])) {
317  reset($this->dat['header']['pagetree']);
318  $lines = [];
319  $this->‪traversePageTree($this->dat['header']['pagetree'], $lines);
320 
321  $viewData['dat'] = ‪$this->dat;
322  $viewData['update'] = ‪$this->update;
323  $viewData['showDiff'] = ‪$this->showDiff;
324  if (!empty($lines)) {
325  foreach ($lines as &$r) {
326  $r['controls'] = $this->‪renderControls($r);
327  $r['message'] = ($r['msg'] && !$this->doesImport ? '<span class="text-danger">' . htmlspecialchars($r['msg']) . '</span>' : '');
328  }
329  $viewData['pagetreeLines'] = $lines;
330  } else {
331  $viewData['pagetreeLines'] = [];
332  }
333  }
334  // Print remaining records that were not contained inside the page tree:
335  if (is_array($this->remainHeader['records'])) {
336  $lines = [];
337  if (is_array($this->remainHeader['records']['pages'])) {
338  $this->‪traversePageRecords($this->remainHeader['records']['pages'], $lines);
339  }
340  $this->‪traverseAllRecords($this->remainHeader['records'], $lines);
341  if (!empty($lines)) {
342  foreach ($lines as &$r) {
343  $r['controls'] = $this->‪renderControls($r);
344  $r['message'] = ($r['msg'] && !$this->doesImport ? '<span class="text-danger">' . htmlspecialchars($r['msg']) . '</span>' : '');
345  }
346  $viewData['remainingRecords'] = $lines;
347  }
348  }
349 
350  return $viewData;
351  }
352 
360  public function ‪traversePageTree($pT, &$lines, $preCode = '')
361  {
362  foreach ($pT as $k => $v) {
363  if ($this->excludeDisabledRecords === true && !$this->‪isActive('pages', $k)) {
364  $this->‪excludePageAndRecords($k, $v);
365  continue;
366  }
367 
368  // Add this page:
369  $this->‪singleRecordLines('pages', $k, $lines, $preCode);
370  // Subrecords:
371  if (is_array($this->dat['header']['pid_lookup'][$k])) {
372  foreach ($this->dat['header']['pid_lookup'][$k] as $t => $recUidArr) {
373  if ($t !== 'pages') {
374  foreach ($recUidArr as $ruid => $value) {
375  $this->‪singleRecordLines($t, $ruid, $lines, $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;');
376  }
377  }
378  }
379  unset($this->remainHeader['pid_lookup'][$k]);
380  }
381  // Subpages, called recursively:
382  if (is_array($v['subrow'])) {
383  $this->‪traversePageTree($v['subrow'], $lines, $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;');
384  }
385  }
386  }
387 
395  protected function ‪isActive($table, $uid)
396  {
397  return
398  !isset(‪$GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'])
399  || !(bool)$this->dat['records'][$table . ':' . $uid]['data'][
400  ‪$GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['disabled']
401  ];
402  }
403 
410  protected function ‪excludePageAndRecords($pageUid, $pageTree)
411  {
412  // Prevent having this page appear in "remaining records" table
413  unset($this->remainHeader['records']['pages'][$pageUid]);
414 
415  // Subrecords
416  if (is_array($this->dat['header']['pid_lookup'][$pageUid])) {
417  foreach ($this->dat['header']['pid_lookup'][$pageUid] as $table => $recordData) {
418  if ($table !== 'pages') {
419  foreach (array_keys($recordData) as $uid) {
420  unset($this->remainHeader['records'][$table][$uid]);
421  }
422  }
423  }
424  unset($this->remainHeader['pid_lookup'][$pageUid]);
425  }
426  // Subpages excluded recursively
427  if (is_array($pageTree['subrow'])) {
428  foreach ($pageTree['subrow'] as $subPageUid => $subPageTree) {
429  $this->‪excludePageAndRecords($subPageUid, $subPageTree);
430  }
431  }
432  }
433 
440  public function ‪traversePageRecords($pT, &$lines)
441  {
442  foreach ($pT as $k => $rHeader) {
443  $this->‪singleRecordLines('pages', $k, $lines, '', 1);
444  // Subrecords:
445  if (is_array($this->dat['header']['pid_lookup'][$k])) {
446  foreach ($this->dat['header']['pid_lookup'][$k] as $t => $recUidArr) {
447  if ($t !== 'pages') {
448  foreach ($recUidArr as $ruid => $value) {
449  $this->‪singleRecordLines($t, $ruid, $lines, '&nbsp;&nbsp;&nbsp;&nbsp;');
450  }
451  }
452  }
453  unset($this->remainHeader['pid_lookup'][$k]);
454  }
455  }
456  }
457 
464  public function ‪traverseAllRecords($pT, &$lines)
465  {
466  foreach ($pT as $t => $recUidArr) {
467  $this->‪addGeneralErrorsByTable($t);
468  if ($t !== 'pages') {
469  $preCode = '';
470  foreach ($recUidArr as $ruid => $value) {
471  $this->‪singleRecordLines($t, $ruid, $lines, $preCode, 1);
472  }
473  }
474  }
475  }
476 
482  protected function ‪addGeneralErrorsByTable($table)
483  {
484  if ($this->update && $table === 'sys_file') {
485  $this->‪error('Updating sys_file records is not supported! They will be imported as new records!');
486  }
487  if ($this->force_all_UIDS && $table === 'sys_file') {
488  $this->‪error('Forcing uids of sys_file records is not supported! They will be imported as new records!');
489  }
490  }
491 
501  public function ‪singleRecordLines($table, $uid, &$lines, $preCode, $checkImportInPidRecord = false)
502  {
503  // Get record:
504  $record = $this->dat['header']['records'][$table][$uid];
505  unset($this->remainHeader['records'][$table][$uid]);
506  if (!is_array($record) && !($table === 'pages' && !$uid)) {
507  $this->‪error('MISSING RECORD: ' . $table . ':' . $uid);
508  }
509  // Begin to create the line arrays information record, pInfo:
510  $pInfo = [];
511  $pInfo['ref'] = $table . ':' . $uid;
512  // Unknown table name:
513  $lang = $this->‪getLanguageService();
514  if ($table === '_SOFTREF_') {
515  $pInfo['preCode'] = $preCode;
516  $pInfo['title'] = '<em>' . htmlspecialchars($lang->getLL('impexpcore_singlereco_softReferencesFiles')) . '</em>';
517  } elseif (!isset(‪$GLOBALS['TCA'][$table])) {
518  // Unknown table name:
519  $pInfo['preCode'] = $preCode;
520  $pInfo['msg'] = 'UNKNOWN TABLE \'' . $pInfo['ref'] . '\'';
521  $pInfo['title'] = '<em>' . htmlspecialchars($record['title']) . '</em>';
522  } else {
523  // prepare data attribute telling whether the record is active or hidden, allowing frontend bulk selection
524  $pInfo['active'] = $this->‪isActive($table, $uid) ? 'active' : 'hidden';
525 
526  // Otherwise, set table icon and title.
527  // Import Validation (triggered by $this->display_import_pid_record) will show messages if import is not possible of various items.
528  if (is_array($this->display_import_pid_record) && !empty($this->display_import_pid_record)) {
529  if ($checkImportInPidRecord) {
530  if (!$this->‪getBackendUser()->doesUserHaveAccess($this->display_import_pid_record, ($table === 'pages' ? 8 : 16))) {
531  $pInfo['msg'] .= '\'' . $pInfo['ref'] . '\' cannot be INSERTED on this page! ';
532  }
533  if (!$this->checkDokType($table, $this->display_import_pid_record['doktype']) && !$GLOBALS['TCA'][$table]['ctrl']['rootLevel']) {
534  $pInfo['msg'] .= '\'' . $table . '\' cannot be INSERTED on this page type (change page type to \'Folder\'.) ';
535  }
536  }
537  if (!$this->‪getBackendUser()->check('tables_modify', $table)) {
538  $pInfo['msg'] .= 'You are not allowed to CREATE \'' . $table . '\' tables! ';
539  }
540  if ($GLOBALS['TCA'][$table]['ctrl']['readOnly']) {
541  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is READ ONLY! ';
542  }
543  if ($GLOBALS['TCA'][$table]['ctrl']['adminOnly'] && !$this->getBackendUser()->isAdmin()) {
544  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is ADMIN ONLY! ';
545  }
546  if ($GLOBALS['TCA'][$table]['ctrl']['is_static']) {
547  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is a STATIC TABLE! ';
548  }
549  if ((int)$GLOBALS['TCA'][$table]['ctrl']['rootLevel'] === 1) {
550  $pInfo['msg'] .= 'TABLE \'' . $table . '\' will be inserted on ROOT LEVEL! ';
551  }
552  $diffInverse = false;
553  $recInf = null;
554  if ($this->update) {
555  // In case of update-PREVIEW we swap the diff-sources.
556  $diffInverse = true;
557  $recInf = $this->doesRecordExist($table, $uid, $this->showDiff ? '*' : '');
558  $pInfo['updatePath'] = $recInf ? htmlspecialchars($this->getRecordPath($recInf['pid'])) : '<strong>NEW!</strong>';
559  // Mode selector:
560  $optValues = [];
561  $optValues[] = $recInf ? $lang->getLL('impexpcore_singlereco_update') : $lang->getLL('impexpcore_singlereco_insert');
562  if ($recInf) {
563  $optValues['as_new'] = $lang->getLL('impexpcore_singlereco_importAsNew');
564  }
565  if ($recInf) {
566  if (!$this->global_ignore_pid) {
567  $optValues['ignore_pid'] = $lang->getLL('impexpcore_singlereco_ignorePid');
568  } else {
569  $optValues['respect_pid'] = $lang->getLL('impexpcore_singlereco_respectPid');
570  }
571  }
572  if (!$recInf && $this->getBackendUser()->isAdmin()) {
573  $optValues['force_uid'] = sprintf($lang->getLL('impexpcore_singlereco_forceUidSAdmin'), $uid);
574  }
575  $optValues['exclude'] = $lang->getLL('impexpcore_singlereco_exclude');
576  if ($table === 'sys_file') {
577  $pInfo['updateMode'] = '';
578  } else {
579  $pInfo['updateMode'] = $this->renderSelectBox('tx_impexp[import_mode][' . $table . ':' . $uid . ']', $this->import_mode[$table . ':' . $uid], $optValues);
580  }
581  }
582  // Diff view:
583  if ($this->showDiff) {
584  // For IMPORTS, get new id:
585  if ($newUid = $this->import_mapId[$table][$uid]) {
586  $diffInverse = false;
587  $recInf = $this->doesRecordExist($table, $newUid, '*');
588  BackendUtility::workspaceOL($table, $recInf);
589  }
590  if (is_array($recInf)) {
591  $pInfo['showDiffContent'] = $this->compareRecords($recInf, $this->dat['records'][$table . ':' . $uid]['data'], $table, $diffInverse);
592  }
593  }
594  }
595  $pInfo['preCode'] = $preCode . '<span title="' . htmlspecialchars($table . ':' . $uid) . '">'
596  . $this->iconFactory->getIconForRecord($table, (array)$this->dat['records'][$table . ':' . $uid]['data'], Icon::SIZE_SMALL)->render()
597  . '</span>';
598  $pInfo['title'] = htmlspecialchars($record['title']);
599  // View page:
600  if ($table === 'pages') {
601  $viewID = $this->mode === 'export' ? $uid : ($this->doesImport ? $this->import_mapId['pages'][$uid] : 0);
602  if ($viewID) {
603  $pInfo['title'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($viewID)) . 'return false;">' . $pInfo['title'] . '</a>';
604  }
605  }
606  }
607  $pInfo['type'] = 'record';
608  $lines[] = $pInfo;
609  // File relations:
610  if (is_array($record['filerefs'])) {
611  $this->addFiles($record['filerefs'], $lines, $preCode);
612  }
613  // DB relations
614  if (is_array($record['rels'])) {
615  $this->addRelations($record['rels'], $lines, $preCode);
616  }
617  // Soft ref
618  if (!empty($record['softrefs'])) {
619  $preCode_A = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;';
620  $preCode_B = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
621  foreach ($record['softrefs'] as $info) {
622  $pInfo = [];
623  $pInfo['preCode'] = $preCode_A . $this->iconFactory->getIcon('status-reference-soft', Icon::SIZE_SMALL)->render();
624  $pInfo['title'] = '<em>' . $info['field'] . ', "' . $info['spKey'] . '" </em>: <span title="' . htmlspecialchars($info['matchString']) . '">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['matchString'], 60)) . '</span>';
625  if ($info['subst']['type']) {
626  if (strlen($info['subst']['title'])) {
627  $pInfo['title'] .= '<br/>' . $preCode_B . '<strong>' . htmlspecialchars($lang->getLL('impexpcore_singlereco_title')) . '</strong> ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['subst']['title'], 60));
628  }
629  if (strlen($info['subst']['description'])) {
630  $pInfo['title'] .= '<br/>' . $preCode_B . '<strong>' . htmlspecialchars($lang->getLL('impexpcore_singlereco_descr')) . '</strong> ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['subst']['description'], 60));
631  }
632  $pInfo['title'] .= '<br/>' . $preCode_B . ($info['subst']['type'] === 'file' ? htmlspecialchars($lang->getLL('impexpcore_singlereco_filename')) . ' <strong>' . $info['subst']['relFileName'] . '</strong>' : '') . ($info['subst']['type'] === 'string' ? htmlspecialchars($lang->getLL('impexpcore_singlereco_value')) . ' <strong>' . $info['subst']['tokenValue'] . '</strong>' : '') . ($info['subst']['type'] === 'db' ? htmlspecialchars($lang->getLL('impexpcore_softrefsel_record')) . ' <strong>' . $info['subst']['recordRef'] . '</strong>' : '');
633  }
634  $pInfo['ref'] = 'SOFTREF';
635  $pInfo['type'] = 'softref';
636  $pInfo['_softRefInfo'] = $info;
637  $pInfo['type'] = 'softref';
638  $mode = $this->softrefCfg[$info['subst']['tokenID']]['mode'];
639  if ($info['‪error'] && $mode !== 'editable' && $mode !== 'exclude') {
640  $pInfo['msg'] .= $info['‪error'];
641  }
642  $lines[] = $pInfo;
643  // Add relations:
644  if ($info['subst']['type'] === 'db') {
645  list($tempTable, $tempUid) = explode(':', $info['subst']['recordRef']);
646  $this->addRelations([['table' => $tempTable, 'id' => $tempUid, 'tokenID' => $info['subst']['tokenID']]], $lines, $preCode_B, [], '');
647  }
648  // Add files:
649  if ($info['subst']['type'] === 'file') {
650  $this->addFiles([$info['file_ID']], $lines, $preCode_B, '', $info['subst']['tokenID']);
651  }
652  }
653  }
654  }
655 
667  public function addRelations($rels, &$lines, $preCode, $recurCheck = [], $htmlColorClass = '')
668  {
669  foreach ($rels as $dat) {
670  $table = $dat['table'];
671  $uid = $dat['id'];
672  $pInfo = [];
673  $pInfo['ref'] = $table . ':' . $uid;
674  if (in_array($pInfo['ref'], $recurCheck)) {
675  continue;
676  }
677  $iconName = 'status-status-checked';
678  $iconClass = '';
679  $staticFixed = false;
680  $record = null;
681  if ($uid > 0) {
682  $record = $this->dat['header']['records'][$table][$uid];
683  if (!is_array($record)) {
684  if ($this->isTableStatic($table) || $this->isExcluded($table, $uid) || $dat['tokenID'] && !$this->includeSoftref($dat['tokenID'])) {
685  $pInfo['title'] = htmlspecialchars('STATIC: ' . $pInfo['ref']);
686  $iconClass = 'text-info';
687  $staticFixed = true;
688  } else {
689  $doesRE = $this->doesRecordExist($table, $uid);
690  $lostPath = $this->getRecordPath($table === 'pages' ? $doesRE['uid'] : $doesRE['pid']);
691  $pInfo['title'] = htmlspecialchars($pInfo['ref']);
692  $pInfo['title'] = '<span title="' . htmlspecialchars($lostPath) . '">' . $pInfo['title'] . '</span>';
693  $pInfo['msg'] = 'LOST RELATION' . (!$doesRE ? ' (Record not found!)' : ' (Path: ' . $lostPath . ')');
694  $iconClass = 'text-danger';
695  $iconName = 'status-dialog-warning';
696  }
697  } else {
698  $pInfo['title'] = htmlspecialchars($record['title']);
699  $pInfo['title'] = '<span title="' . htmlspecialchars($this->getRecordPath(($table === 'pages' ? $record['uid'] : $record['pid']))) . '">' . $pInfo['title'] . '</span>';
700  }
701  } else {
702  // 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
703  $pInfo['title'] = htmlspecialchars('FIXED: ' . $pInfo['ref']);
704  $staticFixed = true;
705  }
706 
707  $icon = '<span class="' . $iconClass . '" title="' . htmlspecialchars($pInfo['ref']) . '">' . $this->iconFactory->getIcon($iconName, Icon::SIZE_SMALL)->render() . '</span>';
708 
709  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;' . $icon;
710  $pInfo['type'] = 'rel';
711  if (!$staticFixed || $this->showStaticRelations) {
712  $lines[] = $pInfo;
713  if (is_array($record) && is_array($record['rels'])) {
714  $this->addRelations($record['rels'], $lines, $preCode . '&nbsp;&nbsp;', array_merge($recurCheck, [$pInfo['ref']]));
715  }
716  }
717  }
718  }
719 
731  public function addFiles($rels, &$lines, $preCode, $htmlColorClass = '', $tokenID = '')
732  {
733  foreach ($rels as $ID) {
734  // Process file:
735  $pInfo = [];
736  $fI = $this->dat['header']['files'][$ID];
737  if (!is_array($fI)) {
738  if (!$tokenID || $this->includeSoftref($tokenID)) {
739  $pInfo['msg'] = 'MISSING FILE: ' . $ID;
740  $this->error('MISSING FILE: ' . $ID);
741  } else {
742  return;
743  }
744  }
745  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;' . $this->iconFactory->getIcon('status-reference-hard', Icon::SIZE_SMALL)->render();
746  $pInfo['title'] = htmlspecialchars($fI['filename']);
747  $pInfo['ref'] = 'FILE';
748  $pInfo['type'] = 'file';
749  // If import mode and there is a non-RTE softreference, check the destination directory:
750  if ($this->mode === 'import' && $tokenID && !$fI['RTE_ORIG_ID']) {
751  if (isset($fI['parentRelFileName'])) {
752  $pInfo['msg'] = 'Seems like this file is already referenced from within an HTML/CSS file. That takes precedence. ';
753  } else {
754  $testDirPrefix = PathUtility::dirname($fI['relFileName']) . '/';
755  $testDirPrefix2 = $this->verifyFolderAccess($testDirPrefix);
756  if (!$testDirPrefix2) {
757  $pInfo['msg'] = 'ERROR: There are no available filemounts to write file in! ';
758  } elseif ($testDirPrefix !== $testDirPrefix2) {
759  $pInfo['msg'] = '‪File will be attempted written to "' . $testDirPrefix2 . '". ';
760  }
761  }
762  // Check if file exists:
763  if (file_exists(Environment::getPublicPath() . '/' . $fI['relFileName'])) {
764  if ($this->update) {
765  $pInfo['updatePath'] .= '‪File exists.';
766  } else {
767  $pInfo['msg'] .= '‪File already exists! ';
768  }
769  }
770  // Check extension:
771  $fileProcObj = $this->getFileProcObj();
772  if ($fileProcObj->actionPerms['addFile']) {
773  $testFI = GeneralUtility::split_fileref(Environment::getPublicPath() . '/' . $fI['relFileName']);
774  if (!$this->allowPHPScripts && !$fileProcObj->checkIfAllowed($testFI['fileext'], $testFI['path'], $testFI['file'])) {
775  $pInfo['msg'] .= '‪File extension was not allowed!';
776  }
777  } else {
778  $pInfo['msg'] = 'You user profile does not allow you to create files on the server!';
779  }
780  }
781  $pInfo['showDiffContent'] = PathUtility::stripPathSitePrefix($this->fileIDMap[$ID]);
782  $lines[] = $pInfo;
783  unset($this->remainHeader['files'][$ID]);
784  // RTE originals:
785  if ($fI['RTE_ORIG_ID']) {
786  $ID = $fI['RTE_ORIG_ID'];
787  $pInfo = [];
788  $fI = $this->dat['header']['files'][$ID];
789  if (!is_array($fI)) {
790  $pInfo['msg'] = 'MISSING RTE original FILE: ' . $ID;
791  $this->error('MISSING RTE original FILE: ' . $ID);
792  }
793  $pInfo['showDiffContent'] = PathUtility::stripPathSitePrefix($this->fileIDMap[$ID]);
794  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . $this->iconFactory->getIcon('status-reference-hard', Icon::SIZE_SMALL)->render();
795  $pInfo['title'] = htmlspecialchars($fI['filename']) . ' <em>(Original)</em>';
796  $pInfo['ref'] = 'FILE';
797  $pInfo['type'] = 'file';
798  $lines[] = $pInfo;
799  unset($this->remainHeader['files'][$ID]);
800  }
801  // External resources:
802  if (is_array($fI['EXT_RES_ID'])) {
803  foreach ($fI['EXT_RES_ID'] as $extID) {
804  $pInfo = [];
805  $fI = $this->dat['header']['files'][$extID];
806  if (!is_array($fI)) {
807  $pInfo['msg'] = 'MISSING External Resource FILE: ' . $extID;
808  $this->error('MISSING External Resource FILE: ' . $extID);
809  } else {
810  $pInfo['updatePath'] = $fI['parentRelFileName'];
811  }
812  $pInfo['showDiffContent'] = PathUtility::stripPathSitePrefix($this->fileIDMap[$extID]);
813  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . $this->iconFactory->getIcon('actions-insert-reference', Icon::SIZE_SMALL)->render();
814  $pInfo['title'] = htmlspecialchars($fI['filename']) . ' <em>(Resource)</em>';
815  $pInfo['ref'] = 'FILE';
816  $pInfo['type'] = 'file';
817  $lines[] = $pInfo;
818  unset($this->remainHeader['files'][$extID]);
819  }
820  }
821  }
822  }
823 
831  public function checkDokType($checkTable, $doktype)
832  {
833  $allowedTableList = $GLOBALS['PAGES_TYPES'][$doktype]['allowedTables'] ?? $GLOBALS['PAGES_TYPES']['default']['allowedTables'];
834  $allowedArray = GeneralUtility::trimExplode(',', $allowedTableList, true);
835  // If all tables or the table is listed as an allowed type, return TRUE
836  if (strstr($allowedTableList, '*') || in_array($checkTable, $allowedArray)) {
837  return true;
838  }
839  return false;
840  }
841 
848  public function renderControls($r)
849  {
850  if ($this->mode === 'export') {
851  if ($r['type'] === 'record') {
852  return '<input type="checkbox" class="t3js-exclude-checkbox" name="tx_impexp[exclude][' . $r['ref'] . ']" id="checkExclude' . $r['ref'] . '" value="1" /> <label for="checkExclude' . $r['ref'] . '">' . htmlspecialchars($this->getLanguageService()->getLL('impexpcore_singlereco_exclude')) . '</label>';
853  }
854  return $r['type'] === 'softref' ? $this->softrefSelector($r['_softRefInfo']) : '';
855  }
856  // During import
857  // For softreferences with editable fields:
858  if ($r['type'] === 'softref' && is_array($r['_softRefInfo']['subst']) && $r['_softRefInfo']['subst']['tokenID']) {
859  $tokenID = $r['_softRefInfo']['subst']['tokenID'];
860  $cfg = $this->softrefCfg[$tokenID];
861  if ($cfg['mode'] === 'editable') {
862  return (strlen($cfg['title']) ? '<strong>' . htmlspecialchars($cfg['title']) . '</strong><br/>' : '') . htmlspecialchars($cfg['description']) . '<br/>
863  <input type="text" name="tx_impexp[softrefInputValues][' . $tokenID . ']" value="' . htmlspecialchars($this->softrefInputValues[$tokenID] ?? $cfg['defValue']) . '" />';
864  }
865  }
866 
867  return '';
868  }
869 
876  public function softrefSelector($cfg)
877  {
878  // Looking for file ID if any:
879  $fI = $cfg['file_ID'] ? $this->dat['header']['files'][$cfg['file_ID']] : [];
880  // Substitution scheme has to be around and RTE images MUST be exported.
881  if (is_array($cfg['subst']) && $cfg['subst']['tokenID'] && !$fI['RTE_ORIG_ID']) {
882  // Create options:
883  $optValues = [];
884  $optValues[''] = '';
885  $optValues['editable'] = $this->getLanguageService()->getLL('impexpcore_softrefsel_editable');
886  $optValues['exclude'] = $this->getLanguageService()->getLL('impexpcore_softrefsel_exclude');
887  // Get current value:
888  $value = $this->softrefCfg[$cfg['subst']['tokenID']]['mode'];
889  // Render options selector:
890  $selectorbox = $this->renderSelectBox('tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][mode]', $value, $optValues) . '<br/>';
891  if ($value === 'editable') {
892  $descriptionField = '';
893  // Title:
894  if (strlen($cfg['subst']['title'])) {
895  $descriptionField .= '
896  <input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][title]" value="' . htmlspecialchars($cfg['subst']['title']) . '" />
897  <strong>' . htmlspecialchars($cfg['subst']['title']) . '</strong><br/>';
898  }
899  // Description:
900  if (!strlen($cfg['subst']['description'])) {
901  $descriptionField .= '
902  ' . htmlspecialchars($this->getLanguageService()->getLL('impexpcore_printerror_description')) . '<br/>
903  <input type="text" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][description]" value="' . htmlspecialchars($this->softrefCfg[$cfg['subst']['tokenID']]['description']) . '" />';
904  } else {
905  $descriptionField .= '
906 
907  <input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][description]" value="' . htmlspecialchars($cfg['subst']['description']) . '" />' . htmlspecialchars($cfg['subst']['description']);
908  }
909  // Default Value:
910  $descriptionField .= '<input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][defValue]" value="' . htmlspecialchars($cfg['subst']['tokenValue']) . '" />';
911  } else {
912  $descriptionField = '';
913  }
914  return $selectorbox . $descriptionField;
915  }
916  return '';
917  }
918 
927  public function verifyFolderAccess($dirPrefix, $noAlternative = false)
928  {
929  // Check the absolute path for public web path, if the user has access - no problem
930  try {
931  ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier($dirPrefix);
932  return $dirPrefix;
933  } catch (InsufficientFolderAccessPermissionsException $e) {
934  // Check all storages available for the user as alternative
935  if (!$noAlternative) {
936  $fileStorages = $this->getBackendUser()->getFileStorages();
937  foreach ($fileStorages as $fileStorage) {
938  try {
939  $folder = $fileStorage->getFolder(rtrim($dirPrefix, '/'));
940  return $folder->getPublicUrl();
941  } catch (InsufficientFolderAccessPermissionsException $e) {
942  }
943  }
944  }
945  }
946  return false;
947  }
948 
949  /*****************************
950  * Helper functions of kinds
951  *****************************/
952 
956  protected function getTemporaryFolderName()
957  {
958  $temporaryPath = Environment::getVarPath() . '/transient/';
959  do {
960  $temporaryFolderName = $temporaryPath . 'export_temp_files_' . mt_rand(1, PHP_INT_MAX);
961  } while (is_dir($temporaryFolderName));
962  GeneralUtility::mkdir($temporaryFolderName);
963  return $temporaryFolderName;
964  }
965 
974  public function flatInversePageTree($idH, $a = [])
975  {
976  if (is_array($idH)) {
977  $idH = array_reverse($idH);
978  foreach ($idH as $k => $v) {
979  $a[$v['uid']] = $v['uid'];
980  if (is_array($v['subrow'])) {
981  $a = $this->flatInversePageTree($v['subrow'], $a);
982  }
983  }
984  }
985  return $a;
986  }
987 
994  public function isTableStatic($table)
995  {
996  if (is_array($GLOBALS['TCA'][$table])) {
997  return $GLOBALS['TCA'][$table]['ctrl']['is_static'] || in_array($table, $this->relStaticTables) || in_array('_ALL', $this->relStaticTables);
998  }
999  return false;
1000  }
1001 
1008  public function inclRelation($table)
1009  {
1010  return is_array($GLOBALS['TCA'][$table])
1011  && (in_array($table, $this->relOnlyTables) || in_array('_ALL', $this->relOnlyTables))
1012  && $this->getBackendUser()->check('tables_select', $table);
1013  }
1014 
1022  public function isExcluded($table, $uid)
1023  {
1024  return (bool)$this->excludeMap[$table . ':' . $uid];
1025  }
1026 
1033  public function includeSoftref($tokenID)
1034  {
1035  $mode = $this->softrefCfg[$tokenID]['mode'];
1036  return $tokenID && $mode !== 'exclude' && $mode !== 'editable';
1037  }
1038 
1045  public function checkPID($pid)
1046  {
1047  if (!isset($this->checkPID_cache[$pid])) {
1048  $this->checkPID_cache[$pid] = (bool)$this->getBackendUser()->isInWebMount($pid);
1049  }
1050  return $this->checkPID_cache[$pid];
1051  }
1052 
1060  public function dontIgnorePid($table, $uid)
1061  {
1062  return $this->import_mode[$table . ':' . $uid] !== 'ignore_pid' && (!$this->global_ignore_pid || $this->import_mode[$table . ':' . $uid] === 'respect_pid');
1063  }
1064 
1073  public function doesRecordExist($table, $uid, $fields = '')
1074  {
1075  return BackendUtility::getRecord($table, $uid, $fields ? $fields : 'uid,pid');
1076  }
1077 
1084  public function getRecordPath($pid)
1085  {
1086  if (!isset($this->cache_getRecordPath[$pid])) {
1087  $clause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
1088  $this->cache_getRecordPath[$pid] = (string)BackendUtility::getRecordPath($pid, $clause, 20);
1089  }
1090  return $this->cache_getRecordPath[$pid];
1091  }
1092 
1101  public function renderSelectBox($prefix, $value, $optValues)
1102  {
1103  $opt = [];
1104  $isSelFlag = 0;
1105  foreach ($optValues as $k => $v) {
1106  $sel = (string)$k === (string)$value ? ' selected="selected"' : '';
1107  if ($sel) {
1108  $isSelFlag++;
1109  }
1110  $opt[] = '<option value="' . htmlspecialchars($k) . '"' . $sel . '>' . htmlspecialchars($v) . '</option>';
1111  }
1112  if (!$isSelFlag && (string)$value !== '') {
1113  $opt[] = '<option value="' . htmlspecialchars($value) . '" selected="selected">' . htmlspecialchars('[\'' . $value . '\']') . '</option>';
1114  }
1115  return '<select name="' . $prefix . '">' . implode('', $opt) . '</select>';
1116  }
1117 
1128  public function compareRecords($databaseRecord, $importRecord, $table, $inverseDiff = false)
1129  {
1130  // Initialize:
1131  $output = [];
1132  $diffUtility = GeneralUtility::makeInstance(DiffUtility::class);
1133  // Check if both inputs are records:
1134  if (is_array($databaseRecord) && is_array($importRecord)) {
1135  // Traverse based on database record
1136  foreach ($databaseRecord as $fN => $value) {
1137  if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type'] !== 'passthrough') {
1138  if (isset($importRecord[$fN])) {
1139  if (trim($databaseRecord[$fN]) !== trim($importRecord[$fN])) {
1140  // Create diff-result:
1141  $output[$fN] = $diffUtility->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));
1142  }
1143  unset($importRecord[$fN]);
1144  }
1145  }
1146  }
1147  // Traverse remaining in import record:
1148  foreach ($importRecord as $fN => $value) {
1149  if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type'] !== 'passthrough') {
1150  $output[$fN] = '<strong>Field missing</strong> in database';
1151  }
1152  }
1153  // Create output:
1154  if (!empty($output)) {
1155  $tRows = [];
1156  foreach ($output as $fN => $state) {
1157  $tRows[] = '
1158  <tr>
1159  <td>' . htmlspecialchars($this->getLanguageService()->sL($GLOBALS['TCA'][$table]['columns'][$fN]['label'])) . ' (' . htmlspecialchars($fN) . ')</td>
1160  <td>' . $state . '</td>
1161  </tr>
1162  ';
1163  }
1164  $output = '<table class="table table-striped table-hover">' . implode('', $tRows) . '</table>';
1165  } else {
1166  $output = 'Match';
1167  }
1168  return '<strong class="text-nowrap">[' . htmlspecialchars($table . ':' . $importRecord['uid'] . ' => ' . $databaseRecord['uid']) . ']:</strong> ' . $output;
1169  }
1170  return 'ERROR: One of the inputs were not an array!';
1171  }
1172 
1179  public function getRTEoriginalFilename($string)
1180  {
1181  // If "magic image":
1182  if (GeneralUtility::isFirstPartOfStr($string, 'RTEmagicC_')) {
1183  // Find original file:
1184  $pI = pathinfo(substr($string, strlen('RTEmagicC_')));
1185  $filename = substr($pI['basename'], 0, -strlen('.' . $pI['extension']));
1186  $origFilePath = 'RTEmagicP_' . $filename;
1187  return $origFilePath;
1188  }
1189  return null;
1190  }
1191 
1197  public function getFileProcObj()
1198  {
1199  if ($this->fileProcObj === null) {
1200  $this->fileProcObj = GeneralUtility::makeInstance(ExtendedFileUtility::class);
1201  $this->fileProcObj->setActionPermissions();
1202  }
1203  return $this->fileProcObj;
1204  }
1205 
1212  public function callHook($name, $params)
1213  {
1214  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name] ?? [] as $hook) {
1215  GeneralUtility::callUserFunction($hook, $params, $this);
1216  }
1217  }
1218 
1226  public function setExcludeDisabledRecords($excludeDisabledRecords = false)
1227  {
1228  $this->excludeDisabledRecords = $excludeDisabledRecords;
1229  return $this;
1230  }
1232  /*****************************
1233  * Error handling
1234  *****************************/
1235 
1241  public function error($msg)
1242  {
1243  $this->errorLog[] = $msg;
1244  }
1245 
1251  public function printErrorLog()
1252  {
1253  return !empty($this->errorLog) ? DebugUtility::viewArray($this->errorLog) : '';
1254  }
1255 
1259  protected function getBackendUser()
1260  {
1261  return $GLOBALS['BE_USER'];
1262  }
1263 
1267  protected function getLanguageService()
1268  {
1269  return $GLOBALS['LANG'];
1270  }
1271 }
‪TYPO3\CMS\Core\Utility\DiffUtility
Definition: DiffUtility.php:23
‪TYPO3\CMS\Impexp\ImportExport\renderControls
‪string renderControls($r)
Definition: ImportExport.php:820
‪TYPO3\CMS\Impexp\ImportExport\isActive
‪bool isActive($table, $uid)
Definition: ImportExport.php:367
‪TYPO3\CMS\Impexp\ImportExport\$extensionDependencies
‪array $extensionDependencies
Definition: ImportExport.php:179
‪TYPO3\CMS\Impexp\ImportExport\$iconFactory
‪IconFactory $iconFactory
Definition: ImportExport.php:230
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:23
‪TYPO3\CMS\Impexp\ImportExport\$showStaticRelations
‪bool $showStaticRelations
Definition: ImportExport.php:73
‪TYPO3\CMS\Impexp\ImportExport\$mode
‪string $mode
Definition: ImportExport.php:85
‪TYPO3\CMS\Impexp\ImportExport\$errorLog
‪array $errorLog
Definition: ImportExport.php:191
‪TYPO3\CMS\Impexp\ImportExport\$allowPHPScripts
‪bool $allowPHPScripts
Definition: ImportExport.php:135
‪TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
Definition: InsufficientFolderAccessPermissionsException.php:21
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:25
‪TYPO3\CMS\Impexp\ImportExport\traversePageTree
‪traversePageTree($pT, &$lines, $preCode='')
Definition: ImportExport.php:332
‪TYPO3\CMS\Impexp\ImportExport\error
‪error($msg)
Definition: ImportExport.php:1213
‪TYPO3\CMS\Impexp\ImportExport\$import_mode
‪array $import_mode
Definition: ImportExport.php:111
‪TYPO3\CMS\Impexp\ImportExport\$checkPID_cache
‪array $checkPID_cache
Definition: ImportExport.php:203
‪TYPO3\CMS\Impexp\ImportExport\$showDiff
‪bool $showDiff
Definition: ImportExport.php:129
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:31
‪TYPO3\CMS\Impexp\ImportExport\$cache_getRecordPath
‪array $cache_getRecordPath
Definition: ImportExport.php:197
‪TYPO3\CMS\Impexp\ImportExport\$softrefCfg
‪array $softrefCfg
Definition: ImportExport.php:173
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:23
‪TYPO3\CMS\Impexp\ImportExport\displayContentOverview
‪array displayContentOverview()
Definition: ImportExport.php:269
‪TYPO3\CMS\Impexp\ImportExport\excludePageAndRecords
‪excludePageAndRecords($pageUid, $pageTree)
Definition: ImportExport.php:382
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:36
‪TYPO3\CMS\Impexp\ImportExport\traversePageRecords
‪traversePageRecords($pT, &$lines)
Definition: ImportExport.php:412
‪TYPO3\CMS\Core\Utility\File\ExtendedFileUtility
Definition: ExtendedFileUtility.php:63
‪TYPO3\CMS\Impexp\ImportExport\$relOnlyTables
‪array $relOnlyTables
Definition: ImportExport.php:154
‪TYPO3\CMS\Impexp\ImportExport\$fileadminFolderName
‪string $fileadminFolderName
Definition: ImportExport.php:79
‪TYPO3\CMS\Impexp\ImportExport\$doesImport
‪bool $doesImport
Definition: ImportExport.php:97
‪TYPO3\CMS\Impexp\ImportExport\singleRecordLines
‪singleRecordLines($table, $uid, &$lines, $preCode, $checkImportInPidRecord=false)
Definition: ImportExport.php:473
‪TYPO3\CMS\Impexp\ImportExport\traverseAllRecords
‪traverseAllRecords($pT, &$lines)
Definition: ImportExport.php:436
‪TYPO3\CMS\Impexp\ImportExport\$fileProcObj
‪ExtendedFileUtility $fileProcObj
Definition: ImportExport.php:222
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:33
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:23
‪TYPO3\CMS\Impexp\ImportExport\getLanguageService
‪LanguageService getLanguageService()
Definition: ImportExport.php:1239
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static bool isLoaded($key, $exitOnError=null)
Definition: ExtensionManagementUtility.php:115
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:45
‪TYPO3\CMS\Impexp\ImportExport\__construct
‪__construct()
Definition: ImportExport.php:242
‪TYPO3\CMS\Impexp\ImportExport
Definition: ImportExport.php:68
‪TYPO3\CMS\Backend\Utility\BackendUtility
Definition: BackendUtility.php:72
‪TYPO3\CMS\Core\Utility\DebugUtility
Definition: DebugUtility.php:24
‪TYPO3\CMS\Impexp\ImportExport\$update
‪bool $update
Definition: ImportExport.php:91
‪TYPO3\CMS\Impexp\ImportExport\$relStaticTables
‪array $relStaticTables
Definition: ImportExport.php:161
‪TYPO3\CMS\Impexp\ImportExport\$display_import_pid_record
‪array $display_import_pid_record
Definition: ImportExport.php:105
‪TYPO3\CMS\Impexp\ImportExport\$excludeDisabledRecords
‪bool $excludeDisabledRecords
Definition: ImportExport.php:237
‪TYPO3\CMS\Impexp\ImportExport\$import_mapId
‪array $import_mapId
Definition: ImportExport.php:185
‪TYPO3\CMS\Impexp\ImportExport\$remainHeader
‪array $remainHeader
Definition: ImportExport.php:226
‪TYPO3\CMS\Impexp\ImportExport\init
‪init()
Definition: ImportExport.php:254
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Impexp\ImportExport\getBackendUser
‪BackendUserAuthentication getBackendUser()
Definition: ImportExport.php:1231
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:39
‪TYPO3\CMS\Impexp\ImportExport\$excludeMap
‪array $excludeMap
Definition: ImportExport.php:167
‪TYPO3\CMS\Impexp\ImportExport\$compress
‪bool $compress
Definition: ImportExport.php:210
‪TYPO3\CMS\Impexp\ImportExport\addGeneralErrorsByTable
‪addGeneralErrorsByTable($table)
Definition: ImportExport.php:454
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:29
‪TYPO3\CMS\Impexp\ImportExport\$dat
‪array $dat
Definition: ImportExport.php:216
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Impexp\ImportExport\$global_ignore_pid
‪bool $global_ignore_pid
Definition: ImportExport.php:117
‪TYPO3\CMS\Impexp\ImportExport\$fileIDMap
‪array $fileIDMap
Definition: ImportExport.php:147
‪TYPO3\CMS\Impexp
‪TYPO3\CMS\Impexp\ImportExport\$softrefInputValues
‪array $softrefInputValues
Definition: ImportExport.php:141
‪TYPO3\CMS\Impexp\ImportExport\$force_all_UIDS
‪bool $force_all_UIDS
Definition: ImportExport.php:123