TYPO3 CMS  TYPO3_7-6
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 
29 
63 abstract class ImportExport
64 {
70  public $showStaticRelations = false;
71 
77  public $fileadminFolderName = '';
78 
84  public $mode = '';
85 
91  public $update = false;
92 
98  public $doesImport = false;
99 
108 
114  public $import_mode = [];
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 $softrefInputValues = [];
150 
156  public $fileIDMap = [];
157 
163  public $relOnlyTables = [];
164 
171  public $relStaticTables = [];
172 
178  public $excludeMap = [];
179 
185  public $softrefCfg = [];
186 
193 
199  public $import_mapId = [];
200 
206  public $errorLog = [];
207 
213  public $cache_getRecordPath = [];
214 
220  public $checkPID_cache = [];
221 
228  public $compress = false;
229 
235  public $dat = [];
236 
242  protected $fileProcObj = null;
243 
247  protected $remainHeader = [];
248 
252  protected $iconFactory;
253 
257  public function __construct()
258  {
259  $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
260  }
261 
262  /**************************
263  * Initialize
264  *************************/
265 
271  public function init()
272  {
273  $this->compress = function_exists('gzcompress');
274  $this->fileadminFolderName = !empty($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) ? rtrim($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') : 'fileadmin';
275  }
276 
277  /********************************************************
278  * Visual rendering of import/export memory, $this->dat
279  ********************************************************/
280 
286  public function displayContentOverview()
287  {
288  if (!isset($this->dat['header'])) {
289  return [];
290  }
291  // Check extension dependencies:
292  foreach ($this->dat['header']['extensionDependencies'] as $extKey) {
293  if (!empty($extKey) && !ExtensionManagementUtility::isLoaded($extKey)) {
294  $this->error('DEPENDENCY: The extension with key "' . $extKey . '" must be installed!');
295  }
296  }
297 
298  // Probably this is done to save memory space?
299  unset($this->dat['files']);
300 
301  $viewData = [];
302  // Traverse header:
303  $this->remainHeader = $this->dat['header'];
304  // If there is a page tree set, show that:
305  if (is_array($this->dat['header']['pagetree'])) {
306  reset($this->dat['header']['pagetree']);
307  $lines = [];
308  $this->traversePageTree($this->dat['header']['pagetree'], $lines);
309 
310  $viewData['dat'] = $this->dat;
311  $viewData['update'] = $this->update;
312  $viewData['showDiff'] = $this->showDiff;
313  if (!empty($lines)) {
314  foreach ($lines as &$r) {
315  $r['controls'] = $this->renderControls($r);
316  $r['fileSize'] = GeneralUtility::formatSize($r['size']);
317  $r['message'] = ($r['msg'] && !$this->doesImport ? '<span class="text-danger">' . htmlspecialchars($r['msg']) . '</span>' : '');
318  }
319  $viewData['pagetreeLines'] = $lines;
320  } else {
321  $viewData['pagetreeLines'] = [];
322  }
323  }
324  // Print remaining records that were not contained inside the page tree:
325  if (is_array($this->remainHeader['records'])) {
326  $lines = [];
327  if (is_array($this->remainHeader['records']['pages'])) {
328  $this->traversePageRecords($this->remainHeader['records']['pages'], $lines);
329  }
330  $this->traverseAllRecords($this->remainHeader['records'], $lines);
331  if (!empty($lines)) {
332  foreach ($lines as &$r) {
333  $r['controls'] = $this->renderControls($r);
334  $r['fileSize'] = GeneralUtility::formatSize($r['size']);
335  $r['message'] = ($r['msg'] && !$this->doesImport ? '<span class="text-danger">' . htmlspecialchars($r['msg']) . '</span>' : '');
336  }
337  $viewData['remainingRecords'] = $lines;
338  }
339  }
340 
341  return $viewData;
342  }
343 
352  public function traversePageTree($pT, &$lines, $preCode = '')
353  {
354  foreach ($pT as $k => $v) {
355  // Add this page:
356  $this->singleRecordLines('pages', $k, $lines, $preCode);
357  // Subrecords:
358  if (is_array($this->dat['header']['pid_lookup'][$k])) {
359  foreach ($this->dat['header']['pid_lookup'][$k] as $t => $recUidArr) {
360  if ($t != 'pages') {
361  foreach ($recUidArr as $ruid => $value) {
362  $this->singleRecordLines($t, $ruid, $lines, $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;');
363  }
364  }
365  }
366  unset($this->remainHeader['pid_lookup'][$k]);
367  }
368  // Subpages, called recursively:
369  if (is_array($v['subrow'])) {
370  $this->traversePageTree($v['subrow'], $lines, $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;');
371  }
372  }
373  }
374 
382  public function traversePageRecords($pT, &$lines)
383  {
384  foreach ($pT as $k => $rHeader) {
385  $this->singleRecordLines('pages', $k, $lines, '', 1);
386  // Subrecords:
387  if (is_array($this->dat['header']['pid_lookup'][$k])) {
388  foreach ($this->dat['header']['pid_lookup'][$k] as $t => $recUidArr) {
389  if ($t != 'pages') {
390  foreach ($recUidArr as $ruid => $value) {
391  $this->singleRecordLines($t, $ruid, $lines, '&nbsp;&nbsp;&nbsp;&nbsp;');
392  }
393  }
394  }
395  unset($this->remainHeader['pid_lookup'][$k]);
396  }
397  }
398  }
399 
407  public function traverseAllRecords($pT, &$lines)
408  {
409  foreach ($pT as $t => $recUidArr) {
410  $this->addGeneralErrorsByTable($t);
411  if ($t != 'pages') {
412  $preCode = '';
413  foreach ($recUidArr as $ruid => $value) {
414  $this->singleRecordLines($t, $ruid, $lines, $preCode, 1);
415  }
416  }
417  }
418  }
419 
426  protected function addGeneralErrorsByTable($table)
427  {
428  if ($this->update && $table === 'sys_file') {
429  $this->error('Updating sys_file records is not supported! They will be imported as new records!');
430  }
431  if ($this->force_all_UIDS && $table === 'sys_file') {
432  $this->error('Forcing uids of sys_file records is not supported! They will be imported as new records!');
433  }
434  }
435 
446  public function singleRecordLines($table, $uid, &$lines, $preCode, $checkImportInPidRecord = false)
447  {
448  // Get record:
449  $record = $this->dat['header']['records'][$table][$uid];
450  unset($this->remainHeader['records'][$table][$uid]);
451  if (!is_array($record) && !($table === 'pages' && !$uid)) {
452  $this->error('MISSING RECORD: ' . $table . ':' . $uid);
453  }
454  // Begin to create the line arrays information record, pInfo:
455  $pInfo = [];
456  $pInfo['ref'] = $table . ':' . $uid;
457  // Unknown table name:
458  $lang = $this->getLanguageService();
459  if ($table === '_SOFTREF_') {
460  $pInfo['preCode'] = $preCode;
461  $pInfo['title'] = '<em>' . $lang->getLL('impexpcore_singlereco_softReferencesFiles', true) . '</em>';
462  } elseif (!isset($GLOBALS['TCA'][$table])) {
463  // Unknown table name:
464  $pInfo['preCode'] = $preCode;
465  $pInfo['msg'] = 'UNKNOWN TABLE \'' . $pInfo['ref'] . '\'';
466  $pInfo['title'] = '<em>' . htmlspecialchars($record['title']) . '</em>';
467  } else {
468  // Otherwise, set table icon and title.
469  // Import Validation (triggered by $this->display_import_pid_record) will show messages if import is not possible of various items.
470  if (is_array($this->display_import_pid_record) && !empty($this->display_import_pid_record)) {
471  if ($checkImportInPidRecord) {
472  if (!$this->getBackendUser()->doesUserHaveAccess($this->display_import_pid_record, ($table === 'pages' ? 8 : 16))) {
473  $pInfo['msg'] .= '\'' . $pInfo['ref'] . '\' cannot be INSERTED on this page! ';
474  }
475  if (!$this->checkDokType($table, $this->display_import_pid_record['doktype']) && !$GLOBALS['TCA'][$table]['ctrl']['rootLevel']) {
476  $pInfo['msg'] .= '\'' . $table . '\' cannot be INSERTED on this page type (change page type to \'Folder\'.) ';
477  }
478  }
479  if (!$this->getBackendUser()->check('tables_modify', $table)) {
480  $pInfo['msg'] .= 'You are not allowed to CREATE \'' . $table . '\' tables! ';
481  }
482  if ($GLOBALS['TCA'][$table]['ctrl']['readOnly']) {
483  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is READ ONLY! ';
484  }
485  if ($GLOBALS['TCA'][$table]['ctrl']['adminOnly'] && !$this->getBackendUser()->isAdmin()) {
486  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is ADMIN ONLY! ';
487  }
488  if ($GLOBALS['TCA'][$table]['ctrl']['is_static']) {
489  $pInfo['msg'] .= 'TABLE \'' . $table . '\' is a STATIC TABLE! ';
490  }
491  if ((int)$GLOBALS['TCA'][$table]['ctrl']['rootLevel'] === 1) {
492  $pInfo['msg'] .= 'TABLE \'' . $table . '\' will be inserted on ROOT LEVEL! ';
493  }
494  $diffInverse = false;
495  $recInf = null;
496  if ($this->update) {
497  // In case of update-PREVIEW we swap the diff-sources.
498  $diffInverse = true;
499  $recInf = $this->doesRecordExist($table, $uid, $this->showDiff ? '*' : '');
500  $pInfo['updatePath'] = $recInf ? htmlspecialchars($this->getRecordPath($recInf['pid'])) : '<strong>NEW!</strong>';
501  // Mode selector:
502  $optValues = [];
503  $optValues[] = $recInf ? $lang->getLL('impexpcore_singlereco_update') : $lang->getLL('impexpcore_singlereco_insert');
504  if ($recInf) {
505  $optValues['as_new'] = $lang->getLL('impexpcore_singlereco_importAsNew');
506  }
507  if ($recInf) {
508  if (!$this->global_ignore_pid) {
509  $optValues['ignore_pid'] = $lang->getLL('impexpcore_singlereco_ignorePid');
510  } else {
511  $optValues['respect_pid'] = $lang->getLL('impexpcore_singlereco_respectPid');
512  }
513  }
514  if (!$recInf && $this->getBackendUser()->isAdmin()) {
515  $optValues['force_uid'] = sprintf($lang->getLL('impexpcore_singlereco_forceUidSAdmin'), $uid);
516  }
517  $optValues['exclude'] = $lang->getLL('impexpcore_singlereco_exclude');
518  if ($table === 'sys_file') {
519  $pInfo['updateMode'] = '';
520  } else {
521  $pInfo['updateMode'] = $this->renderSelectBox('tx_impexp[import_mode][' . $table . ':' . $uid . ']', $this->import_mode[$table . ':' . $uid], $optValues);
522  }
523  }
524  // Diff view:
525  if ($this->showDiff) {
526  // For IMPORTS, get new id:
527  if ($newUid = $this->import_mapId[$table][$uid]) {
528  $diffInverse = false;
529  $recInf = $this->doesRecordExist($table, $newUid, '*');
530  BackendUtility::workspaceOL($table, $recInf);
531  }
532  if (is_array($recInf)) {
533  $pInfo['showDiffContent'] = $this->compareRecords($recInf, $this->dat['records'][$table . ':' . $uid]['data'], $table, $diffInverse);
534  }
535  }
536  }
537  $pInfo['preCode'] = $preCode . '<span title="' . htmlspecialchars($table . ':' . $uid) . '">'
538  . $this->iconFactory->getIconForRecord($table, (array)$this->dat['records'][$table . ':' . $uid]['data'], Icon::SIZE_SMALL)->render()
539  . '</span>';
540  $pInfo['title'] = htmlspecialchars($record['title']);
541  // View page:
542  if ($table === 'pages') {
543  $viewID = $this->mode === 'export' ? $uid : ($this->doesImport ? $this->import_mapId['pages'][$uid] : 0);
544  if ($viewID) {
545  $pInfo['title'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($viewID)) . 'return false;">' . $pInfo['title'] . '</a>';
546  }
547  }
548  }
549  $pInfo['class'] = $table == 'pages' ? 'bgColor4-20' : 'bgColor4';
550  $pInfo['type'] = 'record';
551  $pInfo['size'] = (int)$record['size'];
552  $lines[] = $pInfo;
553  // File relations:
554  if (is_array($record['filerefs'])) {
555  $this->addFiles($record['filerefs'], $lines, $preCode);
556  }
557  // DB relations
558  if (is_array($record['rels'])) {
559  $this->addRelations($record['rels'], $lines, $preCode);
560  }
561  // Soft ref
562  if (!empty($record['softrefs'])) {
563  $preCode_A = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;';
564  $preCode_B = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
565  foreach ($record['softrefs'] as $info) {
566  $pInfo = [];
567  $pInfo['preCode'] = $preCode_A . $this->iconFactory->getIcon('status-status-reference-soft', Icon::SIZE_SMALL)->render();
568  $pInfo['title'] = '<em>' . $info['field'] . ', "' . $info['spKey'] . '" </em>: <span title="' . htmlspecialchars($info['matchString']) . '">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['matchString'], 60)) . '</span>';
569  if ($info['subst']['type']) {
570  if (strlen($info['subst']['title'])) {
571  $pInfo['title'] .= '<br/>' . $preCode_B . '<strong>' . $lang->getLL('impexpcore_singlereco_title', true) . '</strong> ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['subst']['title'], 60));
572  }
573  if (strlen($info['subst']['description'])) {
574  $pInfo['title'] .= '<br/>' . $preCode_B . '<strong>' . $lang->getLL('impexpcore_singlereco_descr', true) . '</strong> ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($info['subst']['description'], 60));
575  }
576  $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>' : '');
577  }
578  $pInfo['ref'] = 'SOFTREF';
579  $pInfo['size'] = 0;
580  $pInfo['class'] = 'bgColor3';
581  $pInfo['type'] = 'softref';
582  $pInfo['_softRefInfo'] = $info;
583  $pInfo['type'] = 'softref';
584  $mode = $this->softrefCfg[$info['subst']['tokenID']]['mode'];
585  if ($info['error'] && $mode !== 'editable' && $mode !== 'exclude') {
586  $pInfo['msg'] .= $info['error'];
587  }
588  $lines[] = $pInfo;
589  // Add relations:
590  if ($info['subst']['type'] == 'db') {
591  list($tempTable, $tempUid) = explode(':', $info['subst']['recordRef']);
592  $this->addRelations([['table' => $tempTable, 'id' => $tempUid, 'tokenID' => $info['subst']['tokenID']]], $lines, $preCode_B, [], '');
593  }
594  // Add files:
595  if ($info['subst']['type'] == 'file') {
596  $this->addFiles([$info['file_ID']], $lines, $preCode_B, '', $info['subst']['tokenID']);
597  }
598  }
599  }
600  }
601 
614  public function addRelations($rels, &$lines, $preCode, $recurCheck = [], $htmlColorClass = '')
615  {
616  foreach ($rels as $dat) {
617  $table = $dat['table'];
618  $uid = $dat['id'];
619  $pInfo = [];
620  $pInfo['ref'] = $table . ':' . $uid;
621  if (in_array($pInfo['ref'], $recurCheck)) {
622  continue;
623  }
624  $iconName = 'status-status-checked';
625  $iconClass = '';
626  $staticFixed = false;
627  $record = null;
628  if ($uid > 0) {
629  $record = $this->dat['header']['records'][$table][$uid];
630  if (!is_array($record)) {
631  if ($this->isTableStatic($table) || $this->isExcluded($table, $uid) || $dat['tokenID'] && !$this->includeSoftref($dat['tokenID'])) {
632  $pInfo['title'] = htmlspecialchars('STATIC: ' . $pInfo['ref']);
633  $iconClass = 'text-info';
634  $staticFixed = true;
635  } else {
636  $doesRE = $this->doesRecordExist($table, $uid);
637  $lostPath = $this->getRecordPath($table === 'pages' ? $doesRE['uid'] : $doesRE['pid']);
638  $pInfo['title'] = htmlspecialchars($pInfo['ref']);
639  $pInfo['title'] = '<span title="' . htmlspecialchars($lostPath) . '">' . $pInfo['title'] . '</span>';
640  $pInfo['msg'] = 'LOST RELATION' . (!$doesRE ? ' (Record not found!)' : ' (Path: ' . $lostPath . ')');
641  $iconClass = 'text-danger';
642  $iconName = 'status-dialog-warning';
643  }
644  } else {
645  $pInfo['title'] = htmlspecialchars($record['title']);
646  $pInfo['title'] = '<span title="' . htmlspecialchars($this->getRecordPath(($table === 'pages' ? $record['uid'] : $record['pid']))) . '">' . $pInfo['title'] . '</span>';
647  }
648  } else {
649  // 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
650  $pInfo['title'] = htmlspecialchars('FIXED: ' . $pInfo['ref']);
651  $staticFixed = true;
652  }
653 
654  $icon = '<span class="' . $iconClass . '" title="' . htmlspecialchars($pInfo['ref']) . '">' . $this->iconFactory->getIcon($iconName, Icon::SIZE_SMALL)->render() . '</span>';
655 
656  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;' . $icon;
657  $pInfo['class'] = $htmlColorClass ?: 'bgColor3';
658  $pInfo['type'] = 'rel';
659  if (!$staticFixed || $this->showStaticRelations) {
660  $lines[] = $pInfo;
661  if (is_array($record) && is_array($record['rels'])) {
662  $this->addRelations($record['rels'], $lines, $preCode . '&nbsp;&nbsp;', array_merge($recurCheck, [$pInfo['ref']]), $htmlColorClass);
663  }
664  }
665  }
666  }
667 
680  public function addFiles($rels, &$lines, $preCode, $htmlColorClass = '', $tokenID = '')
681  {
682  foreach ($rels as $ID) {
683  // Process file:
684  $pInfo = [];
685  $fI = $this->dat['header']['files'][$ID];
686  if (!is_array($fI)) {
687  if (!$tokenID || $this->includeSoftref($tokenID)) {
688  $pInfo['msg'] = 'MISSING FILE: ' . $ID;
689  $this->error('MISSING FILE: ' . $ID);
690  } else {
691  return;
692  }
693  }
694  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;' . $this->iconFactory->getIcon('status-status-reference-hard', Icon::SIZE_SMALL)->render();
695  $pInfo['title'] = htmlspecialchars($fI['filename']);
696  $pInfo['ref'] = 'FILE';
697  $pInfo['size'] = $fI['filesize'];
698  $pInfo['class'] = $htmlColorClass ?: 'bgColor3';
699  $pInfo['type'] = 'file';
700  // If import mode and there is a non-RTE softreference, check the destination directory:
701  if ($this->mode === 'import' && $tokenID && !$fI['RTE_ORIG_ID']) {
702  if (isset($fI['parentRelFileName'])) {
703  $pInfo['msg'] = 'Seems like this file is already referenced from within an HTML/CSS file. That takes precedence. ';
704  } else {
705  $testDirPrefix = PathUtility::dirname($fI['relFileName']) . '/';
706  $testDirPrefix2 = $this->verifyFolderAccess($testDirPrefix);
707  if (!$testDirPrefix2) {
708  $pInfo['msg'] = 'ERROR: There are no available filemounts to write file in! ';
709  } elseif ($testDirPrefix !== $testDirPrefix2) {
710  $pInfo['msg'] = 'File will be attempted written to "' . $testDirPrefix2 . '". ';
711  }
712  }
713  // Check if file exists:
714  if (file_exists(PATH_site . $fI['relFileName'])) {
715  if ($this->update) {
716  $pInfo['updatePath'] .= 'File exists.';
717  } else {
718  $pInfo['msg'] .= 'File already exists! ';
719  }
720  }
721  // Check extension:
722  $fileProcObj = $this->getFileProcObj();
723  if ($fileProcObj->actionPerms['addFile']) {
724  $testFI = GeneralUtility::split_fileref(PATH_site . $fI['relFileName']);
725  if (!$this->allowPHPScripts && !$fileProcObj->checkIfAllowed($testFI['fileext'], $testFI['path'], $testFI['file'])) {
726  $pInfo['msg'] .= 'File extension was not allowed!';
727  }
728  } else {
729  $pInfo['msg'] = 'You user profile does not allow you to create files on the server!';
730  }
731  }
732  $pInfo['showDiffContent'] = PathUtility::stripPathSitePrefix($this->fileIDMap[$ID]);
733  $lines[] = $pInfo;
734  unset($this->remainHeader['files'][$ID]);
735  // RTE originals:
736  if ($fI['RTE_ORIG_ID']) {
737  $ID = $fI['RTE_ORIG_ID'];
738  $pInfo = [];
739  $fI = $this->dat['header']['files'][$ID];
740  if (!is_array($fI)) {
741  $pInfo['msg'] = 'MISSING RTE original FILE: ' . $ID;
742  $this->error('MISSING RTE original FILE: ' . $ID);
743  }
744  $pInfo['showDiffContent'] = PathUtility::stripPathSitePrefix($this->fileIDMap[$ID]);
745  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . $this->iconFactory->getIcon('status-status-reference-hard', Icon::SIZE_SMALL)->render();
746  $pInfo['title'] = htmlspecialchars($fI['filename']) . ' <em>(Original)</em>';
747  $pInfo['ref'] = 'FILE';
748  $pInfo['size'] = $fI['filesize'];
749  $pInfo['class'] = $htmlColorClass ?: 'bgColor3';
750  $pInfo['type'] = 'file';
751  $lines[] = $pInfo;
752  unset($this->remainHeader['files'][$ID]);
753  }
754  // External resources:
755  if (is_array($fI['EXT_RES_ID'])) {
756  foreach ($fI['EXT_RES_ID'] as $extID) {
757  $pInfo = [];
758  $fI = $this->dat['header']['files'][$extID];
759  if (!is_array($fI)) {
760  $pInfo['msg'] = 'MISSING External Resource FILE: ' . $extID;
761  $this->error('MISSING External Resource FILE: ' . $extID);
762  } else {
763  $pInfo['updatePath'] = $fI['parentRelFileName'];
764  }
765  $pInfo['showDiffContent'] = PathUtility::stripPathSitePrefix($this->fileIDMap[$extID]);
766  $pInfo['preCode'] = $preCode . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . $this->iconFactory->getIcon('actions-insert-reference', Icon::SIZE_SMALL)->render();
767  $pInfo['title'] = htmlspecialchars($fI['filename']) . ' <em>(Resource)</em>';
768  $pInfo['ref'] = 'FILE';
769  $pInfo['size'] = $fI['filesize'];
770  $pInfo['class'] = $htmlColorClass ?: 'bgColor3';
771  $pInfo['type'] = 'file';
772  $lines[] = $pInfo;
773  unset($this->remainHeader['files'][$extID]);
774  }
775  }
776  }
777  }
778 
786  public function checkDokType($checkTable, $doktype)
787  {
788  $allowedTableList = isset($GLOBALS['PAGES_TYPES'][$doktype]['allowedTables']) ? $GLOBALS['PAGES_TYPES'][$doktype]['allowedTables'] : $GLOBALS['PAGES_TYPES']['default']['allowedTables'];
789  $allowedArray = GeneralUtility::trimExplode(',', $allowedTableList, true);
790  // If all tables or the table is listed as an allowed type, return TRUE
791  if (strstr($allowedTableList, '*') || in_array($checkTable, $allowedArray)) {
792  return true;
793  }
794  return false;
795  }
796 
803  public function renderControls($r)
804  {
805  if ($this->mode === 'export') {
806  if ($r['type'] === 'record') {
807  return '<input type="checkbox" name="tx_impexp[exclude][' . $r['ref'] . ']" id="checkExclude' . $r['ref'] . '" value="1" /> <label for="checkExclude' . $r['ref'] . '">' . $this->getLanguageService()->getLL('impexpcore_singlereco_exclude', true) . '</label>';
808  } else {
809  return $r['type'] == 'softref' ? $this->softrefSelector($r['_softRefInfo']) : '';
810  }
811  } else {
812  // During import
813  // For softreferences with editable fields:
814  if ($r['type'] == 'softref' && is_array($r['_softRefInfo']['subst']) && $r['_softRefInfo']['subst']['tokenID']) {
815  $tokenID = $r['_softRefInfo']['subst']['tokenID'];
816  $cfg = $this->softrefCfg[$tokenID];
817  if ($cfg['mode'] === 'editable') {
818  return (strlen($cfg['title']) ? '<strong>' . htmlspecialchars($cfg['title']) . '</strong><br/>' : '') . htmlspecialchars($cfg['description']) . '<br/>
819  <input type="text" name="tx_impexp[softrefInputValues][' . $tokenID . ']" value="' . htmlspecialchars((isset($this->softrefInputValues[$tokenID]) ? $this->softrefInputValues[$tokenID] : $cfg['defValue'])) . '" />';
820  }
821  }
822  }
823  return '';
824  }
825 
832  public function softrefSelector($cfg)
833  {
834  // Looking for file ID if any:
835  $fI = $cfg['file_ID'] ? $this->dat['header']['files'][$cfg['file_ID']] : [];
836  // Substitution scheme has to be around and RTE images MUST be exported.
837  if (is_array($cfg['subst']) && $cfg['subst']['tokenID'] && !$fI['RTE_ORIG_ID']) {
838  // Create options:
839  $optValues = [];
840  $optValues[''] = '';
841  $optValues['editable'] = $this->getLanguageService()->getLL('impexpcore_softrefsel_editable');
842  $optValues['exclude'] = $this->getLanguageService()->getLL('impexpcore_softrefsel_exclude');
843  // Get current value:
844  $value = $this->softrefCfg[$cfg['subst']['tokenID']]['mode'];
845  // Render options selector:
846  $selectorbox = $this->renderSelectBox(('tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][mode]'), $value, $optValues) . '<br/>';
847  if ($value === 'editable') {
848  $descriptionField = '';
849  // Title:
850  if (strlen($cfg['subst']['title'])) {
851  $descriptionField .= '
852  <input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][title]" value="' . htmlspecialchars($cfg['subst']['title']) . '" />
853  <strong>' . htmlspecialchars($cfg['subst']['title']) . '</strong><br/>';
854  }
855  // Description:
856  if (!strlen($cfg['subst']['description'])) {
857  $descriptionField .= '
858  ' . $this->getLanguageService()->getLL('impexpcore_printerror_description', true) . '<br/>
859  <input type="text" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][description]" value="' . htmlspecialchars($this->softrefCfg[$cfg['subst']['tokenID']]['description']) . '" />';
860  } else {
861  $descriptionField .= '
862 
863  <input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][description]" value="' . htmlspecialchars($cfg['subst']['description']) . '" />' . htmlspecialchars($cfg['subst']['description']);
864  }
865  // Default Value:
866  $descriptionField .= '<input type="hidden" name="tx_impexp[softrefCfg][' . $cfg['subst']['tokenID'] . '][defValue]" value="' . htmlspecialchars($cfg['subst']['tokenValue']) . '" />';
867  } else {
868  $descriptionField = '';
869  }
870  return $selectorbox . $descriptionField;
871  }
872  return '';
873  }
874 
883  public function verifyFolderAccess($dirPrefix, $noAlternative = false)
884  {
885  $fileProcObj = $this->getFileProcObj();
886  // Check, if dirPrefix is inside a valid Filemount for user:
887  $result = $fileProcObj->checkPathAgainstMounts(PATH_site . $dirPrefix);
888  // If not, try to find another relative filemount and use that instead:
889  if (!$result) {
890  if ($noAlternative) {
891  return false;
892  }
893  // Find first web folder:
894  $result = $fileProcObj->findFirstWebFolder();
895  // If that succeeded, return the path to it:
896  if ($result) {
897  // Remove the "fileadmin/" prefix of input path - and append the rest to the return value:
898  if (GeneralUtility::isFirstPartOfStr($dirPrefix, $this->fileadminFolderName . '/')) {
899  $dirPrefix = substr($dirPrefix, strlen($this->fileadminFolderName . '/'));
900  }
901  return PathUtility::stripPathSitePrefix($fileProcObj->mounts[$result]['path'] . $dirPrefix);
902  }
903  } else {
904  return $dirPrefix;
905  }
906  return false;
907  }
908 
909  /*****************************
910  * Helper functions of kinds
911  *****************************/
912 
917  protected function getTemporaryFolderName()
918  {
919  $temporaryPath = PATH_site . 'typo3temp/';
920  do {
921  $temporaryFolderName = $temporaryPath . 'export_temp_files_' . mt_rand(1, PHP_INT_MAX);
922  } while (is_dir($temporaryFolderName));
923  GeneralUtility::mkdir($temporaryFolderName);
924  return $temporaryFolderName;
925  }
926 
935  public function flatInversePageTree($idH, $a = [])
936  {
937  if (is_array($idH)) {
938  $idH = array_reverse($idH);
939  foreach ($idH as $k => $v) {
940  $a[$v['uid']] = $v['uid'];
941  if (is_array($v['subrow'])) {
942  $a = $this->flatInversePageTree($v['subrow'], $a);
943  }
944  }
945  }
946  return $a;
947  }
948 
955  public function isTableStatic($table)
956  {
957  if (is_array($GLOBALS['TCA'][$table])) {
958  return $GLOBALS['TCA'][$table]['ctrl']['is_static'] || in_array($table, $this->relStaticTables) || in_array('_ALL', $this->relStaticTables);
959  }
960  return false;
961  }
962 
969  public function inclRelation($table)
970  {
971  return is_array($GLOBALS['TCA'][$table])
972  && (in_array($table, $this->relOnlyTables) || in_array('_ALL', $this->relOnlyTables))
973  && $this->getBackendUser()->check('tables_select', $table);
974  }
975 
983  public function isExcluded($table, $uid)
984  {
985  return (bool)$this->excludeMap[$table . ':' . $uid];
986  }
987 
994  public function includeSoftref($tokenID)
995  {
996  $mode = $this->softrefCfg[$tokenID]['mode'];
997  return $tokenID && $mode !== 'exclude' && $mode !== 'editable';
998  }
999 
1006  public function checkPID($pid)
1007  {
1008  if (!isset($this->checkPID_cache[$pid])) {
1009  $this->checkPID_cache[$pid] = (bool)$this->getBackendUser()->isInWebMount($pid);
1010  }
1011  return $this->checkPID_cache[$pid];
1012  }
1013 
1021  public function dontIgnorePid($table, $uid)
1022  {
1023  return $this->import_mode[$table . ':' . $uid] !== 'ignore_pid' && (!$this->global_ignore_pid || $this->import_mode[$table . ':' . $uid] === 'respect_pid');
1024  }
1025 
1034  public function doesRecordExist($table, $uid, $fields = '')
1035  {
1036  return BackendUtility::getRecord($table, $uid, $fields ? $fields : 'uid,pid');
1037  }
1038 
1045  public function getRecordPath($pid)
1046  {
1047  if (!isset($this->cache_getRecordPath[$pid])) {
1048  $clause = $this->getBackendUser()->getPagePermsClause(1);
1049  $this->cache_getRecordPath[$pid] = (string)BackendUtility::getRecordPath($pid, $clause, 20);
1050  }
1051  return $this->cache_getRecordPath[$pid];
1052  }
1053 
1062  public function renderSelectBox($prefix, $value, $optValues)
1063  {
1064  $opt = [];
1065  $isSelFlag = 0;
1066  foreach ($optValues as $k => $v) {
1067  $sel = (string)$k === (string)$value ? ' selected="selected"' : '';
1068  if ($sel) {
1069  $isSelFlag++;
1070  }
1071  $opt[] = '<option value="' . htmlspecialchars($k) . '"' . $sel . '>' . htmlspecialchars($v) . '</option>';
1072  }
1073  if (!$isSelFlag && (string)$value !== '') {
1074  $opt[] = '<option value="' . htmlspecialchars($value) . '" selected="selected">' . htmlspecialchars(('[\'' . $value . '\']')) . '</option>';
1075  }
1076  return '<select name="' . $prefix . '">' . implode('', $opt) . '</select>';
1077  }
1078 
1089  public function compareRecords($databaseRecord, $importRecord, $table, $inverseDiff = false)
1090  {
1091  // Initialize:
1092  $output = [];
1093  $diffUtility = GeneralUtility::makeInstance(DiffUtility::class);
1094  // Check if both inputs are records:
1095  if (is_array($databaseRecord) && is_array($importRecord)) {
1096  // Traverse based on database record
1097  foreach ($databaseRecord as $fN => $value) {
1098  if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type'] != 'passthrough') {
1099  if (isset($importRecord[$fN])) {
1100  if (trim($databaseRecord[$fN]) !== trim($importRecord[$fN])) {
1101  // Create diff-result:
1102  $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));
1103  }
1104  unset($importRecord[$fN]);
1105  }
1106  }
1107  }
1108  // Traverse remaining in import record:
1109  foreach ($importRecord as $fN => $value) {
1110  if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type'] !== 'passthrough') {
1111  $output[$fN] = '<strong>Field missing</strong> in database';
1112  }
1113  }
1114  // Create output:
1115  if (!empty($output)) {
1116  $tRows = [];
1117  foreach ($output as $fN => $state) {
1118  $tRows[] = '
1119  <tr>
1120  <td class="bgColor5">' . $this->getLanguageService()->sL($GLOBALS['TCA'][$table]['columns'][$fN]['label'], true) . ' (' . htmlspecialchars($fN) . ')</td>
1121  <td class="bgColor4">' . $state . '</td>
1122  </tr>
1123  ';
1124  }
1125  $output = '<table border="0" cellpadding="0" cellspacing="1">' . implode('', $tRows) . '</table>';
1126  } else {
1127  $output = 'Match';
1128  }
1129  return '<strong class="text-nowrap">[' . htmlspecialchars(($table . ':' . $importRecord['uid'] . ' => ' . $databaseRecord['uid'])) . ']:</strong> ' . $output;
1130  }
1131  return 'ERROR: One of the inputs were not an array!';
1132  }
1133 
1140  public function getRTEoriginalFilename($string)
1141  {
1142  // If "magic image":
1143  if (GeneralUtility::isFirstPartOfStr($string, 'RTEmagicC_')) {
1144  // Find original file:
1145  $pI = pathinfo(substr($string, strlen('RTEmagicC_')));
1146  $filename = substr($pI['basename'], 0, -strlen(('.' . $pI['extension'])));
1147  $origFilePath = 'RTEmagicP_' . $filename;
1148  return $origFilePath;
1149  }
1150  return null;
1151  }
1152 
1158  public function getFileProcObj()
1159  {
1160  if ($this->fileProcObj === null) {
1161  $this->fileProcObj = GeneralUtility::makeInstance(ExtendedFileUtility::class);
1162  $this->fileProcObj->init([], $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']);
1163  $this->fileProcObj->setActionPermissions();
1164  }
1165  return $this->fileProcObj;
1166  }
1167 
1175  public function callHook($name, $params)
1176  {
1177  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name])) {
1178  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name] as $hook) {
1179  GeneralUtility::callUserFunction($hook, $params, $this);
1180  }
1181  }
1182  }
1183 
1184  /*****************************
1185  * Error handling
1186  *****************************/
1187 
1194  public function error($msg)
1195  {
1196  $this->errorLog[] = $msg;
1197  }
1198 
1204  public function printErrorLog()
1205  {
1206  return !empty($this->errorLog) ? DebugUtility::viewArray($this->errorLog) : '';
1207  }
1208 
1212  protected function getBackendUser()
1213  {
1214  return $GLOBALS['BE_USER'];
1215  }
1216 
1220  protected function getDatabaseConnection()
1221  {
1222  return $GLOBALS['TYPO3_DB'];
1223  }
1224 
1228  protected function getLanguageService()
1229  {
1230  return $GLOBALS['LANG'];
1231  }
1232 }
singleRecordLines($table, $uid, &$lines, $preCode, $checkImportInPidRecord=false)
traversePageTree($pT, &$lines, $preCode='')
$uid
Definition: server.php:38
static formatSize($sizeInBytes, $labels='', $base=0)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']