2 namespace TYPO3\CMS\Impexp
;
4 /***************************************************************
7 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
30 * T3D file Import/Export library (TYPO3 Record Document)
32 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
35 * EXAMPLE for using the impexp-class for exporting stuff:
37 * Create and initialize:
38 * $this->export = t3lib_div::makeInstance('tx_impexp');
39 * $this->export->init();
40 * Set which tables relations we will allow:
41 * $this->export->relOnlyTables[]="tt_news"; // exclusively includes. See comment in the class
44 * $this->export->export_addRecord("pages", $this->pageinfo);
45 * $this->export->export_addRecord("pages", t3lib_BEfunc::getRecord("pages", 38));
46 * $this->export->export_addRecord("pages", t3lib_BEfunc::getRecord("pages", 39));
47 * $this->export->export_addRecord("tt_content", t3lib_BEfunc::getRecord("tt_content", 12));
48 * $this->export->export_addRecord("tt_content", t3lib_BEfunc::getRecord("tt_content", 74));
49 * $this->export->export_addRecord("sys_template", t3lib_BEfunc::getRecord("sys_template", 20));
51 * Adding all the relations (recursively in 5 levels so relations has THEIR relations registered as well)
52 * for($a=0;$a<5;$a++) {
53 * $addR = $this->export->export_addDBRelations($a);
54 * if (!count($addR)) break;
57 * Finally load all the files.
58 * $this->export->export_addFilesFromRelations(); // MUST be after the DBrelations are set so that file from ALL added records are included!
61 * $out = $this->export->compileMemoryToFileContent();
64 * T3D file Import/Export library (TYPO3 Record Document)
66 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
70 // Configuration, general
71 // If set, static relations (not exported) will be shown in overview as well
73 * @todo Define visibility
75 public $showStaticRelations = FALSE;
77 // Name of the "fileadmin" folder where files for export/import should be located
79 * @todo Define visibility
81 public $fileadminFolderName = '';
83 // Whether "import" or "export" mode of object. Set through init() function
85 * @todo Define visibility
89 // Updates all records that has same UID instead of creating new!
91 * @todo Define visibility
93 public $update = FALSE;
95 // Is set by importData() when an import has been done.
97 * @todo Define visibility
99 public $doesImport = FALSE;
101 // Configuration, import
102 // If set to a page-record, then the preview display of the content will expect this page-record to be the target
103 // for the import and accordingly display validation information. This triggers the visual view of the
104 // import/export memory to validate if import is possible
106 * @todo Define visibility
108 public $display_import_pid_record = '';
110 // Used to register the forged UID values for imported records that we want to create with the same UIDs as in the import file. Admin-only feature.
112 * @todo Define visibility
114 public $suggestedInsertUids = array();
116 // Setting import modes during update state: as_new, exclude, force_uid
118 * @todo Define visibility
120 public $import_mode = array();
122 // If set, PID correct is ignored globally
124 * @todo Define visibility
126 public $global_ignore_pid = FALSE;
128 // If set, all UID values are forced! (update or import)
130 * @todo Define visibility
132 public $force_all_UIDS = FALSE;
134 // If set, a diff-view column is added to the overview.
136 * @todo Define visibility
138 public $showDiff = FALSE;
140 // If set, and if the user is admin, allow the writing of PHP scripts to fileadmin/ area.
142 * @todo Define visibility
144 public $allowPHPScripts = FALSE;
146 // Disable logging when importing
148 * @todo Define visibility
150 public $enableLogging = FALSE;
152 // Array of values to substitute in editable softreferences.
154 * @todo Define visibility
156 public $softrefInputValues = array();
158 // Mapping between the fileID from import memory and the final filenames they are written to.
160 * @todo Define visibility
162 public $fileIDMap = array();
164 // Configuration, export
167 * @todo Define visibility
169 public $maxFileSize = 1000000;
171 // 1MB max record size
173 * @todo Define visibility
175 public $maxRecordSize = 1000000;
177 // 10MB max export size
179 * @todo Define visibility
181 public $maxExportSize = 10000000;
183 // add table names here which are THE ONLY ones which will be included into export if found as relations. '_ALL' will allow all tables.
185 * @todo Define visibility
187 public $relOnlyTables = array();
189 // add tables names here which should not be exported with the file. (Where relations should be mapped to same UIDs in target system).
191 * @todo Define visibility
193 public $relStaticTables = array();
195 // Exclude map. Keys are table:uid pairs and if set, records are not added to the export.
197 * @todo Define visibility
199 public $excludeMap = array();
201 // Soft Reference Token ID modes.
203 * @todo Define visibility
205 public $softrefCfg = array();
207 // Listing extension dependencies.
209 * @todo Define visibility
211 public $extensionDependencies = array();
213 // Set by user: If set, compression in t3d files is disabled
215 * @todo Define visibility
217 public $dontCompress = 0;
219 // Boolean, if set, HTML file resources are included.
221 * @todo Define visibility
223 public $includeExtFileResources = 0;
225 // Files with external media (HTML/css style references inside)
227 * @todo Define visibility
229 public $extFileResourceExtensions = 'html,htm,css';
231 // Internal, dynamic:
232 // After records are written this array is filled with [table][original_uid] = [new_uid]
234 * @todo Define visibility
236 public $import_mapId = array();
238 // Keys are [tablename]:[new NEWxxx ids (or when updating it is uids)] while values are arrays with table/uid of the original record it is based on. By the array keys the new ids can be looked up inside tcemain
240 * @todo Define visibility
242 public $import_newId = array();
244 // Page id map for page tree (import)
246 * @todo Define visibility
248 public $import_newId_pids = array();
250 // Internal data accumulation for writing records during import
252 * @todo Define visibility
254 public $import_data = array();
258 * @todo Define visibility
260 public $errorLog = array();
262 // Cache for record paths
264 * @todo Define visibility
266 public $cache_getRecordPath = array();
268 // Cache of checkPID values.
270 * @todo Define visibility
272 public $checkPID_cache = array();
274 // Set internally if the gzcompress function exists
276 * @todo Define visibility
278 public $compress = 0;
280 // Internal import/export memory
282 * @todo Define visibility
284 public $dat = array();
287 * File processing object
289 * @var \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility
290 * @todo Define visibility
292 public $fileProcObj = '';
294 /**************************
298 *************************/
300 * Init the object, both import and export
302 * @param boolean $dontCompress If set, compression in t3d files is disabled
303 * @param string $mode Mode of usage, either "import" or "export
305 * @todo Define visibility
307 public function init($dontCompress = 0, $mode = '') {
308 $this->compress
= function_exists('gzcompress');
309 $this->dontCompress
= $dontCompress;
311 $this->fileadminFolderName
= !empty($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) ?
rtrim($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'], '/') : 'fileadmin';
314 /**************************
316 * Export / Init + Meta Data
318 *************************/
323 * @todo Define visibility
325 public function setHeaderBasics() {
327 if (is_array($this->softrefCfg
)) {
328 foreach ($this->softrefCfg
as $key => $value) {
329 if (!strlen($value['mode'])) {
330 unset($this->softrefCfg
[$key]);
334 // Setting in header memory:
335 // Version of file format
336 $this->dat
['header']['XMLversion'] = '1.0';
337 // Initialize meta data array (to put it in top of file)
338 $this->dat
['header']['meta'] = array();
339 // Add list of tables to consider static
340 $this->dat
['header']['relStaticTables'] = $this->relStaticTables
;
341 // The list of excluded records
342 $this->dat
['header']['excludeMap'] = $this->excludeMap
;
343 // Soft Reference mode for elements
344 $this->dat
['header']['softrefCfg'] = $this->softrefCfg
;
345 // List of extensions the import depends on.
346 $this->dat
['header']['extensionDependencies'] = $this->extensionDependencies
;
352 * @param string $charset Charset for the content in the export. During import the character set will be converted if the target system uses another charset.
354 * @todo Define visibility
356 public function setCharset($charset) {
357 $this->dat
['header']['charset'] = $charset;
363 * @param string $title Title of the export
364 * @param string $description Description of the export
365 * @param string $notes Notes about the contents
366 * @param string $packager_username Backend Username of the packager (the guy making the export)
367 * @param string $packager_name Real name of the packager
368 * @param string $packager_email Email of the packager
370 * @todo Define visibility
372 public function setMetaData($title, $description, $notes, $packager_username, $packager_name, $packager_email) {
373 $this->dat
['header']['meta'] = array(
375 'description' => $description,
377 'packager_username' => $packager_username,
378 'packager_name' => $packager_name,
379 'packager_email' => $packager_email,
380 'TYPO3_version' => TYPO3_version
,
381 'created' => strftime('%A %e. %B %Y', $GLOBALS['EXEC_TIME'])
386 * Sets a thumbnail image to the exported file
388 * @param string $imgFilepath Filename reference, gif, jpg, png. Absolute path.
390 * @todo Define visibility
392 public function addThumbnail($imgFilepath) {
393 if (@is_file
($imgFilepath)) {
394 $imgInfo = @getimagesize
($imgFilepath);
395 if (is_array($imgInfo)) {
396 $fileContent = \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($imgFilepath);
397 $this->dat
['header']['thumbnail'] = array(
398 'imgInfo' => $imgInfo,
399 'content' => $fileContent,
400 'filesize' => strlen($fileContent),
401 'filemtime' => filemtime($imgFilepath),
402 'filename' => basename($imgFilepath)
408 /**************************
410 * Export / Init Page tree
412 *************************/
414 * Sets the page-tree array in the export header and returns the array in a flattened version
416 * @param array $idH Hierarchy of ids, the page tree: array([uid] => array("uid" => [uid], "subrow" => array(.....)), [uid] => ....)
417 * @return array The hierarchical page tree converted to a one-dimensional list of pages
418 * @todo Define visibility
420 public function setPageTree($idH) {
421 $this->dat
['header']['pagetree'] = $this->unsetExcludedSections($idH);
422 return $this->flatInversePageTree($this->dat
['header']['pagetree']);
426 * Removes entries in the page tree which are found in ->excludeMap[]
428 * @param array $idH Page uid hierarchy
429 * @return array Modified input array
432 * @todo Define visibility
434 public function unsetExcludedSections($idH) {
435 if (is_array($idH)) {
436 foreach ($idH as $k => $v) {
437 if ($this->excludeMap
['pages:' . $idH[$k]['uid']]) {
439 } elseif (is_array($idH[$k]['subrow'])) {
440 $idH[$k]['subrow'] = $this->unsetExcludedSections($idH[$k]['subrow']);
448 * Recursively flattening the idH array (for setPageTree() function)
450 * @param array $idH Page uid hierarchy
451 * @param array $a Accumulation array of pages (internal, don't set from outside)
452 * @return array Array with uid-uid pairs for all pages in the page tree.
453 * @see flatInversePageTree_pid()
454 * @todo Define visibility
456 public function flatInversePageTree($idH, $a = array()) {
457 if (is_array($idH)) {
458 $idH = array_reverse($idH);
459 foreach ($idH as $k => $v) {
460 $a[$v['uid']] = $v['uid'];
461 if (is_array($v['subrow'])) {
462 $a = $this->flatInversePageTree($v['subrow'], $a);
470 * Recursively flattening the idH array (for setPageTree() function), setting PIDs as values
472 * @param array $idH Page uid hierarchy
473 * @param array $a Accumulation array of pages (internal, don't set from outside)
474 * @param integer $pid PID value (internal)
475 * @return array Array with uid-pid pairs for all pages in the page tree.
476 * @see flatInversePageTree()
477 * @todo Define visibility
479 public function flatInversePageTree_pid($idH, $a = array(), $pid = -1) {
480 if (is_array($idH)) {
481 $idH = array_reverse($idH);
482 foreach ($idH as $k => $v) {
483 $a[$v['uid']] = $pid;
484 if (is_array($v['subrow'])) {
485 $a = $this->flatInversePageTree_pid($v['subrow'], $a, $v['uid']);
492 /**************************
496 *************************/
498 * Adds the record $row from $table.
499 * No checking for relations done here. Pure data.
501 * @param string $table Table name
502 * @param array $row Record row.
503 * @param integer $relationLevel (Internal) if the record is added as a relation, this is set to the "level" it was on.
505 * @todo Define visibility
507 public function export_addRecord($table, $row, $relationLevel = 0) {
508 \TYPO3\CMS\Backend\Utility\BackendUtility
::workspaceOL($table, $row);
509 if (strcmp($table, '') && is_array($row) && $row['uid'] > 0 && !$this->excludeMap
[($table . ':' . $row['uid'])]) {
510 if ($this->checkPID($table === 'pages' ?
$row['uid'] : $row['pid'])) {
511 if (!isset($this->dat
['records'][($table . ':' . $row['uid'])])) {
512 // Prepare header info:
513 $headerInfo = array();
514 $headerInfo['uid'] = $row['uid'];
515 $headerInfo['pid'] = $row['pid'];
516 $headerInfo['title'] = \TYPO3\CMS\Core\Utility\GeneralUtility
::fixed_lgd_cs(\TYPO3\CMS\Backend\Utility\BackendUtility
::getRecordTitle($table, $row), 40);
517 $headerInfo['size'] = strlen(serialize($row));
518 if ($relationLevel) {
519 $headerInfo['relationLevel'] = $relationLevel;
521 // If record content is not too large in size, set the header content and add the rest:
522 if ($headerInfo['size'] < $this->maxRecordSize
) {
523 // Set the header summary:
524 $this->dat
['header']['records'][$table][$row['uid']] = $headerInfo;
525 // Create entry in the PID lookup:
526 $this->dat
['header']['pid_lookup'][$row['pid']][$table][$row['uid']] = 1;
527 // Initialize reference index object:
528 $refIndexObj = \TYPO3\CMS\Core\Utility\GeneralUtility
::makeInstance('TYPO3\\CMS\\Core\\Database\\ReferenceIndex');
529 // Yes to workspace overlays for exporting....
530 $refIndexObj->WSOL
= TRUE;
532 $this->dat
['records'][$table . ':' . $row['uid']] = array();
533 $this->dat
['records'][$table . ':' . $row['uid']]['data'] = $row;
534 $this->dat
['records'][$table . ':' . $row['uid']]['rels'] = $refIndexObj->getRelations($table, $row);
535 $this->errorLog
= array_merge($this->errorLog
, $refIndexObj->errorLog
);
537 // Add information about the relations in the record in the header:
538 $this->dat
['header']['records'][$table][$row['uid']]['rels'] = $this->flatDBrels($this->dat
['records'][$table . ':' . $row['uid']]['rels']);
539 // Add information about the softrefs to header:
540 $this->dat
['header']['records'][$table][$row['uid']]['softrefs'] = $this->flatSoftRefs($this->dat
['records'][$table . ':' . $row['uid']]['rels']);
542 $this->error('Record ' . $table . ':' . $row['uid'] . ' was larger than maxRecordSize (' . \TYPO3\CMS\Core\Utility\GeneralUtility
::formatSize($this->maxRecordSize
) . ')');
545 $this->error('Record ' . $table . ':' . $row['uid'] . ' already added.');
548 $this->error('Record ' . $table . ':' . $row['uid'] . ' was outside your DB mounts!');
554 * This analyses the existing added records, finds all database relations to records and adds these records to the export file.
555 * This function can be called repeatedly until it returns an empty array. In principle it should not allow to infinite recursivity, but you better set a limit...
556 * Call this BEFORE the ext_addFilesFromRelations (so files from added relations are also included of course)
558 * @param integer $relationLevel Recursion level
559 * @return array overview of relations found and added: Keys [table]:[uid], values array with table and id
560 * @see export_addFilesFromRelations()
561 * @todo Define visibility
563 public function export_addDBRelations($relationLevel = 0) {
566 // Traverse all "rels" registered for "records"
567 if (is_array($this->dat
['records'])) {
568 foreach ($this->dat
['records'] as $k => $value) {
569 if (is_array($this->dat
['records'][$k])) {
570 foreach ($this->dat
['records'][$k]['rels'] as $fieldname => $vR) {
571 // For all DB types of relations:
572 if ($vR['type'] == 'db') {
573 foreach ($vR['itemArray'] as $fI) {
574 $this->export_addDBRelations_registerRelation($fI, $addR);
577 // For all flex/db types of relations:
578 if ($vR['type'] == 'flex') {
579 // DB relations in flex form fields:
580 if (is_array($vR['flexFormRels']['db'])) {
581 foreach ($vR['flexFormRels']['db'] as $subList) {
582 foreach ($subList as $fI) {
583 $this->export_addDBRelations_registerRelation($fI, $addR);
587 // DB oriented soft references in flex form fields:
588 if (is_array($vR['flexFormRels']['softrefs'])) {
589 foreach ($vR['flexFormRels']['softrefs'] as $subList) {
590 foreach ($subList['keys'] as $spKey => $elements) {
591 foreach ($elements as $el) {
592 if ($el['subst']['type'] === 'db' && $this->includeSoftref($el['subst']['tokenID'])) {
593 list($tempTable, $tempUid) = explode(':', $el['subst']['recordRef']);
595 'table' => $tempTable,
598 $this->export_addDBRelations_registerRelation($fI, $addR, $el['subst']['tokenID']);
605 // In any case, if there are soft refs:
606 if (is_array($vR['softrefs']['keys'])) {
607 foreach ($vR['softrefs']['keys'] as $spKey => $elements) {
608 foreach ($elements as $el) {
609 if ($el['subst']['type'] === 'db' && $this->includeSoftref($el['subst']['tokenID'])) {
610 list($tempTable, $tempUid) = explode(':', $el['subst']['recordRef']);
612 'table' => $tempTable,
615 $this->export_addDBRelations_registerRelation($fI, $addR, $el['subst']['tokenID']);
624 $this->error('There were no records available.');
626 // Now, if there were new records to add, do so:
628 foreach ($addR as $fI) {
629 // Get and set record:
630 $row = \TYPO3\CMS\Backend\Utility\BackendUtility
::getRecord($fI['table'], $fI['id']);
631 if (is_array($row)) {
632 $this->export_addRecord($fI['table'], $row, $relationLevel +
1);
634 // Set status message
635 // Relation pointers always larger than zero except certain "select" types with negative values pointing to uids - but that is not supported here.
637 $rId = $fI['table'] . ':' . $fI['id'];
638 if (!isset($this->dat
['records'][$rId])) {
639 $this->dat
['records'][$rId] = 'NOT_FOUND';
640 $this->error('Relation record ' . $rId . ' was not found!');
645 // Return overview of relations found and added
650 * Helper function for export_addDBRelations()
652 * @param array $fI Array with table/id keys to add
653 * @param array $addR Add array, passed by reference to be modified
654 * @param string $tokenID Softref Token ID, if applicable.
656 * @see export_addDBRelations()
657 * @todo Define visibility
659 public function export_addDBRelations_registerRelation($fI, &$addR, $tokenID = '') {
660 $rId = $fI['table'] . ':' . $fI['id'];
661 if (isset($GLOBALS['TCA'][$fI['table']]) && !$this->isTableStatic($fI['table']) && !$this->isExcluded($fI['table'], $fI['id']) && (!$tokenID ||
$this->includeSoftref($tokenID)) && $this->inclRelation($fI['table'])) {
662 if (!isset($this->dat
['records'][$rId])) {
663 // Set this record to be included since it is not already.
670 * This adds all files in relations.
671 * Call this method AFTER adding all records including relations.
674 * @see export_addDBRelations()
675 * @todo Define visibility
677 public function export_addFilesFromRelations() {
678 // Traverse all "rels" registered for "records"
679 if (is_array($this->dat
['records'])) {
680 foreach ($this->dat
['records'] as $k => $value) {
681 if (is_array($this->dat
['records'][$k]['rels'])) {
682 foreach ($this->dat
['records'][$k]['rels'] as $fieldname => $vR) {
683 // For all file type relations:
684 if ($vR['type'] == 'file') {
685 foreach ($vR['newValueFiles'] as $key => $fI) {
686 $this->export_addFile($fI, $k, $fieldname);
687 // Remove the absolute reference to the file so it doesn't expose absolute paths from source server:
688 unset($this->dat
['records'][$k]['rels'][$fieldname]['newValueFiles'][$key]['ID_absFile']);
691 // For all flex type relations:
692 if ($vR['type'] == 'flex') {
693 if (is_array($vR['flexFormRels']['file'])) {
694 foreach ($vR['flexFormRels']['file'] as $key => $subList) {
695 foreach ($subList as $subKey => $fI) {
696 $this->export_addFile($fI, $k, $fieldname);
697 // Remove the absolute reference to the file so it doesn't expose absolute paths from source server:
698 unset($this->dat
['records'][$k]['rels'][$fieldname]['flexFormRels']['file'][$key][$subKey]['ID_absFile']);
702 // DB oriented soft references in flex form fields:
703 if (is_array($vR['flexFormRels']['softrefs'])) {
704 foreach ($vR['flexFormRels']['softrefs'] as $key => $subList) {
705 foreach ($subList['keys'] as $spKey => $elements) {
706 foreach ($elements as $subKey => $el) {
707 if ($el['subst']['type'] === 'file' && $this->includeSoftref($el['subst']['tokenID'])) {
708 // Create abs path and ID for file:
709 $ID_absFile = \TYPO3\CMS\Core\Utility\GeneralUtility
::getFileAbsFileName(PATH_site
. $el['subst']['relFileName']);
710 $ID = md5($ID_absFile);
712 if (!$this->dat
['files'][$ID]) {
714 'filename' => basename($ID_absFile),
715 'ID_absFile' => $ID_absFile,
717 'relFileName' => $el['subst']['relFileName']
719 $this->export_addFile($fI, '_SOFTREF_');
721 $this->dat
['records'][$k]['rels'][$fieldname]['flexFormRels']['softrefs'][$key]['keys'][$spKey][$subKey]['file_ID'] = $ID;
729 // In any case, if there are soft refs:
730 if (is_array($vR['softrefs']['keys'])) {
731 foreach ($vR['softrefs']['keys'] as $spKey => $elements) {
732 foreach ($elements as $subKey => $el) {
733 if ($el['subst']['type'] === 'file' && $this->includeSoftref($el['subst']['tokenID'])) {
734 // Create abs path and ID for file:
735 $ID_absFile = \TYPO3\CMS\Core\Utility\GeneralUtility
::getFileAbsFileName(PATH_site
. $el['subst']['relFileName']);
736 $ID = md5($ID_absFile);
738 if (!$this->dat
['files'][$ID]) {
740 'filename' => basename($ID_absFile),
741 'ID_absFile' => $ID_absFile,
743 'relFileName' => $el['subst']['relFileName']
745 $this->export_addFile($fI, '_SOFTREF_');
747 $this->dat
['records'][$k]['rels'][$fieldname]['softrefs']['keys'][$spKey][$subKey]['file_ID'] = $ID;
757 $this->error('There were no records available.');
762 * Adds a files content to the export memory
764 * @param array $fI File information with three keys: "filename" = filename without path, "ID_absFile" = absolute filepath to the file (including the filename), "ID" = md5 hash of "ID_absFile". "relFileName" is optional for files attached to records, but mandatory for soft referenced files (since the relFileName determines where such a file should be stored!)
765 * @param string $recordRef If the file is related to a record, this is the id on the form [table]:[id]. Information purposes only.
766 * @param string $fieldname If the file is related to a record, this is the field name it was related to. Information purposes only.
768 * @todo Define visibility
770 public function export_addFile($fI, $recordRef = '', $fieldname = '') {
771 if (@is_file
($fI['ID_absFile'])) {
772 if (filesize($fI['ID_absFile']) < $this->maxFileSize
) {
774 $fileRec['filesize'] = filesize($fI['ID_absFile']);
775 $fileRec['filename'] = basename($fI['ID_absFile']);
776 $fileRec['filemtime'] = filemtime($fI['ID_absFile']);
777 //for internal type file_reference
778 $fileRec['relFileRef'] = substr($fI['ID_absFile'], strlen(PATH_site
));
780 $fileRec['record_ref'] = $recordRef . '/' . $fieldname;
782 if ($fI['relFileName']) {
783 $fileRec['relFileName'] = $fI['relFileName'];
785 // Setting this data in the header
786 $this->dat
['header']['files'][$fI['ID']] = $fileRec;
787 // ... and for the recordlisting, why not let us know WHICH relations there was...
788 if ($recordRef && $recordRef !== '_SOFTREF_') {
789 $refParts = explode(':', $recordRef, 2);
790 if (!is_array($this->dat
['header']['records'][$refParts[0]][$refParts[1]]['filerefs'])) {
791 $this->dat
['header']['records'][$refParts[0]][$refParts[1]]['filerefs'] = array();
793 $this->dat
['header']['records'][$refParts[0]][$refParts[1]]['filerefs'][] = $fI['ID'];
795 // ... and finally add the heavy stuff:
796 $fileRec['content'] = \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($fI['ID_absFile']);
797 $fileRec['content_md5'] = md5($fileRec['content']);
798 $this->dat
['files'][$fI['ID']] = $fileRec;
799 // For soft references, do further processing:
800 if ($recordRef === '_SOFTREF_') {
802 if ($RTEoriginal = $this->getRTEoriginalFilename(basename($fI['ID_absFile']))) {
803 $RTEoriginal_absPath = dirname($fI['ID_absFile']) . '/' . $RTEoriginal;
804 if (@is_file
($RTEoriginal_absPath)) {
805 $RTEoriginal_ID = md5($RTEoriginal_absPath);
807 $fileRec['filesize'] = filesize($RTEoriginal_absPath);
808 $fileRec['filename'] = basename($RTEoriginal_absPath);
809 $fileRec['filemtime'] = filemtime($RTEoriginal_absPath);
810 $fileRec['record_ref'] = '_RTE_COPY_ID:' . $fI['ID'];
811 $this->dat
['header']['files'][$fI['ID']]['RTE_ORIG_ID'] = $RTEoriginal_ID;
812 // Setting this data in the header
813 $this->dat
['header']['files'][$RTEoriginal_ID] = $fileRec;
814 // ... and finally add the heavy stuff:
815 $fileRec['content'] = \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($RTEoriginal_absPath);
816 $fileRec['content_md5'] = md5($fileRec['content']);
817 $this->dat
['files'][$RTEoriginal_ID] = $fileRec;
819 $this->error('RTE original file "' . substr($RTEoriginal_absPath, strlen(PATH_site
)) . '" was not found!');
822 // Files with external media?
823 // This is only done with files grabbed by a softreference parser since it is deemed improbable that hard-referenced files should undergo this treatment.
824 $html_fI = pathinfo(basename($fI['ID_absFile']));
825 if ($this->includeExtFileResources
&& \TYPO3\CMS\Core\Utility\GeneralUtility
::inList($this->extFileResourceExtensions
, strtolower($html_fI['extension']))) {
826 $uniquePrefix = '###' . md5($GLOBALS['EXEC_TIME']) . '###';
827 if (strtolower($html_fI['extension']) === 'css') {
828 $prefixedMedias = explode($uniquePrefix, preg_replace('/(url[[:space:]]*\\([[:space:]]*["\']?)([^"\')]*)(["\']?[[:space:]]*\\))/i', '\\1' . $uniquePrefix . '\\2' . $uniquePrefix . '\\3', $fileRec['content']));
831 $htmlParser = \TYPO3\CMS\Core\Utility\GeneralUtility
::makeInstance('TYPO3\\CMS\\Core\\Html\\HtmlParser');
832 $prefixedMedias = explode($uniquePrefix, $htmlParser->prefixResourcePath($uniquePrefix, $fileRec['content'], array(), $uniquePrefix));
834 $htmlResourceCaptured = FALSE;
835 foreach ($prefixedMedias as $k => $v) {
837 $EXTres_absPath = \TYPO3\CMS\Core\Utility\GeneralUtility
::resolveBackPath(dirname($fI['ID_absFile']) . '/' . $v);
838 $EXTres_absPath = \TYPO3\CMS\Core\Utility\GeneralUtility
::getFileAbsFileName($EXTres_absPath);
839 if ($EXTres_absPath && \TYPO3\CMS\Core\Utility\GeneralUtility
::isFirstPartOfStr($EXTres_absPath, PATH_site
. $this->fileadminFolderName
. '/') && @is_file
($EXTres_absPath)) {
840 $htmlResourceCaptured = TRUE;
841 $EXTres_ID = md5($EXTres_absPath);
842 $this->dat
['header']['files'][$fI['ID']]['EXT_RES_ID'][] = $EXTres_ID;
843 $prefixedMedias[$k] = '{EXT_RES_ID:' . $EXTres_ID . '}';
844 // Add file to memory if it is not set already:
845 if (!isset($this->dat
['header']['files'][$EXTres_ID])) {
847 $fileRec['filesize'] = filesize($EXTres_absPath);
848 $fileRec['filename'] = basename($EXTres_absPath);
849 $fileRec['filemtime'] = filemtime($EXTres_absPath);
850 $fileRec['record_ref'] = '_EXT_PARENT_:' . $fI['ID'];
851 // Media relative to the HTML file.
852 $fileRec['parentRelFileName'] = $v;
853 // Setting this data in the header
854 $this->dat
['header']['files'][$EXTres_ID] = $fileRec;
855 // ... and finally add the heavy stuff:
856 $fileRec['content'] = \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($EXTres_absPath);
857 $fileRec['content_md5'] = md5($fileRec['content']);
858 $this->dat
['files'][$EXTres_ID] = $fileRec;
863 if ($htmlResourceCaptured) {
864 $this->dat
['files'][$fI['ID']]['tokenizedContent'] = implode('', $prefixedMedias);
869 $this->error($fI['ID_absFile'] . ' was larger (' . \TYPO3\CMS\Core\Utility\GeneralUtility
::formatSize(filesize($fI['ID_absFile'])) . ') than the maxFileSize (' . \TYPO3\CMS\Core\Utility\GeneralUtility
::formatSize($this->maxFileSize
) . ')! Skipping.');
872 $this->error($fI['ID_absFile'] . ' was not a file! Skipping.');
877 * DB relations flattend to 1-dim array.
878 * The list will be unique, no table/uid combination will appear twice.
880 * @param array $dbrels 2-dim Array of database relations organized by table key
881 * @return array 1-dim array where entries are table:uid and keys are array with table/id
882 * @todo Define visibility
884 public function flatDBrels($dbrels) {
886 foreach ($dbrels as $dat) {
887 if ($dat['type'] == 'db') {
888 foreach ($dat['itemArray'] as $i) {
889 $list[$i['table'] . ':' . $i['id']] = $i;
892 if ($dat['type'] == 'flex' && is_array($dat['flexFormRels']['db'])) {
893 foreach ($dat['flexFormRels']['db'] as $subList) {
894 foreach ($subList as $i) {
895 $list[$i['table'] . ':' . $i['id']] = $i;
904 * Soft References flattend to 1-dim array.
906 * @param array $dbrels 2-dim Array of database relations organized by table key
907 * @return array 1-dim array where entries are arrays with properties of the soft link found and keys are a unique combination of field, spKey, structure path if applicable and token ID
908 * @todo Define visibility
910 public function flatSoftRefs($dbrels) {
912 foreach ($dbrels as $field => $dat) {
913 if (is_array($dat['softrefs']['keys'])) {
914 foreach ($dat['softrefs']['keys'] as $spKey => $elements) {
915 if (is_array($elements)) {
916 foreach ($elements as $subKey => $el) {
917 $lKey = $field . ':' . $spKey . ':' . $subKey;
918 $list[$lKey] = array_merge(array('field' => $field, 'spKey' => $spKey), $el);
919 // Add file_ID key to header - slightly "risky" way of doing this because if the calculation changes for the same value in $this->records[...] this will not work anymore!
920 if ($el['subst'] && $el['subst']['relFileName']) {
921 $list[$lKey]['file_ID'] = md5(PATH_site
. $el['subst']['relFileName']);
927 if ($dat['type'] == 'flex' && is_array($dat['flexFormRels']['softrefs'])) {
928 foreach ($dat['flexFormRels']['softrefs'] as $structurePath => $subSoftrefs) {
929 if (is_array($subSoftrefs['keys'])) {
930 foreach ($subSoftrefs['keys'] as $spKey => $elements) {
931 foreach ($elements as $subKey => $el) {
932 $lKey = $field . ':' . $structurePath . ':' . $spKey . ':' . $subKey;
933 $list[$lKey] = array_merge(array('field' => $field, 'spKey' => $spKey, 'structurePath' => $structurePath), $el);
934 // Add file_ID key to header - slightly "risky" way of doing this because if the calculation changes for the same value in $this->records[...] this will not work anymore!
935 if ($el['subst'] && $el['subst']['relFileName']) {
936 $list[$lKey]['file_ID'] = md5(PATH_site
. $el['subst']['relFileName']);
947 /**************************
951 *************************/
953 * This compiles and returns the data content for an exported file
955 * @param string $type Type of output; "xml" gives xml, otherwise serialized array, possibly compressed.
956 * @return string The output file stream
957 * @todo Define visibility
959 public function compileMemoryToFileContent($type = '') {
960 if ($type == 'xml') {
961 $out = $this->createXML();
963 $compress = $this->doOutputCompress();
966 $out .= $this->addFilePart(serialize($this->dat
['header']), $compress);
968 $out .= $this->addFilePart(serialize($this->dat
['records']), $compress);
970 $out .= $this->addFilePart(serialize($this->dat
['files']), $compress);
976 * Creates XML string from input array
978 * @return string XML content
979 * @todo Define visibility
981 public function createXML() {
984 'alt_options' => array(
986 'disableTypeAttrib' => TRUE,
987 'clearStackPath' => TRUE,
988 'parentTagMap' => array(
990 'records' => 'table',
992 'rec:rels' => 'relations',
993 'relations' => 'element',
994 'filerefs' => 'file',
995 'pid_lookup' => 'page_contents',
996 'header:relStaticTables' => 'static_tables',
997 'static_tables' => 'tablename',
998 'excludeMap' => 'item',
999 'softrefCfg' => 'softrefExportMode',
1000 'extensionDependencies' => 'extkey',
1001 'softrefs' => 'softref_element'
1003 'alt_options' => array(
1004 '/pagetree' => array(
1005 'disableTypeAttrib' => TRUE,
1006 'useIndexTagForNum' => 'node',
1007 'parentTagMap' => array(
1008 'node:subrow' => 'node'
1011 '/pid_lookup/page_contents' => array(
1012 'disableTypeAttrib' => TRUE,
1013 'parentTagMap' => array(
1014 'page_contents' => 'table'
1016 'grandParentTagMap' => array(
1017 'page_contents/table' => 'item'
1022 '/records' => array(
1023 'disableTypeAttrib' => TRUE,
1024 'parentTagMap' => array(
1025 'records' => 'tablerow',
1026 'tablerow:data' => 'fieldlist',
1027 'tablerow:rels' => 'related',
1028 'related' => 'field',
1029 'field:itemArray' => 'relations',
1030 'field:newValueFiles' => 'filerefs',
1031 'field:flexFormRels' => 'flexform',
1032 'relations' => 'element',
1033 'filerefs' => 'file',
1034 'flexform:db' => 'db_relations',
1035 'flexform:file' => 'file_relations',
1036 'flexform:softrefs' => 'softref_relations',
1037 'softref_relations' => 'structurePath',
1038 'db_relations' => 'path',
1039 'file_relations' => 'path',
1040 'path' => 'element',
1041 'keys' => 'softref_key',
1042 'softref_key' => 'softref_element'
1044 'alt_options' => array(
1045 '/records/tablerow/fieldlist' => array(
1046 'useIndexTagForAssoc' => 'field'
1051 'disableTypeAttrib' => TRUE,
1052 'parentTagMap' => array(
1058 // Creating XML file from $outputArray:
1059 $charset = $this->dat
['header']['charset'] ?
$this->dat
['header']['charset'] : 'utf-8';
1060 $XML = '<?xml version="1.0" encoding="' . $charset . '" standalone="yes" ?>' . LF
;
1061 $XML .= \TYPO3\CMS\Core\Utility\GeneralUtility
::array2xml($this->dat
, '', 0, 'T3RecordDocument', 0, $options);
1066 * Returns TRUE if the output should be compressed.
1068 * @return boolean TRUE if compression is possible AND requested.
1069 * @todo Define visibility
1071 public function doOutputCompress() {
1072 return $this->compress
&& !$this->dontCompress
;
1076 * Returns a content part for a filename being build.
1078 * @param array $data Data to store in part
1079 * @param boolean $compress Compress file?
1080 * @return string Content stream.
1081 * @todo Define visibility
1083 public function addFilePart($data, $compress = FALSE) {
1085 $data = gzcompress($data);
1087 return md5($data) . ':' . ($compress ?
'1' : '0') . ':' . str_pad(strlen($data), 10, '0', STR_PAD_LEFT
) . ':' . $data . ':';
1090 /***********************
1094 ***********************/
1096 * Imports the internal data array to $pid.
1098 * @param integer $pid Page ID in which to import the content
1100 * @todo Define visibility
1102 public function importData($pid) {
1103 // Set this flag to indicate that an import is being/has been done.
1104 $this->doesImport
= 1;
1106 // These vars MUST last for the whole section not being cleared. They are used by the method setRelations() which are called at the end of the import session.
1107 $this->import_mapId
= array();
1108 $this->import_newId
= array();
1109 $this->import_newId_pids
= array();
1110 // Temporary files stack initialized:
1111 $this->unlinkFiles
= array();
1112 $this->alternativeFileName
= array();
1113 $this->alternativeFilePath
= array();
1114 // Write records, first pages, then the rest
1115 // Fields with "hard" relations to database, files and flexform fields are kept empty during this run
1116 $this->writeRecords_pages($pid);
1117 $this->writeRecords_records($pid);
1118 // Finally all the file and DB record references must be fixed. This is done after all records have supposedly been written to database:
1119 // $this->import_mapId will indicate two things: 1) that a record WAS written to db and 2) that it has got a new id-number.
1120 $this->setRelations();
1121 // And when all DB relations are in place, we can fix file and DB relations in flexform fields (since data structures often depends on relations to a DS record):
1122 $this->setFlexFormRelations();
1123 // Unlink temporary files:
1124 $this->unlinkTempFiles();
1125 // Finally, traverse all records and process softreferences with substitution attributes.
1126 $this->processSoftReferences();
1130 * Writing pagetree/pages to database:
1132 * @param integer $pid PID in which to import. If the operation is an update operation, the root of the page tree inside will be moved to this PID unless it is the same as the root page from the import
1134 * @see writeRecords_records()
1135 * @todo Define visibility
1137 public function writeRecords_pages($pid) {
1138 // First, write page structure if any:
1139 if (is_array($this->dat
['header']['records']['pages'])) {
1140 // $pageRecords is a copy of the pages array in the imported file. Records here are unset one by one when the addSingle function is called.
1141 $pageRecords = $this->dat
['header']['records']['pages'];
1142 $this->import_data
= array();
1143 // First add page tree if any
1144 if (is_array($this->dat
['header']['pagetree'])) {
1145 $pagesFromTree = $this->flatInversePageTree($this->dat
['header']['pagetree']);
1146 foreach ($pagesFromTree as $uid) {
1147 $thisRec = $this->dat
['header']['records']['pages'][$uid];
1148 // PID: Set the main $pid, unless a NEW-id is found
1149 $setPid = isset($this->import_newId_pids
[$thisRec['pid']]) ?
$this->import_newId_pids
[$thisRec['pid']] : $pid;
1150 $this->addSingle('pages', $uid, $setPid);
1151 unset($pageRecords[$uid]);
1154 // Then add all remaining pages not in tree on root level:
1155 if (count($pageRecords)) {
1156 $remainingPageUids = array_keys($pageRecords);
1157 foreach ($remainingPageUids as $pUid) {
1158 $this->addSingle('pages', $pUid, $pid);
1161 // Now write to database:
1162 $tce = $this->getNewTCE();
1163 $this->callHook('before_writeRecordsPages', array(
1165 'data' => &$this->import_data
1167 $tce->suggestedInsertUids
= $this->suggestedInsertUids
;
1168 $tce->start($this->import_data
, array());
1169 $tce->process_datamap();
1170 $this->callHook('after_writeRecordsPages', array(
1173 // post-processing: Registering new ids (end all tcemain sessions with this)
1174 $this->addToMapId($tce->substNEWwithIDs
);
1175 // In case of an update, order pages from the page tree correctly:
1176 if ($this->update
&& is_array($this->dat
['header']['pagetree'])) {
1177 $this->writeRecords_pages_order($pid);
1183 * Organize all updated pages in page tree so they are related like in the import file
1184 * Only used for updates and when $this->dat['header']['pagetree'] is an array.
1186 * @param integer $pid Page id in which to import
1189 * @see writeRecords_pages(), writeRecords_records_order()
1190 * @todo Define visibility
1192 public function writeRecords_pages_order($pid) {
1193 $cmd_data = array();
1194 // Get uid-pid relations and traverse them in order to map to possible new IDs
1195 $pidsFromTree = $this->flatInversePageTree_pid($this->dat
['header']['pagetree']);
1196 foreach ($pidsFromTree as $origPid => $newPid) {
1197 if ($newPid >= 0 && $this->dontIgnorePid('pages', $origPid)) {
1198 // If the page had a new id (because it was created) use that instead!
1199 if (substr($this->import_newId_pids
[$origPid], 0, 3) === 'NEW') {
1200 if ($this->import_mapId
['pages'][$origPid]) {
1201 $mappedPid = $this->import_mapId
['pages'][$origPid];
1202 $cmd_data['pages'][$mappedPid]['move'] = $newPid;
1205 $cmd_data['pages'][$origPid]['move'] = $newPid;
1209 // Execute the move commands if any:
1210 if (count($cmd_data)) {
1211 $tce = $this->getNewTCE();
1212 $this->callHook('before_writeRecordsPagesOrder', array(
1214 'data' => &$cmd_data
1216 $tce->start(array(), $cmd_data);
1217 $tce->process_cmdmap();
1218 $this->callHook('after_writeRecordsPagesOrder', array(
1225 * Write all database records except pages (writtein in writeRecords_pages())
1227 * @param integer $pid Page id in which to import
1229 * @see writeRecords_pages()
1230 * @todo Define visibility
1232 public function writeRecords_records($pid) {
1233 // Write the rest of the records
1234 $this->import_data
= array();
1235 if (is_array($this->dat
['header']['records'])) {
1236 foreach ($this->dat
['header']['records'] as $table => $recs) {
1237 if ($table != 'pages') {
1238 foreach ($recs as $uid => $thisRec) {
1239 // PID: Set the main $pid, unless a NEW-id is found
1240 $setPid = isset($this->import_mapId
['pages'][$thisRec['pid']]) ?
$this->import_mapId
['pages'][$thisRec['pid']] : $pid;
1241 if (is_array($GLOBALS['TCA'][$table]) && $GLOBALS['TCA'][$table]['ctrl']['rootLevel']) {
1245 $this->addSingle($table, $uid, $setPid);
1250 $this->error('Error: No records defined in internal data array.');
1252 // Now write to database:
1253 $tce = $this->getNewTCE();
1254 $this->callHook('before_writeRecordsRecords', array(
1256 'data' => &$this->import_data
1258 $tce->suggestedInsertUids
= $this->suggestedInsertUids
;
1259 // Because all records are being submitted in their correct order with positive pid numbers - and so we should reverse submission order internally.
1260 $tce->reverseOrder
= 1;
1261 $tce->start($this->import_data
, array());
1262 $tce->process_datamap();
1263 $this->callHook('after_writeRecordsRecords', array(
1266 // post-processing: Removing files and registering new ids (end all tcemain sessions with this)
1267 $this->addToMapId($tce->substNEWwithIDs
);
1268 // In case of an update, order pages from the page tree correctly:
1269 if ($this->update
) {
1270 $this->writeRecords_records_order($pid);
1275 * Organize all updated record to their new positions.
1276 * Only used for updates
1278 * @param integer $mainPid Main PID into which we import.
1281 * @see writeRecords_records(), writeRecords_pages_order()
1282 * @todo Define visibility
1284 public function writeRecords_records_order($mainPid) {
1285 $cmd_data = array();
1286 if (is_array($this->dat
['header']['pagetree'])) {
1287 $pagesFromTree = $this->flatInversePageTree($this->dat
['header']['pagetree']);
1289 $pagesFromTree = array();
1291 if (is_array($this->dat
['header']['pid_lookup'])) {
1292 foreach ($this->dat
['header']['pid_lookup'] as $pid => $recList) {
1293 $newPid = isset($this->import_mapId
['pages'][$pid]) ?
$this->import_mapId
['pages'][$pid] : $mainPid;
1294 if (\TYPO3\CMS\Core\Utility\MathUtility
::canBeInterpretedAsInteger($newPid)) {
1295 foreach ($recList as $tableName => $uidList) {
1296 // If $mainPid===$newPid then we are on root level and we can consider to move pages as well!
1297 // (they will not be in the page tree!)
1298 if (($tableName != 'pages' ||
!$pagesFromTree[$pid]) && is_array($uidList)) {
1299 $uidList = array_reverse(array_keys($uidList));
1300 foreach ($uidList as $uid) {
1301 if ($this->dontIgnorePid($tableName, $uid)) {
1302 $cmd_data[$tableName][$uid]['move'] = $newPid;
1312 // Execute the move commands if any:
1313 if (count($cmd_data)) {
1314 $tce = $this->getNewTCE();
1315 $this->callHook('before_writeRecordsRecordsOrder', array(
1317 'data' => &$cmd_data
1319 $tce->start(array(), $cmd_data);
1320 $tce->process_cmdmap();
1321 $this->callHook('after_writeRecordsRecordsOrder', array(
1328 * Adds a single record to the $importData array. Also copies files to tempfolder.
1329 * However all File/DB-references and flexform field contents are set to blank for now! That is done with setRelations() later
1331 * @param string $table Table name (from import memory)
1332 * @param integer $uid Record UID (from import memory)
1333 * @param integer $pid Page id
1335 * @see writeRecords()
1336 * @todo Define visibility
1338 public function addSingle($table, $uid, $pid) {
1339 if ($this->import_mode
[$table . ':' . $uid] !== 'exclude') {
1340 $record = $this->dat
['records'][$table . ':' . $uid]['data'];
1341 if (is_array($record)) {
1342 if ($this->update
&& $this->doesRecordExist($table, $uid) && $this->import_mode
[$table . ':' . $uid] !== 'as_new') {
1345 $ID = uniqid('NEW');
1347 $this->import_newId
[$table . ':' . $ID] = array('table' => $table, 'uid' => $uid);
1348 if ($table == 'pages') {
1349 $this->import_newId_pids
[$uid] = $ID;
1351 // Set main record data:
1352 $this->import_data
[$table][$ID] = $record;
1353 $this->import_data
[$table][$ID]['tx_impexp_origuid'] = $this->import_data
[$table][$ID]['uid'];
1354 // Reset permission data:
1355 if ($table === 'pages') {
1356 // Have to reset the user/group IDs so pages are owned by importing user. Otherwise strange things may happen for non-admins!
1357 unset($this->import_data
[$table][$ID]['perms_userid']);
1358 unset($this->import_data
[$table][$ID]['perms_groupid']);
1361 unset($this->import_data
[$table][$ID]['uid']);
1363 if (\TYPO3\CMS\Core\Utility\MathUtility
::canBeInterpretedAsInteger($ID)) {
1364 unset($this->import_data
[$table][$ID]['pid']);
1367 $this->import_data
[$table][$ID]['pid'] = $pid;
1368 if (($this->import_mode
[$table . ':' . $uid] === 'force_uid' && $this->update ||
$this->force_all_UIDS
) && $GLOBALS['BE_USER']->isAdmin()) {
1369 $this->import_data
[$table][$ID]['uid'] = $uid;
1370 $this->suggestedInsertUids
[$table . ':' . $uid] = 'DELETE';
1373 // Setting db/file blank:
1374 foreach ($this->dat
['records'][$table . ':' . $uid]['rels'] as $field => $config) {
1375 switch ((string) $config['type']) {
1379 // Fixed later in ->setRelations() [because we need to know ALL newly created IDs before we can map relations!]
1380 // In the meantime we set NO values for relations:
1381 $this->import_data
[$table][$ID][$field] = '';
1384 // Fixed later in setFlexFormRelations()
1385 // In the meantime we set NO value for flexforms - this is mainly because file references inside will not be processed properly; In fact references will point to no file or existing files (in which case there will be double-references which is a big problem of course!)
1386 $this->import_data
[$table][$ID][$field] = '';
1390 } elseif ($table . ':' . $uid != 'pages:0') {
1391 // On root level we don't want this error message.
1392 $this->error('Error: no record was found in data array!', 1);
1398 * Registers the substNEWids in memory.
1400 * @param array $substNEWwithIDs From tcemain to be merged into internal mapping variable in this object
1402 * @see writeRecords()
1403 * @todo Define visibility
1405 public function addToMapId($substNEWwithIDs) {
1406 foreach ($this->import_data
as $table => $recs) {
1407 foreach ($recs as $id => $value) {
1408 $old_uid = $this->import_newId
[$table . ':' . $id]['uid'];
1409 if (isset($substNEWwithIDs[$id])) {
1410 $this->import_mapId
[$table][$old_uid] = $substNEWwithIDs[$id];
1411 } elseif ($this->update
) {
1412 // Map same ID to same ID....
1413 $this->import_mapId
[$table][$old_uid] = $id;
1415 $this->error('Possible error: ' . $table . ':' . $old_uid . ' had no new id assigned to it. This indicates that the record was not added to database during import. Please check changelog!', 1);
1422 * Returns a new $TCE object
1424 * @return object $TCE object
1425 * @todo Define visibility
1427 public function getNewTCE() {
1428 $tce = \TYPO3\CMS\Core\Utility\GeneralUtility
::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
1429 $tce->stripslashes_values
= 0;
1430 $tce->dontProcessTransformations
= 1;
1431 $tce->enableLogging
= $this->enableLogging
;
1432 $tce->alternativeFileName
= $this->alternativeFileName
;
1433 $tce->alternativeFilePath
= $this->alternativeFilePath
;
1438 * Cleaning up all the temporary files stored in typo3temp/ folder
1441 * @todo Define visibility
1443 public function unlinkTempFiles() {
1444 foreach ($this->unlinkFiles
as $fileName) {
1445 if (\TYPO3\CMS\Core\Utility\GeneralUtility
::isFirstPartOfStr($fileName, PATH_site
. 'typo3temp/')) {
1446 \TYPO3\CMS\Core\Utility\GeneralUtility
::unlink_tempfile($fileName);
1448 if (is_file($fileName)) {
1449 $this->error('Error: ' . $fileName . ' was NOT unlinked as it should have been!', 1);
1452 $this->error('Error: ' . $fileName . ' was not in temp-path. Not removed!', 1);
1455 $this->unlinkFiles
= array();
1458 /***************************
1460 * Import / Relations setting
1462 ***************************/
1464 * At the end of the import process all file and DB relations should be set properly (that is relations to imported records are all re-created so imported records are correctly related again)
1465 * Relations in flexform fields are processed in setFlexFormRelations() after this function
1468 * @see setFlexFormRelations()
1469 * @todo Define visibility
1471 public function setRelations() {
1472 $updateData = array();
1473 // import_newId contains a register of all records that was in the import memorys "records" key
1474 foreach ($this->import_newId
as $nId => $dat) {
1475 $table = $dat['table'];
1477 // original UID - NOT the new one!
1478 // If the record has been written and received a new id, then proceed:
1479 if (is_array($this->import_mapId
[$table]) && isset($this->import_mapId
[$table][$uid])) {
1480 $thisNewUid = \TYPO3\CMS\Backend\Utility\BackendUtility
::wsMapId($table, $this->import_mapId
[$table][$uid]);
1481 if (is_array($this->dat
['records'][$table . ':' . $uid]['rels'])) {
1482 // Traverse relation fields of each record
1483 foreach ($this->dat
['records'][$table . ':' . $uid]['rels'] as $field => $config) {
1484 switch ((string) $config['type']) {
1486 if (is_array($config['itemArray']) && count($config['itemArray'])) {
1487 $valArray = $this->setRelations_db($config['itemArray']);
1488 $updateData[$table][$thisNewUid][$field] = implode(',', $valArray);
1492 if (is_array($config['newValueFiles']) && count($config['newValueFiles'])) {
1494 foreach ($config['newValueFiles'] as $fI) {
1495 $valArr[] = $this->import_addFileNameToBeCopied($fI);
1497 $updateData[$table][$thisNewUid][$field] = implode(',', $valArr);
1503 $this->error('Error: no record was found in data array!', 1);
1506 $this->error('Error: this records is NOT created it seems! (' . $table . ':' . $uid . ')', 1);
1509 if (count($updateData)) {
1510 $tce = $this->getNewTCE();
1511 $this->callHook('before_setRelation', array(
1513 'data' => &$updateData
1515 $tce->start($updateData, array());
1516 $tce->process_datamap();
1517 $this->callHook('after_setRelations', array(
1524 * Maps relations for database
1526 * @param array $itemArray Array of item sets (table/uid) from a dbAnalysis object
1527 * @return array Array with values [table]_[uid]. These values have the regular tcemain-input group/select type which means they will automatically be processed into a uid-list or MM relations.
1528 * @todo Define visibility
1530 public function setRelations_db($itemArray) {
1531 $valArray = array();
1532 foreach ($itemArray as $relDat) {
1533 if (is_array($this->import_mapId
[$relDat['table']]) && isset($this->import_mapId
[$relDat['table']][$relDat['id']])) {
1534 $valArray[] = $relDat['table'] . '_' . $this->import_mapId
[$relDat['table']][$relDat['id']];
1535 } elseif ($this->isTableStatic($relDat['table']) ||
$this->isExcluded($relDat['table'], $relDat['id']) ||
$relDat['id'] < 0) {
1536 // Checking for less than zero because some select types could contain negative values, eg. fe_groups (-1, -2) and sys_language (-1 = ALL languages). This must be handled on both export and import.
1537 $valArray[] = $relDat['table'] . '_' . $relDat['id'];
1539 $this->error('Lost relation: ' . $relDat['table'] . ':' . $relDat['id'], 1);
1546 * Writes the file from import array to temp dir and returns the filename of it.
1548 * @param array $fI File information with three keys: "filename" = filename without path, "ID_absFile" = absolute filepath to the file (including the filename), "ID" = md5 hash of "ID_absFile
1549 * @return string Absolute filename of the temporary filename of the file. In ->alternativeFileName the original name is set.
1550 * @todo Define visibility
1552 public function import_addFileNameToBeCopied($fI) {
1553 if (is_array($this->dat
['files'][$fI['ID']])) {
1554 $tmpFile = \TYPO3\CMS\Core\Utility\GeneralUtility
::tempnam('import_temp_');
1555 \TYPO3\CMS\Core\Utility\GeneralUtility
::writeFile($tmpFile, $this->dat
['files'][$fI['ID']]['content']);
1557 if (@is_file
($tmpFile)) {
1558 $this->unlinkFiles
[] = $tmpFile;
1559 if (filesize($tmpFile) == $this->dat
['files'][$fI['ID']]['filesize']) {
1560 $this->alternativeFileName
[$tmpFile] = $fI['filename'];
1561 $this->alternativeFilePath
[$tmpFile] = $this->dat
['files'][$fI['ID']]['relFileRef'];
1564 $this->error('Error: temporary file ' . $tmpFile . ' had a size (' . filesize($tmpFile) . ') different from the original (' . $this->dat
['files'][$fI['ID']]['filesize'] . ')', 1);
1567 $this->error('Error: temporary file ' . $tmpFile . ' was not written as it should have been!', 1);
1570 $this->error('Error: No file found for ID ' . $fI['ID'], 1);
1575 * After all DB relations has been set in the end of the import (see setRelations()) then it is time to correct all relations inside of FlexForm fields.
1576 * The reason for doing this after is that the setting of relations may affect (quite often!) which data structure is used for the flexforms field!
1579 * @see setRelations()
1580 * @todo Define visibility
1582 public function setFlexFormRelations() {
1583 $updateData = array();
1584 // import_newId contains a register of all records that was in the import memorys "records" key
1585 foreach ($this->import_newId
as $nId => $dat) {
1586 $table = $dat['table'];
1588 // original UID - NOT the new one!
1589 // If the record has been written and received a new id, then proceed:
1590 if (is_array($this->import_mapId
[$table]) && isset($this->import_mapId
[$table][$uid])) {
1591 $thisNewUid = \TYPO3\CMS\Backend\Utility\BackendUtility
::wsMapId($table, $this->import_mapId
[$table][$uid]);
1592 if (is_array($this->dat
['records'][$table . ':' . $uid]['rels'])) {
1593 \TYPO3\CMS\Core\Utility\GeneralUtility
::loadTCA($table);
1594 // Traverse relation fields of each record
1595 foreach ($this->dat
['records'][$table . ':' . $uid]['rels'] as $field => $config) {
1596 switch ((string) $config['type']) {
1598 // Get XML content and set as default value (string, non-processed):
1599 $updateData[$table][$thisNewUid][$field] = $this->dat
['records'][$table . ':' . $uid]['data'][$field];
1600 // If there has been registered relations inside the flex form field, run processing on the content:
1601 if (count($config['flexFormRels']['db']) ||
count($config['flexFormRels']['file'])) {
1602 $origRecordRow = \TYPO3\CMS\Backend\Utility\BackendUtility
::getRecord($table, $thisNewUid, '*');
1603 // This will fetch the new row for the element (which should be updated with any references to data structures etc.)
1604 $conf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
1605 if (is_array($origRecordRow) && is_array($conf) && $conf['type'] === 'flex') {
1606 // Get current data structure and value array:
1607 $dataStructArray = \TYPO3\CMS\Backend\Utility\BackendUtility
::getFlexFormDS($conf, $origRecordRow, $table);
1608 $currentValueArray = \TYPO3\CMS\Core\Utility\GeneralUtility
::xml2array($updateData[$table][$thisNewUid][$field]);
1609 // Do recursive processing of the XML data:
1610 $iteratorObj = \TYPO3\CMS\Core\Utility\GeneralUtility
::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
1611 $iteratorObj->callBackObj
= $this;
1612 $currentValueArray['data'] = $iteratorObj->checkValue_flex_procInData($currentValueArray['data'], array(), array(), $dataStructArray, array($table, $thisNewUid, $field, $config), 'remapListedDBRecords_flexFormCallBack');
1613 // The return value is set as an array which means it will be processed by tcemain for file and DB references!
1614 if (is_array($currentValueArray['data'])) {
1615 $updateData[$table][$thisNewUid][$field] = $currentValueArray;
1623 $this->error('Error: no record was found in data array!', 1);
1626 $this->error('Error: this records is NOT created it seems! (' . $table . ':' . $uid . ')', 1);
1629 if (count($updateData)) {
1630 $tce = $this->getNewTCE();
1631 $this->callHook('before_setFlexFormRelations', array(
1633 'data' => &$updateData
1635 $tce->start($updateData, array());
1636 $tce->process_datamap();
1637 $this->callHook('after_setFlexFormRelations', array(
1644 * Callback function for traversing the FlexForm structure in relation to remapping database relations
1646 * @param array $pParams Set of parameters in numeric array: table, uid, field
1647 * @param array $dsConf TCA config for field (from Data Structure of course)
1648 * @param string $dataValue Field value (from FlexForm XML)
1649 * @param string $dataValue_ext1 Not used
1650 * @param string $dataValue_ext2 Not used
1651 * @param string $path Path of where the data structure of the element is found
1652 * @return array Array where the "value" key carries the value.
1653 * @see setFlexFormRelations()
1654 * @todo Define visibility
1656 public function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) {
1657 // Extract parameters:
1658 list($table, $uid, $field, $config) = $pParams;
1659 // In case the $path is used as index without a trailing slash we will remove that
1660 if (!is_array($config['flexFormRels']['db'][$path]) && is_array($config['flexFormRels']['db'][rtrim($path, '/')])) {
1661 $path = rtrim($path, '/');
1663 if (is_array($config['flexFormRels']['db'][$path])) {
1664 $valArray = $this->setRelations_db($config['flexFormRels']['db'][$path]);
1665 $dataValue = implode(',', $valArray);
1667 if (is_array($config['flexFormRels']['file'][$path])) {
1668 foreach ($config['flexFormRels']['file'][$path] as $fI) {
1669 $valArr[] = $this->import_addFileNameToBeCopied($fI);
1671 $dataValue = implode(',', $valArr);
1673 return array('value' => $dataValue);
1676 /**************************
1678 * Import / Soft References
1680 *************************/
1682 * Processing of soft references
1685 * @todo Define visibility
1687 public function processSoftReferences() {
1690 // Traverse records:
1691 if (is_array($this->dat
['header']['records'])) {
1692 foreach ($this->dat
['header']['records'] as $table => $recs) {
1693 foreach ($recs as $uid => $thisRec) {
1694 // If there are soft references defined, traverse those:
1695 if (isset($GLOBALS['TCA'][$table]) && is_array($thisRec['softrefs'])) {
1696 \TYPO3\CMS\Core\Utility\GeneralUtility
::loadTCA($table);
1697 // First traversal is to collect softref configuration and split them up based on fields. This could probably also have been done with the "records" key instead of the header.
1698 $fieldsIndex = array();
1699 foreach ($thisRec['softrefs'] as $softrefDef) {
1700 // If a substitution token is set:
1701 if ($softrefDef['field'] && is_array($softrefDef['subst']) && $softrefDef['subst']['tokenID']) {
1702 $fieldsIndex[$softrefDef['field']][$softrefDef['subst']['tokenID']] = $softrefDef;
1706 $thisNewUid = \TYPO3\CMS\Backend\Utility\BackendUtility
::wsMapId($table, $this->import_mapId
[$table][$uid]);
1707 // Now, if there are any fields that require substitution to be done, lets go for that:
1708 foreach ($fieldsIndex as $field => $softRefCfgs) {
1709 if (is_array($GLOBALS['TCA'][$table]['columns'][$field])) {
1710 $conf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
1711 if ($conf['type'] === 'flex') {
1712 // This will fetch the new row for the element (which should be updated with any references to data structures etc.)
1713 $origRecordRow = \TYPO3\CMS\Backend\Utility\BackendUtility
::getRecord($table, $thisNewUid, '*');
1714 if (is_array($origRecordRow)) {
1715 // Get current data structure and value array:
1716 $dataStructArray = \TYPO3\CMS\Backend\Utility\BackendUtility
::getFlexFormDS($conf, $origRecordRow, $table);
1717 $currentValueArray = \TYPO3\CMS\Core\Utility\GeneralUtility
::xml2array($origRecordRow[$field]);
1718 // Do recursive processing of the XML data:
1719 /** @var $iteratorObj \TYPO3\CMS\Core\DataHandling\DataHandler */
1720 $iteratorObj = \TYPO3\CMS\Core\Utility\GeneralUtility
::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
1721 $iteratorObj->callBackObj
= $this;
1722 $currentValueArray['data'] = $iteratorObj->checkValue_flex_procInData($currentValueArray['data'], array(), array(), $dataStructArray, array($table, $uid, $field, $softRefCfgs), 'processSoftReferences_flexFormCallBack');
1723 // The return value is set as an array which means it will be processed by tcemain for file and DB references!
1724 if (is_array($currentValueArray['data'])) {
1725 $inData[$table][$thisNewUid][$field] = $currentValueArray;
1729 // Get tokenizedContent string and proceed only if that is not blank:
1730 $tokenizedContent = $this->dat
['records'][$table . ':' . $uid]['rels'][$field]['softrefs']['tokenizedContent'];
1731 if (strlen($tokenizedContent) && is_array($softRefCfgs)) {
1732 $inData[$table][$thisNewUid][$field] = $this->processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid);
1741 // Now write to database:
1742 $tce = $this->getNewTCE();
1743 $this->callHook('before_processSoftReferences', array(
1747 $tce->enableLogging
= TRUE;
1748 $tce->start($inData, array());
1749 $tce->process_datamap();
1750 $this->callHook('after_processSoftReferences', array(
1756 * Callback function for traversing the FlexForm structure in relation to remapping softreference relations
1758 * @param array $pParams Set of parameters in numeric array: table, uid, field
1759 * @param array $dsConf TCA config for field (from Data Structure of course)
1760 * @param string $dataValue Field value (from FlexForm XML)
1761 * @param string $dataValue_ext1 Not used
1762 * @param string $dataValue_ext2 Not used
1763 * @param string $path Path of where the data structure where the element is found
1764 * @return array Array where the "value" key carries the value.
1765 * @see setFlexFormRelations()
1766 * @todo Define visibility
1768 public function processSoftReferences_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) {
1769 // Extract parameters:
1770 list($table, $origUid, $field, $softRefCfgs) = $pParams;
1771 if (is_array($softRefCfgs)) {
1772 // First, find all soft reference configurations for this structure path (they are listed flat in the header):
1773 $thisSoftRefCfgList = array();
1774 foreach ($softRefCfgs as $sK => $sV) {
1775 if ($sV['structurePath'] === $path) {
1776 $thisSoftRefCfgList[$sK] = $sV;
1779 // If any was found, do processing:
1780 if (count($thisSoftRefCfgList)) {
1781 // Get tokenizedContent string and proceed only if that is not blank:
1782 $tokenizedContent = $this->dat
['records'][$table . ':' . $origUid]['rels'][$field]['flexFormRels']['softrefs'][$path]['tokenizedContent'];
1783 if (strlen($tokenizedContent)) {
1784 $dataValue = $this->processSoftReferences_substTokens($tokenizedContent, $thisSoftRefCfgList, $table, $origUid);
1789 return array('value' => $dataValue);
1793 * Substition of softreference tokens
1795 * @param string $tokenizedContent Content of field with soft reference tokens in.
1796 * @param array $softRefCfgs Soft reference configurations
1797 * @param string $table Table for which the processing occurs
1798 * @param string $uid UID of record from table
1799 * @return string The input content with tokens substituted according to entries in softRefCfgs
1800 * @todo Define visibility
1802 public function processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid) {
1803 // traverse each softref type for this field:
1804 foreach ($softRefCfgs as $cfg) {
1806 $tokenID = $cfg['subst']['tokenID'];
1807 // Default is current token value:
1808 $insertValue = $cfg['subst']['tokenValue'];
1810 switch ((string) $this->softrefCfg
[$tokenID]['mode']) {
1812 // Exclude is a simple passthrough of the value
1815 // Editable always picks up the value from this input array:
1816 $insertValue = $this->softrefInputValues
[$tokenID];
1819 // Mapping IDs/creating files: Based on type, look up new value:
1820 switch ((string) $cfg['subst']['type']) {
1822 // Create / Overwrite file:
1823 $insertValue = $this->processSoftReferences_saveFile($cfg['subst']['relFileName'], $cfg, $table, $uid);
1827 // Trying to map database element if found in the mapID array:
1828 list($tempTable, $tempUid) = explode(':', $cfg['subst']['recordRef']);
1829 if (isset($this->import_mapId
[$tempTable][$tempUid])) {
1830 $insertValue = \TYPO3\CMS\Backend\Utility\BackendUtility
::wsMapId($tempTable, $this->import_mapId
[$tempTable][$tempUid]);
1831 // Look if reference is to a page and the original token value was NOT an integer - then we assume is was an alias and try to look up the new one!
1832 if ($tempTable === 'pages' && !\TYPO3\CMS\Core\Utility\MathUtility
::canBeInterpretedAsInteger($cfg['subst']['tokenValue'])) {
1833 $recWithUniqueValue = \TYPO3\CMS\Backend\Utility\BackendUtility
::getRecord($tempTable, $insertValue, 'alias');
1834 if ($recWithUniqueValue['alias']) {
1835 $insertValue = $recWithUniqueValue['alias'];
1843 // Finally, swap the soft reference token in tokenized content with the insert value:
1844 $tokenizedContent = str_replace('{softref:' . $tokenID . '}', $insertValue, $tokenizedContent);
1846 return $tokenizedContent;
1850 * Process a soft reference file
1852 * @param string $relFileName Old Relative filename
1853 * @param array $cfg soft reference configuration array
1854 * @param string $table Table for which the processing occurs
1855 * @param string $uid UID of record from table
1856 * @return string New relative filename (value to insert instead of the softref token)
1857 * @todo Define visibility
1859 public function processSoftReferences_saveFile($relFileName, $cfg, $table, $uid) {
1860 if ($fileHeaderInfo = $this->dat
['header']['files'][$cfg['file_ID']]) {
1861 // Initialize; Get directory prefix for file and find possible RTE filename
1862 $dirPrefix = dirname($relFileName) . '/';
1863 $rteOrigName = $this->getRTEoriginalFilename(basename($relFileName));
1864 // If filename looks like an RTE file, and the directory is in "uploads/", then process as a RTE file!
1865 if ($rteOrigName && \TYPO3\CMS\Core\Utility\GeneralUtility
::isFirstPartOfStr($dirPrefix, 'uploads/')) {
1867 // First, find unique RTE file name:
1868 if (@is_dir
((PATH_site
. $dirPrefix))) {
1869 // From the "original" RTE filename, produce a new "original" destination filename which is unused. Even if updated, the image should be unique. Currently the problem with this is that it leaves a lot of unused RTE images...
1870 $fileProcObj = $this->getFileProcObj();
1871 $origDestName = $fileProcObj->getUniqueName($rteOrigName, PATH_site
. $dirPrefix);
1872 // Create copy file name:
1873 $pI = pathinfo($relFileName);
1874 $copyDestName = dirname($origDestName) . '/RTEmagicC_' . substr(basename($origDestName), 10) . '.' . $pI['extension'];
1875 if (!@is_file
($copyDestName) && !@is_file
($origDestName) && $origDestName === \TYPO3\CMS\Core\Utility\GeneralUtility
::getFileAbsFileName($origDestName) && $copyDestName === \TYPO3\CMS\Core\Utility\GeneralUtility
::getFileAbsFileName($copyDestName)) {
1876 if ($this->dat
['header']['files'][$fileHeaderInfo['RTE_ORIG_ID']]) {
1877 // Write the copy and original RTE file to the respective filenames:
1878 $this->writeFileVerify($copyDestName, $cfg['file_ID'], TRUE);
1879 $this->writeFileVerify($origDestName, $fileHeaderInfo['RTE_ORIG_ID'], TRUE);
1880 // Return the relative path of the copy file name:
1881 return substr($copyDestName, strlen(PATH_site
));
1883 $this->error('ERROR: Could not find original file ID');
1886 $this->error('ERROR: The destination filenames "' . $copyDestName . '" and "' . $origDestName . '" either existed or have non-valid names');
1889 $this->error('ERROR: "' . PATH_site
. $dirPrefix . '" was not a directory, so could not process file "' . $relFileName . '"');
1891 } elseif (\TYPO3\CMS\Core\Utility\GeneralUtility
::isFirstPartOfStr($dirPrefix, $this->fileadminFolderName
. '/')) {
1892 // File in fileadmin/ folder:
1893 // Create file (and possible resources)
1894 $newFileName = $this->processSoftReferences_saveFile_createRelFile($dirPrefix, basename($relFileName), $cfg['file_ID'], $table, $uid);
1895 if (strlen($newFileName)) {
1896 $relFileName = $newFileName;
1898 $this->error('ERROR: No new file created for "' . $relFileName . '"');
1901 $this->error('ERROR: Sorry, cannot operate on non-RTE files which are outside the fileadmin folder.');
1904 $this->error('ERROR: Could not find file ID in header.');
1906 // Return (new) filename relative to PATH_site:
1907 return $relFileName;
1911 * Create file in directory and return the new (unique) filename
1913 * @param string $origDirPrefix Directory prefix, relative, with trailing slash
1914 * @param string $fileName Filename (without path)
1915 * @param string $fileID File ID from import memory
1916 * @param string $table Table for which the processing occurs
1917 * @param string $uid UID of record from table
1918 * @return string New relative filename, if any
1919 * @todo Define visibility
1921 public function processSoftReferences_saveFile_createRelFile($origDirPrefix, $fileName, $fileID, $table, $uid) {
1922 // If the fileID map contains an entry for this fileID then just return the relative filename of that entry; we don't want to write another unique filename for this one!
1923 if ($this->fileIDMap
[$fileID]) {
1924 return substr($this->fileIDMap
[$fileID], strlen(PATH_site
));
1926 // Verify FileMount access to dir-prefix. Returns the best alternative relative path if any
1927 $dirPrefix = $this->verifyFolderAccess($origDirPrefix);
1928 if ($dirPrefix && (!$this->update ||
$origDirPrefix === $dirPrefix) && $this->checkOrCreateDir($dirPrefix)) {
1929 $fileHeaderInfo = $this->dat
['header']['files'][$fileID];
1930 $updMode = $this->update
&& $this->import_mapId
[$table][$uid] === $uid && $this->import_mode
[$table . ':' . $uid] !== 'as_new';
1931 // Create new name for file:
1932 // Must have same ID in map array (just for security, is not really needed) and NOT be set "as_new".
1934 $newName = PATH_site
. $dirPrefix . $fileName;
1936 // Create unique filename:
1937 $fileProcObj = $this->getFileProcObj();
1938 $newName = $fileProcObj->getUniqueName($fileName, PATH_site
. $dirPrefix);
1941 if ($this->writeFileVerify($newName, $fileID)) {
1942 // If the resource was an HTML/CSS file with resources attached, we will write those as well!
1943 if (is_array($fileHeaderInfo['EXT_RES_ID'])) {
1944 $tokenizedContent = $this->dat
['files'][$fileID]['tokenizedContent'];
1945 $tokenSubstituted = FALSE;
1946 $fileProcObj = $this->getFileProcObj();
1948 foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
1949 if ($this->dat
['files'][$res_fileID]['filename']) {
1950 // Resolve original filename:
1951 $relResourceFileName = $this->dat
['files'][$res_fileID]['parentRelFileName'];
1952 $absResourceFileName = \TYPO3\CMS\Core\Utility\GeneralUtility
::resolveBackPath(PATH_site
. $origDirPrefix . $relResourceFileName);
1953 $absResourceFileName = \TYPO3\CMS\Core\Utility\GeneralUtility
::getFileAbsFileName($absResourceFileName);
1954 if ($absResourceFileName && \TYPO3\CMS\Core\Utility\GeneralUtility
::isFirstPartOfStr($absResourceFileName, PATH_site
. $this->fileadminFolderName
. '/')) {
1955 $destDir = substr(dirname($absResourceFileName) . '/', strlen(PATH_site
));
1956 if ($this->verifyFolderAccess($destDir, TRUE) && $this->checkOrCreateDir($destDir)) {
1957 $this->writeFileVerify($absResourceFileName, $res_fileID);
1959 $this->error('ERROR: Could not create file in directory "' . $destDir . '"');
1962 $this->error('ERROR: Could not resolve path for "' . $relResourceFileName . '"');
1964 $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent);
1965 $tokenSubstituted = TRUE;
1969 // Create the resouces directory name (filename without extension, suffixed "_FILES")
1970 $resourceDir = dirname($newName) . '/' . preg_replace('/\\.[^.]*$/', '', basename($newName)) . '_FILES';
1971 if (\TYPO3\CMS\Core\Utility\GeneralUtility
::mkdir($resourceDir)) {
1972 foreach ($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
1973 if ($this->dat
['files'][$res_fileID]['filename']) {
1974 $absResourceFileName = $fileProcObj->getUniqueName($this->dat
['files'][$res_fileID]['filename'], $resourceDir);
1975 $relResourceFileName = substr($absResourceFileName, strlen(dirname($resourceDir)) +
1);
1976 $this->writeFileVerify($absResourceFileName, $res_fileID);
1977 $tokenizedContent = str_replace('{EXT_RES_ID:' . $res_fileID . '}', $relResourceFileName, $tokenizedContent);
1978 $tokenSubstituted = TRUE;
1983 // If substitutions has been made, write the content to the file again:
1984 if ($tokenSubstituted) {
1985 \TYPO3\CMS\Core\Utility\GeneralUtility
::writeFile($newName, $tokenizedContent);
1988 return substr($newName, strlen(PATH_site
));
1994 * Writes a file from the import memory having $fileID to file name $fileName which must be an absolute path inside PATH_site
1996 * @param string $fileName Absolute filename inside PATH_site to write to
1997 * @param string $fileID File ID from import memory
1998 * @param boolean $bypassMountCheck Bypasses the checking against filemounts - only for RTE files!
1999 * @return boolean Returns TRUE if it went well. Notice that the content of the file is read again, and md5 from import memory is validated.
2000 * @todo Define visibility
2002 public function writeFileVerify($fileName, $fileID, $bypassMountCheck = FALSE) {
2003 $fileProcObj = $this->getFileProcObj();
2004 if ($fileProcObj->actionPerms
['newFile']) {
2005 // Just for security, check again. Should actually not be necessary.
2006 if ($fileProcObj->checkPathAgainstMounts($fileName) ||
$bypassMountCheck) {
2007 $fI = \TYPO3\CMS\Core\Utility\GeneralUtility
::split_fileref($fileName);
2008 if ($fileProcObj->checkIfAllowed($fI['fileext'], $fI['path'], $fI['file']) ||
$this->allowPHPScripts
&& $GLOBALS['BE_USER']->isAdmin()) {
2009 if (\TYPO3\CMS\Core\Utility\GeneralUtility
::getFileAbsFileName($fileName)) {
2010 if ($this->dat
['files'][$fileID]) {
2011 \TYPO3\CMS\Core\Utility\GeneralUtility
::writeFile($fileName, $this->dat
['files'][$fileID]['content']);
2012 $this->fileIDMap
[$fileID] = $fileName;
2013 if (md5(\TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($fileName)) == $this->dat
['files'][$fileID]['content_md5']) {
2016 $this->error('ERROR: File content "' . $fileName . '" was corrupted');
2019 $this->error('ERROR: File ID "' . $fileID . '" could not be found');
2022 $this->error('ERROR: Filename "' . $fileName . '" was not a valid relative file path!');
2025 $this->error('ERROR: Filename "' . $fileName . '" failed against extension check or deny-pattern!');
2028 $this->error('ERROR: Filename "' . $fileName . '" was not allowed in destination path!');
2031 $this->error('ERROR: You did not have sufficient permissions to write the file "' . $fileName . '"');
2036 * Returns TRUE if directory exists and if it doesn't it will create directory and return TRUE if that succeeded.
2038 * @param string $dirPrefix Directory to create. Having a trailing slash. Must be in fileadmin/. Relative to PATH_site
2039 * @return boolean TRUE, if directory exists (was created)
2040 * @todo Define visibility
2042 public function checkOrCreateDir($dirPrefix) {
2043 // Split dir path and remove first directory (which should be "fileadmin")
2044 $filePathParts = explode('/', $dirPrefix);
2045 $firstDir = array_shift($filePathParts);
2046 if ($firstDir === $this->fileadminFolderName
&& \TYPO3\CMS\Core\Utility\GeneralUtility
::getFileAbsFileName($dirPrefix)) {
2048 foreach ($filePathParts as $dirname) {
2049 $pathAcc .= '/' . $dirname;
2050 if (strlen($dirname)) {
2051 if (!@is_dir
((PATH_site
. $this->fileadminFolderName
. $pathAcc))) {
2052 if (!\TYPO3\CMS\Core\Utility\GeneralUtility
::mkdir((PATH_site
. $this->fileadminFolderName
. $pathAcc))) {
2053 $this->error('ERROR: Directory could not be created....B');
2057 } elseif ($dirPrefix === $this->fileadminFolderName
. $pathAcc) {
2060 $this->error('ERROR: Directory could not be created....A');
2067 * Verifies that the input path (relative to PATH_site) is found in the backend users filemounts.
2068 * If it doesn't it will try to find another relative filemount for the user and return an alternative path prefix for the file.
2070 * @param string $dirPrefix Path relative to PATH_site
2071 * @param boolean $noAlternative If set, Do not look for alternative path! Just return FALSE
2072 * @return string If a path is available that will be returned, otherwise FALSE.
2073 * @todo Define visibility
2075 public function verifyFolderAccess($dirPrefix, $noAlternative = FALSE) {
2076 $fileProcObj = $this->getFileProcObj();
2077 // Check, if dirPrefix is inside a valid Filemount for user:
2078 $result = $fileProcObj->checkPathAgainstMounts(PATH_site
. $dirPrefix);
2079 // If not, try to find another relative filemount and use that instead:
2081 if ($noAlternative) {
2084 // Find first web folder:
2085 $result = $fileProcObj->findFirstWebFolder();
2086 // If that succeeded, return the path to it:
2088 // Remove the "fileadmin/" prefix of input path - and append the rest to the return value:
2089 if (\TYPO3\CMS\Core\Utility\GeneralUtility
::isFirstPartOfStr($dirPrefix, $this->fileadminFolderName
. '/')) {
2090 $dirPrefix = substr($dirPrefix, strlen($this->fileadminFolderName
. '/'));
2092 return substr($fileProcObj->mounts
[$result]['path'] . $dirPrefix, strlen(PATH_site
));
2099 /**************************
2103 *************************/
2105 * Loads the header section/all of the $filename into memory
2107 * @param string $filename Filename, absolute
2108 * @param boolean $all If set, all information is loaded (header, records and files). Otherwise the default is to read only the header information
2109 * @return boolean TRUE if the operation went well
2110 * @todo Define visibility
2112 public function loadFile($filename, $all = 0) {
2113 if (@is_file
($filename)) {
2114 $fI = pathinfo($filename);
2115 if (strtolower($fI['extension']) == 'xml') {
2117 $xmlContent = \TYPO3\CMS\Core\Utility\GeneralUtility
::getUrl($filename);
2118 if (strlen($xmlContent)) {
2119 $this->dat
= \TYPO3\CMS\Core\Utility\GeneralUtility
::xml2array($xmlContent, '', TRUE);
2120 if (is_array($this->dat
)) {
2121 if ($this->dat
['_DOCUMENT_TAG'] === 'T3RecordDocument' && is_array($this->dat
['header']) && is_array($this->dat
['records'])) {
2125 $this->error('XML file did not contain proper XML for TYPO3 Import');
2128 $this->error('XML could not be parsed: ' . $this->dat
);
2131 $this->error('Error opening file: ' . $filename);
2135 if ($fd = fopen($filename, 'rb')) {
2136 $this->dat
['header'] = $this->getNextFilePart($fd, 1, 'header');
2138 $this->dat
['records'] = $this->getNextFilePart($fd, 1, 'records');
2139 $this->dat
['files'] = $this->getNextFilePart($fd, 1, 'files');
2144 $this->error('Error opening file: ' . $filename);
2149 $this->error('Filename not found: ' . $filename);
2155 * Returns the next content part form the fileresource (t3d), $fd
2157 * @param pointer $fd File pointer
2158 * @param boolean $unserialize If set, the returned content is unserialized into an array, otherwise you get the raw string
2159 * @param string $name For error messages this indicates the section of the problem.
2160 * @return string Data string
2163 * @todo Define visibility
2165 public function getNextFilePart($fd, $unserialize = 0, $name = '') {
2166 $initStrLen = 32 +
1 +
1 +
1 +
10 +
1;
2167 // Getting header data
2168 $initStr = fread($fd, $initStrLen);
2169 $initStrDat = explode(':', $initStr);
2170 if (strstr($initStrDat[0], 'Warning') == FALSE) {
2171 if (!strcmp($initStrDat[3], '')) {
2172 $datString = fread($fd, intval($initStrDat[2]));
2174 if (!strcmp(md5($datString), $initStrDat[0])) {
2175 if ($initStrDat[1]) {
2176 if ($this->compress
) {
2177 $datString = gzuncompress($datString);
2179 $this->error('Content read error: This file requires decompression, but this server does not offer gzcompress()/gzuncompress() functions.', 1);
2182 return $unserialize ?
unserialize($datString) : $datString;
2184 $this->error('MD5 check failed (' . $name . ')');
2187 $this->error('File read error: InitString had a wrong length. (' . $name . ')');
2190 $this->error('File read error: Warning message in file. (' . $initStr . fgets($fd) . ')');
2195 * Loads T3D file content into the $this->dat array
2196 * (This function can be used to test the output strings from ->compileMemoryToFileContent())
2198 * @param string $filecontent File content
2200 * @todo Define visibility
2202 public function loadContent($filecontent) {
2204 $this->dat
['header'] = $this->getNextContentPart($filecontent, $pointer, 1, 'header');
2205 $this->dat
['records'] = $this->getNextContentPart($filecontent, $pointer, 1, 'records');
2206 $this->dat
['files'] = $this->getNextContentPart($filecontent, $pointer, 1, 'files');
2211 * Returns the next content part from the $filecontent
2213 * @param string $filecontent File content string
2214 * @param integer $pointer File pointer (where to read from)
2215 * @param boolean $unserialize If set, the returned content is unserialized into an array, otherwise you get the raw string
2216 * @param string $name For error messages this indicates the section of the problem.
2217 * @return string Data string
2218 * @todo Define visibility
2220 public function getNextContentPart($filecontent, &$pointer, $unserialize = 0, $name = '') {
2221 $initStrLen = 32 +
1 +
1 +
1 +
10 +
1;
2222 // getting header data
2223 $initStr = substr($filecontent, $pointer, $initStrLen);
2224 $pointer +
= $initStrLen;
2225 $initStrDat = explode(':', $initStr);
2226 if (!strcmp($initStrDat[3], '')) {
2227 $datString = substr($filecontent, $pointer, intval($initStrDat[2]));
2228 $pointer +
= intval($initStrDat[2]) +
1;
2229 if (!strcmp(md5($datString), $initStrDat[0])) {
2230 if ($initStrDat[1]) {
2231 if ($this->compress
) {
2232 $datString = gzuncompress($datString);
2234 $this->error('Content read error: This file requires decompression, but this server does not offer gzcompress()/gzuncompress() functions.', 1);
2237 return $unserialize ?
unserialize($datString) : $datString;
2239 $this->error('MD5 check failed (' . $name . ')');
2242 $this->error('Content read error: InitString had a wrong length. (' . $name . ')');
2247 * Setting up the object based on the recently loaded ->dat array
2250 * @todo Define visibility
2252 public function loadInit() {
2253 $this->relStaticTables
= (array) $this->dat
['header']['relStaticTables'];
2254 $this->excludeMap
= (array) $this->dat
['header']['excludeMap'];
2255 $this->softrefCfg
= (array) $this->dat
['header']['softrefCfg'];
2256 $this->extensionDependencies
= (array) $this->dat
['header']['extensionDependencies'];
2257 $this->fixCharsets();
2261 * Fix charset of import memory if different from system charset
2265 * @todo Define visibility
2267 public function fixCharsets() {
2269 $importCharset = $this->dat
['header']['charset'];
2270 if ($importCharset) {
2271 if ($importCharset !== $LANG->charSet
) {
2272 $this->error('CHARSET: Converting charset of input file (' . $importCharset . ') to the system charset (' . $LANG->charSet
. ')');
2273 // Convert meta data:
2274 if (is_array($this->dat
['header']['meta'])) {
2275 $LANG->csConvObj
->convArray($this->dat
['header']['meta'], $importCharset, $LANG->charSet
);
2277 // Convert record headers:
2278 if (is_array($this->dat
['header']['records'])) {
2279 $LANG->csConvObj
->convArray($this->dat
['header']['records'], $importCharset, $LANG->charSet
);
2281 // Convert records themselves:
2282 if (is_array($this->dat
['records'])) {
2283 $LANG->csConvObj
->convArray($this->dat
['records'], $importCharset, $LANG->charSet
);
2287 $this->error('CHARSET: No charset found in import file!');
2291 /********************************************************
2293 * Visual rendering of import/export memory, $this->dat
2295 ********************************************************/
2297 * Displays an overview of the header-content.
2299 * @return string HTML content
2300 * @todo Define visibility
2302 public function displayContentOverview() {
2304 // Check extension dependencies:
2305 if (is_array($this->dat
['header']['extensionDependencies'])) {
2306 foreach ($this->dat
['header']['extensionDependencies'] as $extKey) {
2307 if (!\TYPO3\CMS\Core\Utility\ExtensionManagementUtility
::isLoaded($extKey)) {
2308 $this->error('DEPENDENCY: The extension with key "' . $extKey . '" must be installed!');
2312 // Probably this is done to save memory space?
2313 unset($this->dat
['files']);
2315 $this->remainHeader
= $this->dat
['header'];
2316 if (is_array($this->remainHeader
)) {
2317 // If there is a page tree set, show that:
2318 if (is_array($this->dat
['header']['pagetree'])) {
2319 reset($this->dat
['header']['pagetree']);
2321 $this->traversePageTree($this->dat
['header']['pagetree'], $lines);
2324 <tr class="bgColor5 tableheader">
2325 <td>' . $LANG->getLL('impexpcore_displaycon_controls', 1) . '</td>
2326 <td>' . $LANG->getLL('impexpcore_displaycon_title', 1) . '</td>
2327 <td>' . $LANG->getLL('impexpcore_displaycon_size', 1) . '</td>
2328 <td>' . $LANG->getLL('impexpcore_displaycon_message', 1) . '</td>
2329 ' . ($this->update ?
'<td>' . $LANG->getLL('impexpcore_displaycon_updateMode', 1) . '</td>' : '') . '
2330 ' . ($this->update ?
'<td>' . $LANG->getLL('impexpcore_displaycon_currentPath', 1) . '</td>' : '') . '
2331 ' . ($this->showDiff ?
'<td>' . $LANG->getLL('impexpcore_displaycon_result', 1) . '</td>' : '') . '
2333 foreach ($lines as $r) {
2335 <tr class="' . $r['class'] . '">
2336 <td>' . $this->renderControls($r) . '</td>
2337 <td nowrap="nowrap">' . $r['preCode'] . $r['title'] . '</td>
2338 <td nowrap="nowrap">' . \TYPO3\CMS\Core\Utility\GeneralUtility
::formatSize($r['size']) . '</td>
2339 <td nowrap="nowrap">' . ($r['msg'] && !$this->doesImport ?
'<span class="typo3-red">' . htmlspecialchars($r['msg']) . '</span>' : '') . '</td>
2340 ' . ($this->update ?
'<td nowrap="nowrap">' . $r['updateMode'] . '</td>' : '') . '
2341 ' . ($this->update ?
'<td nowrap="nowrap">' . $r['updatePath'] . '</td>' : '') . '
2342 ' . ($this->showDiff ?
'<td>' . $r['showDiffContent'] . '</td>' : '') . '
2346 <strong>' . $LANG->getLL('impexpcore_displaycon_insidePagetree', 1) . '</strong>
2348 <table border="0" cellpadding="0" cellspacing="1">' . implode('', $rows) . '</table>
2351 // Print remaining records that were not contained inside the page tree:
2353 if (is_array($this->remainHeader
['records'])) {
2354 if (is_array($this->remainHeader
['records']['pages'])) {
2355 $this->traversePageRecords($this->remainHeader
['records']['pages'], $lines);
2357 $this->traverseAllRecords($this->remainHeader
['records'], $lines);
2358 if (count($lines)) {
2361 <tr class="bgColor5 tableheader">
2362 <td>' . $LANG->getLL('impexpcore_displaycon_controls', 1) . '</td>
2363 <td>' . $LANG->getLL('impexpcore_displaycon_title', 1) . '</td>
2364 <td>' . $LANG->getLL('impexpcore_displaycon_size', 1) . '</td>
2365 <td>' . $LANG->getLL('impexpcore_displaycon_message', 1) . '</td>
2366 ' . ($this->update ?
'<td>' . $LANG->getLL('impexpcore_displaycon_updateMode', 1) . '</td>' : '') . '
2367 ' . ($this->update ?
'<td>' . $LANG->getLL('impexpcore_displaycon_currentPath', 1) . '</td>' : '') . '
2368 ' . ($this->showDiff ?
'<td>' . $LANG->getLL('impexpcore_displaycon_result', 1) . '</td>' : '') . '
2370 foreach ($lines as $r) {
2371 $rows[] = '<tr class="' . $r['class'] . '">
2372 <td>' . $this->renderControls($r) . '</td>
2373 <td nowrap="nowrap">' . $r['preCode'] . $r['title'] . '</td>
2374 <td nowrap="nowrap">' . \TYPO3\CMS\Core\Utility\GeneralUtility
::formatSize($r['size']) . '</td>
2375 <td nowrap="nowrap">' . ($r['msg'] && !$this->doesImport ?
'<span class="typo3-red">' . htmlspecialchars($r['msg']) . '</span>' : '') . '</td>
2376 ' . ($this->update ?
'<td nowrap="nowrap">' . $r['updateMode'] . '</td>' : '') . '
2377 ' . ($this->update ?
'<td nowrap="nowrap">' . $r['updatePath'] . '</td>' : '') . '
2378 ' . ($this->showDiff ?
'<td>' . $r['showDiffContent'] . '</td>' : '') . '
2382 <strong>' . $LANG->getLL('impexpcore_singlereco_outsidePagetree', 1) . '</strong>
2384 <table border="0" cellpadding="0" cellspacing="1">' . implode('', $rows) . '</table>';
2392 * Go through page tree for display
2394 * @param array $pT Page tree array with uid/subrow (from ->dat[header][pagetree]
2395 * @param array $lines Output lines array (is passed by reference and modified)
2396 * @param string $preCode Pre-HTML code
2398 * @todo Define visibility
2400 public function traversePageTree($pT, &$lines, $preCode = '') {
2401 foreach ($pT as $k => $v) {
2403 $this->singleRecordLines('pages', $k, $lines, $preCode);
2405 if (is_array($this->dat
['header']['pid_lookup'][$k])) {
2406 foreach ($this->dat
['header']['pid_lookup'][$k] as $t => $recUidArr) {
2407 if ($t != 'pages') {
2408 foreach ($recUidArr as $ruid => $value) {
2409 $this->singleRecordLines($t, $ruid, $lines, $preCode . ' ');
2413 unset($this->remainHeader
['pid_lookup'][$k]);
2415 // Subpages, called recursively:
2416 if (is_array($v['subrow'])) {
2417 $this->traversePageTree($v['subrow'], $lines, $preCode . ' ');
2423 * Go through remaining pages (not in tree)
2425 * @param array $pT Page tree array with uid/subrow (from ->dat[header][pagetree]
2426 * @param array $lines Output lines array (is passed by reference and modified)
2428 * @todo Define visibility
2430 public function traversePageRecords($pT, &$lines) {
2431 foreach ($pT as $k => $rHeader) {
2432 $this->singleRecordLines('pages', $k, $lines, '', 1);
2434 if (is_array($this->dat
['header']['pid_lookup'][$k])) {
2435 foreach ($this->dat
['header']['pid_lookup'][$k] as $t => $recUidArr) {
2436 if ($t != 'pages') {
2437 foreach ($recUidArr as $ruid => $value) {
2438 $this->singleRecordLines($t, $ruid, $lines, ' ');
2442 unset($this->remainHeader
['pid_lookup'][$k]);
2448 * Go through ALL records (if the pages are displayed first, those will not be amoung these!)
2450 * @param array $pT Page tree array with uid/subrow (from ->dat[header][pagetree]
2451 * @param array $lines Output lines array (is passed by reference and modified)
2453 * @todo Define visibility
2455 public function traverseAllRecords($pT, &$lines) {
2456 foreach ($pT as $t => $recUidArr) {
2457 if ($t != 'pages') {
2459 foreach ($recUidArr as $ruid => $value) {
2460 $this->singleRecordLines($t, $ruid, $lines, $preCode, 1);
2467 * Add entries for a single record
2469 * @param string $table Table name
2470 * @param integer $uid Record uid
2471 * @param array $lines Output lines array (is passed by reference and modified)
2472 * @param string $preCode Pre-HTML code
2473 * @param boolean $checkImportInPidRecord If you want import validation, you can set this so it checks if the import can take place on the specified page.
2475 * @todo Define visibility
2477 public function singleRecordLines($table, $uid, &$lines, $preCode, $checkImportInPidRecord = 0) {
2480 $record = $this->dat
['header']['records'][$table][$uid];
2481 unset($this->remainHeader
['records'][$table][$uid]);
2482 if (!is_array($record) && !($table === 'pages' && !$uid)) {
2483 $this->error('MISSING RECORD: ' . $table . ':' . $uid, 1);
2485 // Begin to create the line arrays information record, pInfo:
2487 $pInfo['ref'] = $table . ':' . $uid;
2488 // Unknown table name:
2489 if ($table === '_SOFTREF_') {
2490 $pInfo['preCode'] = $preCode;
2491 $pInfo['title'] = '<em>' . $GLOBALS['LANG']->getLL('impexpcore_singlereco_softReferencesFiles', 1) . '</em>';
2492 } elseif (!isset($GLOBALS['TCA'][$table])) {
2493 // Unknown table name:
2494 $pInfo['preCode'] = $preCode;
2495 $pInfo['msg'] = 'UNKNOWN TABLE \'' . $pInfo['ref'] . '\'';
2496 $pInfo['title'] = '<em>' . htmlspecialchars($record['title']) . '</em>';
2498 // Otherwise, set table icon and title.
2499 // Import Validation (triggered by $this->display_import_pid_record) will show messages if import is not possible of various items.
2500 if (is_array($this->display_import_pid_record
)) {
2501 if ($checkImportInPidRecord) {
2502 if (!$GLOBALS['BE_USER']->doesUserHaveAccess($this->display_import_pid_record
, ($table === 'pages' ?
8 : 16))) {
2503 $pInfo['msg'] .= '\'' . $pInfo['ref'] . '\' cannot be INSERTED on this page! ';
2505 if (!$this->checkDokType($table, $this->display_import_pid_record
['doktype']) && !$GLOBALS['TCA'][$table]['ctrl']['rootLevel']) {
2506 $pInfo['msg'] .= '\'' . $table . '\' cannot be INSERTED on this page type (change page type to \'Folder\'.) ';
2509 if (!$GLOBALS['BE_USER']->check('tables_modify', $table)) {
2510 $pInfo['msg'] .= 'You are not allowed to CREATE \'' . $table . '\' tables! ';
2512 if ($GLOBALS['TCA'][$table]['ctrl']['readOnly']) {
2513 $pInfo['msg'] .= 'TABLE \'' . $table . '\' is READ ONLY! ';
2515 if ($GLOBALS['TCA'][$table]['ctrl']['adminOnly'] && !$GLOBALS['BE_USER']->isAdmin()) {
2516 $pInfo['msg'] .= 'TABLE \'' . $table . '\' is ADMIN ONLY! ';
2518 if ($GLOBALS['TCA'][$table]['ctrl']['is_static']) {
2519 $pInfo['msg'] .= 'TABLE \'' . $table . '\' is a STATIC TABLE! ';
2521 if ($GLOBALS['TCA'][$table]['ctrl']['rootLevel']) {
2522 $pInfo['msg'] .= 'TABLE \'' . $table . '\' will be inserted on ROOT LEVEL! ';
2524 $diffInverse = FALSE;
2525 if ($this->update
) {
2526 // In case of update-PREVIEW we swap the diff-sources.
2527 $diffInverse = TRUE;
2528 $recInf = $this->doesRecordExist($table, $uid, $this->showDiff ?
'*' : '');
2529 $pInfo['updatePath'] = $recInf ?
htmlspecialchars($this->getRecordPath($recInf['pid'])) : '<strong>NEW!</strong>';
2531 $optValues = array();
2532 $optValues[] = $recInf ?
$LANG->getLL('impexpcore_singlereco_update') : $LANG->getLL('impexpcore_singlereco_insert');
2534 $optValues['as_new'] = $LANG->getLL('impexpcore_singlereco_importAsNew');
2537 if (!$this->global_ignore_pid
) {
2538 $optValues['ignore_pid'] = $LANG->getLL('impexpcore_singlereco_ignorePid');
2540 $optValues['respect_pid'] = $LANG->getLL('impexpcore_singlereco_respectPid');
2543 if (!$recInf && $GLOBALS['BE_USER']->isAdmin()) {
2544 $optValues['force_uid'] = sprintf($LANG->getLL('impexpcore_singlereco_forceUidSAdmin'), $uid);
2546 $optValues['exclude'] = $LANG->getLL('impexpcore_singlereco_exclude');
2547 $pInfo['updateMode'] = $this->renderSelectBox('tx_impexp[import_mode][' . $table . ':' . $uid . ']', $this->import_mode
[$table . ':' . $uid], $optValues);
2550 if ($this->showDiff
) {
2551 // For IMPORTS, get new id:
2552 if ($newUid = $this->import_mapId
[$table][$uid]) {
2553 $diffInverse = FALSE;
2554 $recInf = $this->doesRecordExist($table, $newUid, '*');
2555 \TYPO3\CMS\Backend\Utility\BackendUtility
::workspaceOL($table, $recInf);
2557 if (is_array($recInf)) {
2558 $pInfo['showDiffContent'] = $this->compareRecords($recInf, $this->dat
['records'][$table . ':' . $uid]['data'], $table, $diffInverse);
2562 $pInfo['preCode'] = $preCode . \t3lib_iconworks
::getSpriteIconForRecord($table, (array) $this->dat
['records'][($table . ':' . $uid)]['data'], array('title' => htmlspecialchars(($table . ':' . $uid))));
2563 $pInfo['title'] = htmlspecialchars($record['title']);
2565 if ($table === 'pages') {
2566 $viewID = $this->mode
=== 'export' ?
$uid : ($this->doesImport ?
$this->import_mapId
['pages'][$uid] : 0);
2568 $pInfo['title'] = '<a href="#" onclick="' . htmlspecialchars(\TYPO3\CMS\Backend\Utility\BackendUtility
::viewOnClick($viewID, $GLOBALS['BACK_PATH'])) . 'return false;">' . $pInfo['title'] . '</a>';
2572 $pInfo['class'] = $table == 'pages' ?
'bgColor4-20' : 'bgColor4';
2573 $pInfo['type'] = 'record';
2574 $pInfo['size'] = $record['size'];
2577 if (is_array($record['filerefs'])) {
2578 $this->addFiles($record['filerefs'], $lines, $preCode);
2581 if (is_array($record['rels'])) {
2582 $this->addRelations($record['rels'], $lines, $preCode);
2585 if (count($record['softrefs'])) {
2586 $preCode_A = $preCode . ' ';
2587 $preCode_B = $preCode . ' ';
2588 foreach ($record['softrefs'] as $info) {
2590 $pInfo['preCode'] = $preCode_A . \TYPO3\CMS\Backend\Utility\IconUtility
::getSpriteIcon('status-status-reference-soft');
2591 $pInfo['title'] = '<em>' . $info['field'] . ', "' . $info['spKey'] . '" </em>: <span title="' . htmlspecialchars($info['matchString']) . '">' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility
::fixed_lgd_cs($info['matchString'], 60)) . '</span>';
2592 if ($info['subst']['type']) {
2593 if (strlen($info['subst']['title'])) {
2594 $pInfo['title'] .= '<br/>' . $preCode_B . '<strong>' . $LANG->getLL('impexpcore_singlereco_title', 1) . '</strong> ' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility
::fixed_lgd_cs($info['subst']['title'], 60));
2596 if (strlen($info['subst']['description'])) {
2597 $pInfo['title'] .= '<br/>' . $preCode_B . '<strong>' . $LANG->getLL('impexpcore_singlereco_descr', 1) . '</strong> ' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility
::fixed_lgd_cs($info['subst']['description'], 60));
2599 $pInfo['title'] .= '<br/>' . $preCode_B . ($info['subst']['type'] == 'file' ?
$LANG->getLL('impexpcore_singlereco_filename', 1) . ' <strong>' . $info['subst']['relFileName'] . '</strong>' : '') . ($info['subst']['type'] == 'string' ?
$LANG->getLL('impexpcore_singlereco_value', 1) . ' <strong>' . $info['subst']['tokenValue'] . '</strong>' : '') . ($info['subst']['type'] == 'db' ?
$LANG->getLL('impexpcore_softrefsel_record', 1) . ' <strong>' . $info['subst']['recordRef'] . '</strong>' : '');
2601 $pInfo['ref'] = 'SOFTREF';
2602 $pInfo['size'] = '';
2603 $pInfo['class'] = 'bgColor3';
2604 $pInfo['type'] = 'softref';
2605 $pInfo['_softRefInfo'] = $info;
2606 $pInfo['type'] = 'softref';
2607 if ($info['error'] && !\TYPO3\CMS\Core\Utility\GeneralUtility
::inList('editable,exclude', $this->softrefCfg
[$info['subst']['tokenID']]['mode'])) {
2608 $pInfo['msg'] .= $info['error'];
2612 if ($info['subst']['type'] == 'db') {
2613 list($tempTable, $tempUid) = explode(':', $info['subst']['recordRef']);
2614 $this->addRelations(array(array('table' => $tempTable, 'id' => $tempUid, 'tokenID' => $info['subst']['tokenID'])), $lines, $preCode_B, array(), '');
2617 if ($info['subst']['type'] == 'file') {
2618 $this->addFiles(array($info['file_ID']), $lines, $preCode_B, '', $info['subst']['tokenID']);
2625 * Add DB relations entries for a record's rels-array
2627 * @param array $rels Array of relations
2628 * @param array $lines Output lines array (is passed by reference and modified)
2629 * @param string $preCode Pre-HTML code
2630 * @param array $recurCheck Recursivity check stack
2631 * @param string $htmlColorClass Alternative HTML color class to use.
2634 * @see singleRecordLines()
2635 * @todo Define visibility
2637 public function addRelations($rels, &$lines, $preCode, $recurCheck = array(), $htmlColorClass = '') {
2638 foreach ($rels as $dat) {
2639 $table = $dat['table'];
2643 $staticFixed = FALSE;
2644 $pInfo['ref'] = $table . ':' . $uid;
2645 if (!in_array($pInfo['ref'], $recurCheck)) {
2647 $record = $this->dat
['header']['records'][$table][$uid];
2648 if (!is_array($record)) {
2649 if ($this->isTableStatic($table) ||
$this->isExcluded($table, $uid) ||
$dat['tokenID'] && !$this->includeSoftref($dat['tokenID'])) {
2650 $pInfo['title'] = htmlspecialchars('STATIC: ' . $pInfo['ref']);
2651 $Iprepend = '_static';
2652 $staticFixed = TRUE;
2654 $doesRE = $this->doesRecordExist($table, $uid);
2655 $lostPath = $this->getRecordPath($table === 'pages' ?
$doesRE['uid'] : $doesRE['pid']);
2656 $pInfo['title'] = htmlspecialchars($pInfo['ref']);
2657 $pInfo['title'] = '<span title="' . htmlspecialchars($lostPath) . '">' . $pInfo['title'] . '</span>';
2658 $pInfo['msg'] = 'LOST RELATION' . (!$doesRE ?
' (Record not found!)' : ' (Path: ' . $lostPath . ')');
2659 $Iprepend = '_lost';
2662 $pInfo['title'] = htmlspecialchars($record['title']);
2663 $pInfo['title'] = '<span title="' . htmlspecialchars($this->getRecordPath(($table === 'pages' ?
$record['uid'] : $record['pid']))) . '">' . $pInfo['title'] . '</span>';
2666 // 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