TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
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 
30 
64 abstract class ImportExport
65 {
71  public $showStaticRelations = false;
72 
78  public $fileadminFolderName = '';
79 
85  public $mode = '';
86 
92  public $update = false;
93 
99  public $doesImport = false;
100 
109 
115  public $import_mode = [];
116 
122  public $global_ignore_pid = false;
123 
129  public $force_all_UIDS = false;
130 
136  public $showDiff = false;
137 
143  public $allowPHPScripts = false;
144 
150  public $softrefInputValues = [];
151 
157  public $fileIDMap = [];
158 
165  public $relOnlyTables = [];
166 
173  public $relStaticTables = [];
174 
180  public $excludeMap = [];
181 
187  public $softrefCfg = [];
188 
195 
201  public $import_mapId = [];
202 
208  public $errorLog = [];
209 
215  public $cache_getRecordPath = [];
216 
222  public $checkPID_cache = [];
223 
230  public $compress = false;
231 
237  public $dat = [];
238 
244  protected $fileProcObj = null;
245 
249  protected $remainHeader = [];
250 
254  protected $iconFactory;
255 
262  protected $excludeDisabledRecords = false;
263 
267  public function __construct()
268  {
269  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
270  }
271 
272  /**************************
273  * Initialize
274  *************************/
275 
281  public function init()
282  {
283  $this->compress = function_exists('gzcompress');
284  $this->fileadminFolderName = !empty($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) ? rtrim($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') : 'fileadmin';
285  }
286 
287  /********************************************************
288  * Visual rendering of import/export memory, $this->dat
289  ********************************************************/
290 
296  public function displayContentOverview()
297  {
298  if (!isset($this->dat['header'])) {
299  return [];
300  }
301  // Check extension dependencies:
302  foreach ($this->dat['header']['extensionDependencies'] as $extKey) {
303  if (!empty($extKey) && !ExtensionManagementUtility::isLoaded($extKey)) {
304  $this->error('DEPENDENCY: The extension with key "' . $extKey . '" must be installed!');
305  }
306  }
307 
308  // Probably this is done to save memory space?
309  unset($this->dat['files']);
310 
311  $viewData = [];
312  // Traverse header:
313  $this->remainHeader = $this->dat['header'];
314  // If there is a page tree set, show that:
315  if (is_array($this->dat['header']['pagetree'])) {
316  reset($this->dat['header']['pagetree']);
317  $lines = [];
318  $this->traversePageTree($this->dat['header']['pagetree'], $lines);
319 
320  $viewData['dat'] = $this->dat;
321  $viewData['update'] = $this->update;
322  $viewData['showDiff'] = $this->showDiff;
323  if (!empty($lines)) {
324  foreach ($lines as &$r) {
325  $r['controls'] = $this->renderControls($r);
326  $r['fileSize'] = GeneralUtility::formatSize($r['size']);
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['fileSize'] = GeneralUtility::formatSize($r['size']);
345  $r['message'] = ($r['msg'] && !$this->doesImport ? '<span class="text-danger">' . htmlspecialchars($r['msg']) . '</span>' : '');
346  }
347  $viewData['remainingRecords'] = $lines;
348  }
349  }
350 
351  return $viewData;
352  }
353 
362  public function traversePageTree($pT, &$lines, $preCode = '')
363  {
364  foreach ($pT as $k => $v) {
365  if ($this->excludeDisabledRecords === true && !$this->isActive('pages', $k)) {
366  $this->excludePageAndRecords($k, $v);
367  continue;
368  }
369 
370  // Add this page:
371  $this->singleRecordLines('pages', $k, $lines, $preCode);
372  // Subrecords:
373  if (is_array($this->dat['header']['pid_lookup'][$k])) {
374  foreach ($this->dat['header']['pid_lookup'][$k] as $t => $recUidArr) {
375  if ($t != 'pages') {
376  foreach ($recUidArr as $ruid => $value) {
377  $this->singleRecordLines($t, $ruid, $lines, $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;');
378  }
379  }
380  }
381  unset($this->remainHeader['pid_lookup'][$k]);
382  }
383  // Subpages, called recursively:
384  if (is_array($v['subrow'])) {
385  $this->traversePageTree($v['subrow'], $lines, $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;');
386  }
387  }
388  }
389 
397  protected function isActive($table, $uid)
398  {
399  return
400  !isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'])
401  || !(bool)$this->dat['records'][$table . ':' . $uid]['data'][
402  $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['disabled']
403  ];
404  }
405 
413  protected function excludePageAndRecords($pageUid, $pageTree)
414  {
415  // Prevent having this page appear in "remaining records" table
416  unset($this->remainHeader['records']['pages'][$pageUid]);
417 
418  // Subrecords
419  if (is_array($this->dat['header']['pid_lookup'][$pageUid])) {
420  foreach ($this->dat['header']['pid_lookup'][$pageUid] as $table => $recordData) {
421  if ($table != 'pages') {
422  foreach (array_keys($recordData) as $uid) {
423  unset($this->remainHeader['records'][$table][$uid]);
424  }
425  }
426  }
427  unset($this->remainHeader['pid_lookup'][$pageUid]);
428  }
429  // Subpages excluded recursively
430  if (is_array($pageTree['subrow'])) {
431  foreach ($pageTree['subrow'] as $subPageUid => $subPageTree) {
432  $this->excludePageAndRecords($subPageUid, $subPageTree);
433  }
434  }
435  }
436 
444  public function traversePageRecords($pT, &$lines)
445  {
446  foreach ($pT as $k => $rHeader) {
447  $this->singleRecordLines('pages', $k, $lines, '', 1);
448  // Subrecords:
449  if (is_array($this->dat['header']['pid_lookup'][$k])) {
450  foreach ($this->dat['header']['pid_lookup'][$k] as $t => $recUidArr) {
451  if ($t != 'pages') {
452  foreach ($recUidArr as $ruid => $value) {
453  $this->singleRecordLines($t, $ruid, $lines, '&nbsp;&nbsp;&nbsp;&nbsp;');
454  }
455  }
456  }
457  unset($this->remainHeader['pid_lookup'][$k]);
458  }
459  }
460  }
461 
469  public function traverseAllRecords($pT, &$lines)
470  {
471  foreach ($pT as $t => $recUidArr) {
472  $this->addGeneralErrorsByTable($t);
473  if ($t != 'pages') {
474  $preCode = '';
475  foreach ($recUidArr as $ruid => $value) {
476  $this->singleRecordLines($t, $ruid, $lines, $preCode, 1);
477  }
478  }
479  }
480  }
481 
488  protected function addGeneralErrorsByTable($table)
489  {
490  if ($this->update && $table === 'sys_file') {
491  $this->error('Updating sys_file records is not supported! They will be imported as new records!');
492  }
493  if ($this->force_all_UIDS && $table === 'sys_file') {
494  $this->error('Forcing uids of sys_file records is not supported! They will be imported as new records!');
495  }
496  }
497 
508  public function singleRecordLines($table, $uid, &$lines, $preCode, $checkImportInPidRecord = false)
509  {
510  // Get record:
511  $record = $this->dat['header']['records'][$table][$uid];
512  unset($this->remainHeader['records'][$table][$uid]);
513  if (!is_array($record) && !($table === 'pages' && !$uid)) {
514  $this->error('MISSING RECORD: ' . $table . ':' . $uid);
515  }
516  // Begin to create the line arrays information record, pInfo:
517  $pInfo = [];
518  $pInfo['ref'] = $table . ':' . $uid;
519  // Unknown table name:
520  $lang = $this->getLanguageService();
521  if ($table === '_SOFTREF_') {
522  $pInfo['preCode'] = $preCode;
523  $pInfo['title'] = '<em>' . htmlspecialchars($lang->getLL('impexpcore_singlereco_softReferencesFiles')) . '</em>';
524  } elseif (!isset($GLOBALS['TCA'][$table])) {
525  // Unknown table name:
526  $pInfo['preCode'] = $preCode;
527  $pInfo['msg'] = 'UNKNOWN TABLE \'' . $pInfo['ref'] . '\'';
528  $pInfo['title'] = '<em>' . htmlspecialchars($record['title']) . '</em>';
529  } else {
530  // prepare data attribute telling whether the record is active or hidden, allowing frontend bulk selection
531  $pInfo['active'] = $this->isActive($table, $uid) ? 'active' : 'hidden';
532 
533  // Otherwise, set table icon and title.
534  // Import Validation (triggered by $this->display_import_pid_record) will show messages if import is not possible of various items.
535  if (is_array($this->display_import_pid_record) && !empty($this->display_import_pid_record)) {
536  if ($checkImportInPidRecord) {
537  if (!$this->getBackendUser()->doesUserHaveAccess($this->display_import_pid_record, ($table === 'pages' ? 8 : 16))) {
538  $pInfo['msg'] .= '\'' . $pInfo['ref'] . '\' cannot be INSERTED on this page! ';
539  }
540  if (!$this->checkDokType($table, $this->display_import_pid_record['doktype']) && !$GLOBALS['TCA'][$table]['ctrl']['rootLevel']) {
541  $pInfo['msg'] .= '\'' . $table . '\' cannot be INSERTED on this page type (change page type to \'Folder\'.) ';
542  }
543  }
544  if (!$this->getBackendUser()->check('tables_modify', $table)) {
545  $pInfo['msg'] .= 'You are not allowed to CREATE \'' . $table . '\' tables! ';
546  }
547  if ($GLOBALS['TCA'][$table]['ctrl']['readOnly']) {
548  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is READ ONLY! ';
549  }
550  if ($GLOBALS['TCA'][$table]['ctrl']['adminOnly'] && !$this->getBackendUser()->isAdmin()) {
551  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is ADMIN ONLY! ';
552  }
553  if ($GLOBALS['TCA'][$table]['ctrl']['is_static']) {
554  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is a STATIC TABLE! ';
555  }
556  if ((int)$GLOBALS['TCA'][$table]['ctrl']['rootLevel'] === 1) {
557  $pInfo['msg'] .= 'TABLE \'' . $table . '\' will be inserted on ROOT LEVEL! ';
558  }
559  $diffInverse = false;
560  $recInf = null;
561  if ($this->update) {
562  // In case of update-PREVIEW we swap the diff-sources.
563  $diffInverse = true;
564  $recInf = $this->doesRecordExist($table, $uid, $this->showDiff ? '*' : '');
565  $pInfo['updatePath'] = $recInf ? htmlspecialchars($this->getRecordPath($recInf['pid'])) : '<strong>NEW!</strong>';
566  // Mode selector:
567  $optValues = [];
568  $optValues[] = $recInf ? $lang->getLL('impexpcore_singlereco_update') : $lang->getLL('impexpcore_singlereco_insert');
569  if ($recInf) {
570  $optValues['as_new'] = $lang->getLL('impexpcore_singlereco_importAsNew');
571  }
572  if ($recInf) {
573  if (!$this->global_ignore_pid) {
574  $optValues['ignore_pid'] = $lang->getLL('impexpcore_singlereco_ignorePid');
575  } else {
576  $optValues['respect_pid'] = $lang->getLL('impexpcore_singlereco_respectPid');
577  }
578  }
579  if (!$recInf && $this->getBackendUser()->isAdmin()) {
580  $optValues['force_uid'] = sprintf($lang->getLL('impexpcore_singlereco_forceUidSAdmin'), $uid);
581  }
582  $optValues['exclude'] = $lang->getLL('impexpcore_singlereco_exclude');
583  if ($table === 'sys_file') {
584  $pInfo['updateMode'] = '';
585  } else {
586  $pInfo['updateMode'] = $this->renderSelectBox('tx_impexp[import_mode][' . $table . ':' . $uid . ']', $this->import_mode[$table . ':' . $uid], $optValues);
587  }
588  }
589  // Diff view:
590  if ($this->showDiff) {
591  // For IMPORTS, get new id:
592  if ($newUid = $this->import_mapId[$table][$uid]) {
593  $diffInverse = false;
594  $recInf = $this->doesRecordExist($table, $newUid, '*');
595  BackendUtility::workspaceOL($table, $recInf);
596  }
597  if (is_array($recInf)) {
598  $pInfo['showDiffContent'] = $this->compareRecords($recInf, $this->dat['records'][$table . ':' . $uid]['data'], $table, $diffInverse);
599  }
600  }
601  }
602  $pInfo['preCode'] = $preCode . '<span title="' . htmlspecialchars($table . ':' . $uid) . '">'
603  . $this->iconFactory->getIconForRecord($table, (array)$this->dat['records'][$table . ':' . $uid]['data'], Icon::SIZE_SMALL)->render()
604  . '</span>';
605  $pInfo['title'] = htmlspecialchars($record['title']);
606  // View page:
607  if ($table === 'pages') {
608  $viewID = $this->mode === 'export' ? $uid : ($this->doesImport ? $this->import_mapId['pages'][$uid] : 0);
609  if ($viewID) {
610  $pInfo['title'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($viewID)) . 'return false;">' . $pInfo['title'] . '</a>';
611  }
612  }
613  }
614  $pInfo['type'] = 'record';
615  $pInfo['size'] = $record['size'];
616  $lines[] = $pInfo;
617  // File relations:
618  if (is_array($record['filerefs'])) {
619  $this->addFiles($record['filerefs'], $lines, $preCode);
620  }
621  // DB relations
622  if (is_array($record['rels'])) {
623  $this->addRelations($record['rels'], $lines, $preCode);
624  }
625  // Soft ref
626  if (!empty($record['softrefs'])) {
627  $preCode_A = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;';
628  $preCode_B = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
629  foreach ($record['softrefs'] as $info) {
630  $pInfo = [];
631  $pInfo['preCode'] = $preCode_A . $this->iconFactory->getIcon('status-status-reference-soft', Icon::SIZE_SMALL)->render();
632  $pInfo['title'] = '<em>' . $info['field'] . ', "' . $info['spKey'] . '" </em>: <span title="' . htmlspecialchars($info['matchString']) . '">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['matchString'], 60)) . '</span>';
633  if ($info['subst']['type']) {
634  if (strlen($info['subst']['title'])) {
635  $pInfo['title'] .= '<br/>' . $preCode_B . '<strong>' . htmlspecialchars($lang->getLL('impexpcore_singlereco_title')) . '</strong> ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['subst']['title'], 60));
636  }
637  if (strlen($info['subst']['description'])) {
638  $pInfo['title'] .= '<br/>' . $preCode_B . '<strong>' . htmlspecialchars($lang->getLL('impexpcore_singlereco_descr')) . '</strong> ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['subst']['description'], 60));
639  }
640  $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>' : '');
641  }
642  $pInfo['ref'] = 'SOFTREF';
643  $pInfo['size'] = '';
644  $pInfo['type'] = 'softref';
645  $pInfo['_softRefInfo'] = $info;
646  $pInfo['type'] = 'softref';
647  $mode = $this->softrefCfg[$info['subst']['tokenID']]['mode'];
648  if ($info['error'] && $mode !== 'editable' && $mode !== 'exclude') {
649  $pInfo['msg'] .= $info['error'];
650  }
651  $lines[] = $pInfo;
652  // Add relations:
653  if ($info['subst']['type'] == 'db') {
654  list($tempTable, $tempUid) = explode(':', $info['subst']['recordRef']);
655  $this->addRelations([['table' => $tempTable, 'id' => $tempUid, 'tokenID' => $info['subst']['tokenID']]], $lines, $preCode_B, [], '');
656  }
657  // Add files:
658  if ($info['subst']['type'] == 'file') {
659  $this->addFiles([$info['file_ID']], $lines, $preCode_B, '', $info['subst']['tokenID']);
660  }
661  }
662  }
663  }
664 
677  public function addRelations($rels, &$lines, $preCode, $recurCheck = [], $htmlColorClass = '')
678  {
679  foreach ($rels as $dat) {
680  $table = $dat['table'];
681  $uid = $dat['id'];
682  $pInfo = [];
683  $pInfo['ref'] = $table . ':' . $uid;
684  if (in_array($pInfo['ref'], $recurCheck)) {
685  continue;
686  }
687  $iconName = 'status-status-checked';
688  $iconClass = '';
689  $staticFixed = false;
690  $record = null;
691  if ($uid > 0) {
692  $record = $this->dat['header']['records'][$table][$uid];
693  if (!is_array($record)) {
694  if ($this->isTableStatic($table) || $this->isExcluded($table, $uid) || $dat['tokenID'] && !$this->includeSoftref($dat['tokenID'])) {
695  $pInfo['title'] = htmlspecialchars('STATIC: ' . $pInfo['ref']);
696  $iconClass = 'text-info';
697  $staticFixed = true;
698  } else {
699  $doesRE = $this->doesRecordExist($table, $uid);
700  $lostPath = $this->getRecordPath($table === 'pages' ? $doesRE['uid'] : $doesRE['pid']);
701  $pInfo['title'] = htmlspecialchars($pInfo['ref']);
702  $pInfo['title'] = '<span title="' . htmlspecialchars($lostPath) . '">' . $pInfo['title'] . '</span>';
703  $pInfo['msg'] = 'LOST RELATION' . (!$doesRE ? ' (Record not found!)' : ' (Path: ' . $lostPath . ')');
704  $iconClass = 'text-danger';
705  $iconName = 'status-dialog-warning';
706  }
707  } else {
708  $pInfo['title'] = htmlspecialchars($record['title']);
709  $pInfo['title'] = '<span title="' . htmlspecialchars($this->getRecordPath(($table === 'pages' ? $record['uid'] : $record['pid']))) . '">' . $pInfo['title'] . '</span>';
710  }
711  } else {
712  // 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
713  $pInfo['title'] = htmlspecialchars('FIXED: ' . $pInfo['ref']);
714  $staticFixed = true;
715  }
716 
717  $icon = '<span class="' . $iconClass . '" title="' . htmlspecialchars($pInfo['ref']) . '">' . $this->iconFactory->getIcon($iconName, Icon::SIZE_SMALL)->render() . '</span>';
718 
719  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;' . $icon;
720  $pInfo['type'] = 'rel';
721  if (!$staticFixed || $this->showStaticRelations) {
722  $lines[] = $pInfo;
723  if (is_array($record) && is_array($record['rels'])) {
724  $this->addRelations($record['rels'], $lines, $preCode . '&nbsp;&nbsp;', array_merge($recurCheck, [$pInfo['ref']]));
725  }
726  }
727  }
728  }
729 
742  public function addFiles($rels, &$lines, $preCode, $htmlColorClass = '', $tokenID = '')
743  {
744  foreach ($rels as $ID) {
745  // Process file:
746  $pInfo = [];
747  $fI = $this->dat['header']['files'][$ID];
748  if (!is_array($fI)) {
749  if (!$tokenID || $this->includeSoftref($tokenID)) {
750  $pInfo['msg'] = 'MISSING FILE: ' . $ID;
751  $this->error('MISSING FILE: ' . $ID);
752  } else {
753  return;
754  }
755  }
756  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;' . $this->iconFactory->getIcon('status-status-reference-hard', Icon::SIZE_SMALL)->render();
757  $pInfo['title'] = htmlspecialchars($fI['filename']);
758  $pInfo['ref'] = 'FILE';
759  $pInfo['size'] = $fI['filesize'];
760  $pInfo['type'] = 'file';
761  // If import mode and there is a non-RTE softreference, check the destination directory:
762  if ($this->mode === 'import' && $tokenID && !$fI['RTE_ORIG_ID']) {
763  if (isset($fI['parentRelFileName'])) {
764  $pInfo['msg'] = 'Seems like this file is already referenced from within an HTML/CSS file. That takes precedence. ';
765  } else {
766  $testDirPrefix = PathUtility::dirname($fI['relFileName']) . '/';
767  $testDirPrefix2 = $this->verifyFolderAccess($testDirPrefix);
768  if (!$testDirPrefix2) {
769  $pInfo['msg'] = 'ERROR: There are no available filemounts to write file in! ';
770  } elseif ($testDirPrefix !== $testDirPrefix2) {
771  $pInfo['msg'] = 'File will be attempted written to "' . $testDirPrefix2 . '". ';
772  }
773  }
774  // Check if file exists:
775  if (file_exists(PATH_site . $fI['relFileName'])) {
776  if ($this->update) {
777  $pInfo['updatePath'] .= 'File exists.';
778  } else {
779  $pInfo['msg'] .= 'File already exists! ';
780  }
781  }
782  // Check extension:
783  $fileProcObj = $this->getFileProcObj();
784  if ($fileProcObj->actionPerms['addFile']) {
785  $testFI = GeneralUtility::split_fileref(PATH_site . $fI['relFileName']);
786  if (!$this->allowPHPScripts && !$fileProcObj->checkIfAllowed($testFI['fileext'], $testFI['path'], $testFI['file'])) {
787  $pInfo['msg'] .= 'File extension was not allowed!';
788  }
789  } else {
790  $pInfo['msg'] = 'You user profile does not allow you to create files on the server!';
791  }
792  }
793  $pInfo['showDiffContent'] = PathUtility::stripPathSitePrefix($this->fileIDMap[$ID]);
794  $lines[] = $pInfo;
795  unset($this->remainHeader['files'][$ID]);
796  // RTE originals:
797  if ($fI['RTE_ORIG_ID']) {
798  $ID = $fI['RTE_ORIG_ID'];
799  $pInfo = [];
800  $fI = $this->dat['header']['files'][$ID];
801  if (!is_array($fI)) {
802  $pInfo['msg'] = 'MISSING RTE original FILE: ' . $ID;
803  $this->error('MISSING RTE original FILE: ' . $ID);
804  }
805  $pInfo['showDiffContent'] = PathUtility::stripPathSitePrefix($this->fileIDMap[$ID]);
806  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . $this->iconFactory->getIcon('status-status-reference-hard', Icon::SIZE_SMALL)->render();
807  $pInfo['title'] = htmlspecialchars($fI['filename']) . ' <em>(Original)</em>';
808  $pInfo['ref'] = 'FILE';
809  $pInfo['size'] = $fI['filesize'];
810  $pInfo['type'] = 'file';
811  $lines[] = $pInfo;
812  unset($this->remainHeader['files'][$ID]);
813  }
814  // External resources:
815  if (is_array($fI['EXT_RES_ID'])) {
816  foreach ($fI['EXT_RES_ID'] as $extID) {
817  $pInfo = [];
818  $fI = $this->dat['header']['files'][$extID];
819  if (!is_array($fI)) {
820  $pInfo['msg'] = 'MISSING External Resource FILE: ' . $extID;
821  $this->error('MISSING External Resource FILE: ' . $extID);
822  } else {
823  $pInfo['updatePath'] = $fI['parentRelFileName'];
824  }
825  $pInfo['showDiffContent'] = PathUtility::stripPathSitePrefix($this->fileIDMap[$extID]);
826  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . $this->iconFactory->getIcon('actions-insert-reference', Icon::SIZE_SMALL)->render();
827  $pInfo['title'] = htmlspecialchars($fI['filename']) . ' <em>(Resource)</em>';
828  $pInfo['ref'] = 'FILE';
829  $pInfo['size'] = $fI['filesize'];
830  $pInfo['type'] = 'file';
831  $lines[] = $pInfo;
832  unset($this->remainHeader['files'][$extID]);
833  }
834  }
835  }
836  }
837 
845  public function checkDokType($checkTable, $doktype)
846  {
847  $allowedTableList = isset($GLOBALS['PAGES_TYPES'][$doktype]['allowedTables']) ? $GLOBALS['PAGES_TYPES'][$doktype]['allowedTables'] : $GLOBALS['PAGES_TYPES']['default']['allowedTables'];
848  $allowedArray = GeneralUtility::trimExplode(',', $allowedTableList, true);
849  // If all tables or the table is listed as an allowed type, return TRUE
850  if (strstr($allowedTableList, '*') || in_array($checkTable, $allowedArray)) {
851  return true;
852  }
853  return false;
854  }
855 
862  public function renderControls($r)
863  {
864  if ($this->mode === 'export') {
865  if ($r['type'] === 'record') {
866  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>';
867  } else {
868  return $r['type'] == 'softref' ? $this->softrefSelector($r['_softRefInfo']) : '';
869  }
870  } else {
871  // During import
872  // For softreferences with editable fields:
873  if ($r['type'] == 'softref' && is_array($r['_softRefInfo']['subst']) && $r['_softRefInfo']['subst']['tokenID']) {
874  $tokenID = $r['_softRefInfo']['subst']['tokenID'];
875  $cfg = $this->softrefCfg[$tokenID];
876  if ($cfg['mode'] === 'editable') {
877  return (strlen($cfg['title']) ? '<strong>' . htmlspecialchars($cfg['title']) . '</strong><br/>' : '') . htmlspecialchars($cfg['description']) . '<br/>
878  <input type="text" name="tx_impexp[softrefInputValues][' . $tokenID . ']" value="' . htmlspecialchars((isset($this->softrefInputValues[$tokenID]) ? $this->softrefInputValues[$tokenID] : $cfg['defValue'])) . '" />';
879  }
880  }
881  }
882  return '';
883  }
884 
891  public function softrefSelector($cfg)
892  {
893  // Looking for file ID if any:
894  $fI = $cfg['file_ID'] ? $this->dat['header']['files'][$cfg['file_ID']] : [];
895  // Substitution scheme has to be around and RTE images MUST be exported.
896  if (is_array($cfg['subst']) && $cfg['subst']['tokenID'] && !$fI['RTE_ORIG_ID']) {
897  // Create options:
898  $optValues = [];
899  $optValues[''] = '';
900  $optValues['editable'] = $this->getLanguageService()->getLL('impexpcore_softrefsel_editable');
901  $optValues['exclude'] = $this->getLanguageService()->getLL('impexpcore_softrefsel_exclude');
902  // Get current value:
903  $value = $this->softrefCfg[$cfg['subst']['tokenID']]['mode'];
904  // Render options selector:
905  $selectorbox = $this->renderSelectBox(('tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][mode]'), $value, $optValues) . '<br/>';
906  if ($value === 'editable') {
907  $descriptionField = '';
908  // Title:
909  if (strlen($cfg['subst']['title'])) {
910  $descriptionField .= '
911  <input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][title]" value="' . htmlspecialchars($cfg['subst']['title']) . '" />
912  <strong>' . htmlspecialchars($cfg['subst']['title']) . '</strong><br/>';
913  }
914  // Description:
915  if (!strlen($cfg['subst']['description'])) {
916  $descriptionField .= '
917  ' . htmlspecialchars($this->getLanguageService()->getLL('impexpcore_printerror_description')) . '<br/>
918  <input type="text" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][description]" value="' . htmlspecialchars($this->softrefCfg[$cfg['subst']['tokenID']]['description']) . '" />';
919  } else {
920  $descriptionField .= '
921 
922  <input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][description]" value="' . htmlspecialchars($cfg['subst']['description']) . '" />' . htmlspecialchars($cfg['subst']['description']);
923  }
924  // Default Value:
925  $descriptionField .= '<input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][defValue]" value="' . htmlspecialchars($cfg['subst']['tokenValue']) . '" />';
926  } else {
927  $descriptionField = '';
928  }
929  return $selectorbox . $descriptionField;
930  }
931  return '';
932  }
933 
942  public function verifyFolderAccess($dirPrefix, $noAlternative = false)
943  {
944  // Check the absolute path for PATH_site, if the user has access - no problem
945  try {
946  ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier($dirPrefix);
947  return $dirPrefix;
948  } catch (InsufficientFolderAccessPermissionsException $e) {
949  // Check all storages available for the user as alternative
950  if (!$noAlternative) {
951  $fileStorages = $this->getBackendUser()->getFileStorages();
952  foreach ($fileStorages as $fileStorage) {
953  try {
954  $folder = $fileStorage->getFolder(rtrim($dirPrefix, '/'));
955  return $folder->getPublicUrl();
956  } catch (InsufficientFolderAccessPermissionsException $e) {
957  }
958  }
959  }
960  }
961  return false;
962  }
963 
964  /*****************************
965  * Helper functions of kinds
966  *****************************/
967 
972  protected function getTemporaryFolderName()
973  {
974  $temporaryPath = PATH_site . 'typo3temp/var/transient/';
975  do {
976  $temporaryFolderName = $temporaryPath . 'export_temp_files_' . mt_rand(1, PHP_INT_MAX);
977  } while (is_dir($temporaryFolderName));
978  GeneralUtility::mkdir($temporaryFolderName);
979  return $temporaryFolderName;
980  }
981 
990  public function flatInversePageTree($idH, $a = [])
991  {
992  if (is_array($idH)) {
993  $idH = array_reverse($idH);
994  foreach ($idH as $k => $v) {
995  $a[$v['uid']] = $v['uid'];
996  if (is_array($v['subrow'])) {
997  $a = $this->flatInversePageTree($v['subrow'], $a);
998  }
999  }
1000  }
1001  return $a;
1002  }
1003 
1010  public function isTableStatic($table)
1011  {
1012  if (is_array($GLOBALS['TCA'][$table])) {
1013  return $GLOBALS['TCA'][$table]['ctrl']['is_static'] || in_array($table, $this->relStaticTables) || in_array('_ALL', $this->relStaticTables);
1014  }
1015  return false;
1016  }
1017 
1024  public function inclRelation($table)
1025  {
1026  return is_array($GLOBALS['TCA'][$table])
1027  && (in_array($table, $this->relOnlyTables) || in_array('_ALL', $this->relOnlyTables))
1028  && $this->getBackendUser()->check('tables_select', $table);
1029  }
1030 
1038  public function isExcluded($table, $uid)
1039  {
1040  return (bool)$this->excludeMap[$table . ':' . $uid];
1041  }
1042 
1049  public function includeSoftref($tokenID)
1050  {
1051  $mode = $this->softrefCfg[$tokenID]['mode'];
1052  return $tokenID && $mode !== 'exclude' && $mode !== 'editable';
1053  }
1054 
1061  public function checkPID($pid)
1062  {
1063  if (!isset($this->checkPID_cache[$pid])) {
1064  $this->checkPID_cache[$pid] = (bool)$this->getBackendUser()->isInWebMount($pid);
1065  }
1066  return $this->checkPID_cache[$pid];
1067  }
1068 
1076  public function dontIgnorePid($table, $uid)
1077  {
1078  return $this->import_mode[$table . ':' . $uid] !== 'ignore_pid' && (!$this->global_ignore_pid || $this->import_mode[$table . ':' . $uid] === 'respect_pid');
1079  }
1080 
1089  public function doesRecordExist($table, $uid, $fields = '')
1090  {
1091  return BackendUtility::getRecord($table, $uid, $fields ? $fields : 'uid,pid');
1092  }
1093 
1100  public function getRecordPath($pid)
1101  {
1102  if (!isset($this->cache_getRecordPath[$pid])) {
1103  $clause = $this->getBackendUser()->getPagePermsClause(1);
1104  $this->cache_getRecordPath[$pid] = (string)BackendUtility::getRecordPath($pid, $clause, 20);
1105  }
1106  return $this->cache_getRecordPath[$pid];
1107  }
1108 
1117  public function renderSelectBox($prefix, $value, $optValues)
1118  {
1119  $opt = [];
1120  $isSelFlag = 0;
1121  foreach ($optValues as $k => $v) {
1122  $sel = (string)$k === (string)$value ? ' selected="selected"' : '';
1123  if ($sel) {
1124  $isSelFlag++;
1125  }
1126  $opt[] = '<option value="' . htmlspecialchars($k) . '"' . $sel . '>' . htmlspecialchars($v) . '</option>';
1127  }
1128  if (!$isSelFlag && (string)$value !== '') {
1129  $opt[] = '<option value="' . htmlspecialchars($value) . '" selected="selected">' . htmlspecialchars(('[\'' . $value . '\']')) . '</option>';
1130  }
1131  return '<select name="' . $prefix . '">' . implode('', $opt) . '</select>';
1132  }
1133 
1144  public function compareRecords($databaseRecord, $importRecord, $table, $inverseDiff = false)
1145  {
1146  // Initialize:
1147  $output = [];
1148  $diffUtility = GeneralUtility::makeInstance(DiffUtility::class);
1149  // Check if both inputs are records:
1150  if (is_array($databaseRecord) && is_array($importRecord)) {
1151  // Traverse based on database record
1152  foreach ($databaseRecord as $fN => $value) {
1153  if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type'] != 'passthrough') {
1154  if (isset($importRecord[$fN])) {
1155  if (trim($databaseRecord[$fN]) !== trim($importRecord[$fN])) {
1156  // Create diff-result:
1157  $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));
1158  }
1159  unset($importRecord[$fN]);
1160  }
1161  }
1162  }
1163  // Traverse remaining in import record:
1164  foreach ($importRecord as $fN => $value) {
1165  if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type'] !== 'passthrough') {
1166  $output[$fN] = '<strong>Field missing</strong> in database';
1167  }
1168  }
1169  // Create output:
1170  if (!empty($output)) {
1171  $tRows = [];
1172  foreach ($output as $fN => $state) {
1173  $tRows[] = '
1174  <tr>
1175  <td>' . htmlspecialchars($this->getLanguageService()->sL($GLOBALS['TCA'][$table]['columns'][$fN]['label'])) . ' (' . htmlspecialchars($fN) . ')</td>
1176  <td>' . $state . '</td>
1177  </tr>
1178  ';
1179  }
1180  $output = '<table class="table table-striped table-hover">' . implode('', $tRows) . '</table>';
1181  } else {
1182  $output = 'Match';
1183  }
1184  return '<strong class="text-nowrap">[' . htmlspecialchars(($table . ':' . $importRecord['uid'] . ' => ' . $databaseRecord['uid'])) . ']:</strong> ' . $output;
1185  }
1186  return 'ERROR: One of the inputs were not an array!';
1187  }
1188 
1195  public function getRTEoriginalFilename($string)
1196  {
1197  // If "magic image":
1198  if (GeneralUtility::isFirstPartOfStr($string, 'RTEmagicC_')) {
1199  // Find original file:
1200  $pI = pathinfo(substr($string, strlen('RTEmagicC_')));
1201  $filename = substr($pI['basename'], 0, -strlen(('.' . $pI['extension'])));
1202  $origFilePath = 'RTEmagicP_' . $filename;
1203  return $origFilePath;
1204  }
1205  return null;
1206  }
1207 
1213  public function getFileProcObj()
1214  {
1215  if ($this->fileProcObj === null) {
1216  $this->fileProcObj = GeneralUtility::makeInstance(ExtendedFileUtility::class);
1217  $this->fileProcObj->setActionPermissions();
1218  }
1219  return $this->fileProcObj;
1220  }
1221 
1229  public function callHook($name, $params)
1230  {
1231  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name])) {
1232  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name] as $hook) {
1233  GeneralUtility::callUserFunction($hook, $params, $this);
1234  }
1235  }
1236  }
1237 
1245  public function setExcludeDisabledRecords($excludeDisabledRecords = false)
1246  {
1247  $this->excludeDisabledRecords = $excludeDisabledRecords;
1248  return $this;
1249  }
1250 
1251  /*****************************
1252  * Error handling
1253  *****************************/
1254 
1261  public function error($msg)
1262  {
1263  $this->errorLog[] = $msg;
1264  }
1265 
1271  public function printErrorLog()
1272  {
1273  return !empty($this->errorLog) ? DebugUtility::viewArray($this->errorLog) : '';
1274  }
1275 
1279  protected function getBackendUser()
1280  {
1281  return $GLOBALS['BE_USER'];
1282  }
1283 
1287  protected function getLanguageService()
1288  {
1289  return $GLOBALS['LANG'];
1290  }
1291 }
singleRecordLines($table, $uid, &$lines, $preCode, $checkImportInPidRecord=false)
traversePageTree($pT, &$lines, $preCode= '')
static formatSize($sizeInBytes, $labels= '', $base=0)
excludePageAndRecords($pageUid, $pageTree)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)