389a0f1c2a4d86ff1c95a250e6ea6ad044a33496
[Packages/TYPO3.CMS.git] / typo3 / sysext / impexp / class.tx_impexp.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2009 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * T3D file Import/Export library (TYPO3 Record Document)
29 *
30 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
31 */
32 /**
33 * [CLASS/FUNCTION INDEX of SCRIPT]
34 *
35 *
36 *
37 * 198: class tx_impexp
38 *
39 * SECTION: Initialize
40 * 261: function init($dontCompress=0,$mode='')
41 *
42 * SECTION: Export / Init + Meta Data
43 * 292: function setHeaderBasics()
44 * 316: function setCharset($charset)
45 * 331: function setMetaData($title,$description,$notes,$packager_username,$packager_name,$packager_email)
46 * 351: function addThumbnail($imgFilepath)
47 *
48 * SECTION: Export / Init Page tree
49 * 389: function setPageTree($idH)
50 * 402: function unsetExcludedSections($idH)
51 * 424: function flatInversePageTree($idH,$a=array())
52 * 447: function flatInversePageTree_pid($idH,$a=array(),$pid=-1)
53 *
54 * SECTION: Export
55 * 486: function export_addRecord($table,$row,$relationLevel=0)
56 * 544: function export_addDBRelations($relationLevel=0)
57 * 648: function export_addDBRelations_registerRelation($fI, &$addR, $tokenID='')
58 * 672: function export_addFilesFromRelations()
59 * 773: function export_addFile($fI, $recordRef='', $fieldname='')
60 * 898: function flatDBrels($dbrels)
61 * 924: function flatSoftRefs($dbrels)
62 *
63 * SECTION: File Output
64 * 988: function compileMemoryToFileContent($type='')
65 * 1014: function createXML()
66 * 1106: function doOutputCompress()
67 * 1117: function addFilePart($data, $compress=FALSE)
68 *
69 * SECTION: Import
70 * 1150: function importData($pid)
71 * 1191: function writeRecords_pages($pid)
72 * 1246: function writeRecords_pages_order($pid)
73 * 1284: function writeRecords_records($pid)
74 * 1334: function writeRecords_records_order($mainPid)
75 * 1383: function addSingle($table,$uid,$pid)
76 * 1457: function addToMapId($substNEWwithIDs)
77 * 1477: function getNewTCE()
78 * 1491: function unlinkTempFiles()
79 *
80 * SECTION: Import / Relations setting
81 * 1529: function setRelations()
82 * 1584: function setRelations_db($itemArray)
83 * 1611: function import_addFileNameToBeCopied($fI)
84 * 1634: function setFlexFormRelations()
85 * 1718: function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path)
86 *
87 * SECTION: Import / Soft References
88 * 1760: function processSoftReferences()
89 * 1851: function processSoftReferences_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path)
90 * 1890: function processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid)
91 * 1954: function processSoftReferences_saveFile($relFileName, $cfg, $table, $uid)
92 * 2015: function processSoftReferences_saveFile_createRelFile($origDirPrefix, $fileName, $fileID, $table, $uid)
93 * 2104: function writeFileVerify($fileName, $fileID, $bypassMountCheck=FALSE)
94 * 2131: function checkOrCreateDir($dirPrefix)
95 * 2164: function verifyFolderAccess($dirPrefix, $noAlternative=FALSE)
96 *
97 * SECTION: File Input
98 * 2214: function loadFile($filename,$all=0)
99 * 2257: function getNextFilePart($fd,$unserialize=0,$name='')
100 * 2284: function loadContent($filecontent)
101 * 2302: function getNextContentPart($filecontent,&$pointer,$unserialize=0,$name='')
102 * 2327: function loadInit()
103 * 2343: function fixCharsets()
104 *
105 * SECTION: Visual rendering of import/export memory, $this->dat
106 * 2398: function displayContentOverview()
107 * 2506: function traversePageTree($pT,&$lines,$preCode='')
108 * 2541: function traversePageRecords($pT,&$lines)
109 * 2568: function traverseAllRecords($pT,&$lines)
110 * 2590: function singleRecordLines($table,$uid,&$lines,$preCode,$checkImportInPidRecord=0)
111 * 2748: function addRelations($rels,&$lines,$preCode,$recurCheck=array(),$htmlColorClass='')
112 * 2813: function addFiles($rels,&$lines,$preCode,$htmlColorClass='',$tokenID='')
113 * 2931: function checkDokType($checkTable,$doktype)
114 * 2947: function renderControls($r)
115 * 2975: function softrefSelector($cfg)
116 *
117 * SECTION: Helper functions of kinds
118 * 3051: function isTableStatic($table)
119 * 3065: function inclRelation($table)
120 * 3080: function isExcluded($table,$uid)
121 * 3092: function includeSoftref($tokenID)
122 * 3102: function checkPID($pid)
123 * 3119: function dontIgnorePid($table, $uid)
124 * 3132: function doesRecordExist($table,$uid,$fields='')
125 * 3142: function getRecordPath($pid)
126 * 3159: function renderSelectBox($prefix,$value,$optValues)
127 * 3183: function compareRecords($databaseRecord, $importRecord, $table, $inverseDiff=FALSE)
128 * 3250: function getRTEoriginalFilename($string)
129 * 3267: function &getFileProcObj()
130 *
131 * SECTION: Error handling
132 * 3299: function error($msg)
133 * 3308: function printErrorLog()
134 *
135 * TOTAL FUNCTIONS: 72
136 * (This index is automatically created/updated by the extension "extdeveval")
137 *
138 */
139 /**
140 * EXAMPLE for using the impexp-class for exporting stuff:
141 *
142 * // Create and initialize:
143 * $this->export = t3lib_div::makeInstance('tx_impexp');
144 * $this->export->init();
145 * // Set which tables relations we will allow:
146 * $this->export->relOnlyTables[]="tt_news"; // exclusively includes. See comment in the class
147 *
148 * // Adding records:
149 * $this->export->export_addRecord("pages",$this->pageinfo);
150 * $this->export->export_addRecord("pages",t3lib_BEfunc::getRecord("pages",38));
151 * $this->export->export_addRecord("pages",t3lib_BEfunc::getRecord("pages",39));
152 * $this->export->export_addRecord("tt_content",t3lib_BEfunc::getRecord("tt_content",12));
153 * $this->export->export_addRecord("tt_content",t3lib_BEfunc::getRecord("tt_content",74));
154 * $this->export->export_addRecord("sys_template",t3lib_BEfunc::getRecord("sys_template",20));
155 *
156 * // Adding all the relations (recursively in 5 levels so relations has THEIR relations registered as well)
157 * for($a=0;$a<5;$a++) {
158 * $addR = $this->export->export_addDBRelations($a);
159 * if (!count($addR)) break;
160 * }
161 *
162 * // Finally load all the files.
163 * $this->export->export_addFilesFromRelations(); // MUST be after the DBrelations are set so that file from ALL added records are included!
164 *
165 * // Now the internal DAT array is ready to export:
166 * #debug($this->export->dat);
167 *
168 * // Write export
169 * $out = $this->export->compileMemoryToFileContent();
170 * #t3lib_div::writeFile(PATH_site."fileadmin/relations.t3d",$out);
171 * #debug(strlen($out));
172 */
173
174 @ini_set('max_execution_time',600);
175 @ini_set('memory_limit','256m');
176
177
178
179
180
181
182
183 /**
184 * T3D file Import/Export library (TYPO3 Record Document)
185 *
186 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
187 * @package TYPO3
188 * @subpackage tx_impexp
189 */
190 class tx_impexp {
191
192 // Configuration, general
193 var $showStaticRelations = FALSE; // If set, static relations (not exported) will be shown in overview as well
194 var $fileadminFolderName = 'fileadmin'; // Name of the "fileadmin" folder where files for export/import should be located
195
196 var $mode = ''; // Whether "import" or "export" mode of object. Set through init() function
197 var $update = FALSE; // Updates all records that has same UID instead of creating new!
198 var $doesImport = FALSE; // Is set by importData() when an import has been done.
199
200 // Configuration, import
201 var $display_import_pid_record = ''; // If set to a page-record, then the preview display of the content will expect this page-record to be the target for the import and accordingly display validation information. This triggers the visual view of the import/export memory to validate if import is possible
202 var $suggestedInsertUids = array(); // 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.
203 var $import_mode = array(); // Setting import modes during update state: as_new, exclude, force_uid
204 var $global_ignore_pid = FALSE; // If set, PID correct is ignored globally
205 var $force_all_UIDS = FALSE; // If set, all UID values are forced! (update or import)
206 var $showDiff = FALSE; // If set, a diff-view column is added to the overview.
207 var $allowPHPScripts = FALSE; // If set, and if the user is admin, allow the writing of PHP scripts to fileadmin/ area.
208 var $enableLogging = FALSE; // Disable logging when importing
209 var $softrefInputValues = array(); // Array of values to substitute in editable softreferences.
210 var $fileIDMap = array(); // Mapping between the fileID from import memory and the final filenames they are written to.
211
212 // Configuration, export
213 var $maxFileSize = 1000000; // 1MB max file size
214 var $maxRecordSize = 1000000; // 1MB max record size
215 var $maxExportSize = 10000000; // 10MB max export size
216 var $relOnlyTables = array(); // add table names here which are THE ONLY ones which will be included into export if found as relations. '_ALL' will allow all tables.
217 var $relStaticTables = array(); // add tables names here which should not be exported with the file. (Where relations should be mapped to same UIDs in target system).
218 var $excludeMap = array(); // Exclude map. Keys are table:uid pairs and if set, records are not added to the export.
219 var $softrefCfg = array(); // Soft Reference Token ID modes.
220 var $extensionDependencies = array(); // Listing extension dependencies.
221 var $dontCompress = 0; // Set by user: If set, compression in t3d files is disabled
222 var $includeExtFileResources = 0; // Boolean, if set, HTML file resources are included.
223 var $extFileResourceExtensions = 'html,htm,css'; // Files with external media (HTML/css style references inside)
224
225 // Internal, dynamic:
226 var $import_mapId = array(); // After records are written this array is filled with [table][original_uid] = [new_uid]
227 var $import_newId = array(); // 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
228 var $import_newId_pids = array(); // Page id map for page tree (import)
229 var $import_data = array(); // Internal data accumulation for writing records during import
230 var $errorLog = array(); // Error log.
231 var $cache_getRecordPath = array(); // Cache for record paths
232 var $checkPID_cache = array(); // Cache of checkPID values.
233
234 var $compress = 0; // Set internally if the gzcompress function exists
235 var $dat = array(); // Internal import/export memory
236
237 /**
238 * File processing object
239 *
240 * @var t3lib_extFileFunctions
241 */
242 var $fileProcObj = '';
243
244
245
246 /**************************
247 *
248 * Initialize
249 *
250 *************************/
251
252 /**
253 * Init the object, both import and export
254 *
255 * @param boolean If set, compression in t3d files is disabled
256 * @param string Mode of usage, either "import" or "export"
257 * @return void
258 */
259 function init($dontCompress=0,$mode='') {
260 $this->compress = function_exists('gzcompress');
261 $this->dontCompress = $dontCompress;
262
263 $this->mode = $mode;
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279 /**************************
280 *
281 * Export / Init + Meta Data
282 *
283 *************************/
284
285 /**
286 * Set header basics
287 *
288 * @return void
289 */
290 function setHeaderBasics() {
291
292 // Initializing:
293 if (is_array($this->softrefCfg)) {
294 foreach($this->softrefCfg as $key => $value) {
295 if (!strlen($value['mode'])) unset($this->softrefCfg[$key]);
296 }
297 }
298
299 // Setting in header memory:
300 $this->dat['header']['XMLversion'] = '1.0'; // Version of file format
301 $this->dat['header']['meta'] = array(); // Initialize meta data array (to put it in top of file)
302 $this->dat['header']['relStaticTables'] = $this->relStaticTables; // Add list of tables to consider static
303 $this->dat['header']['excludeMap'] = $this->excludeMap; // The list of excluded records
304 $this->dat['header']['softrefCfg'] = $this->softrefCfg; // Soft Reference mode for elements
305 $this->dat['header']['extensionDependencies'] = $this->extensionDependencies; // List of extensions the import depends on.
306 }
307
308 /**
309 * Set charset
310 *
311 * @param string Charset for the content in the export. During import the character set will be converted if the target system uses another charset.
312 * @return void
313 */
314 function setCharset($charset) {
315 $this->dat['header']['charset'] = $charset;
316 }
317
318 /**
319 * Sets meta data
320 *
321 * @param string Title of the export
322 * @param string Description of the export
323 * @param string Notes about the contents
324 * @param string Backend Username of the packager (the guy making the export)
325 * @param string Real name of the packager
326 * @param string Email of the packager
327 * @return void
328 */
329 function setMetaData($title,$description,$notes,$packager_username,$packager_name,$packager_email) {
330 $this->dat['header']['meta'] = array(
331 'title' => $title,
332 'description' => $description,
333 'notes' => $notes,
334 'packager_username' => $packager_username,
335 'packager_name' => $packager_name,
336 'packager_email' => $packager_email,
337 'TYPO3_version' => TYPO3_version,
338 # 'loaded_extensions' => $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList'],
339 'created' => strftime('%A %e. %B %Y', time())
340 );
341 }
342
343 /**
344 * Sets a thumbnail image to the exported file
345 *
346 * @param string Filename reference, gif, jpg, png. Absolute path.
347 * @return void
348 */
349 function addThumbnail($imgFilepath) {
350 if (@is_file($imgFilepath)) {
351 $imgInfo = @getimagesize($imgFilepath);
352 if (is_array($imgInfo)) {
353 $fileContent = t3lib_div::getUrl($imgFilepath);
354 $this->dat['header']['thumbnail'] = array(
355 'imgInfo' => $imgInfo,
356 'content' => $fileContent,
357 'filesize' => strlen($fileContent),
358 'filemtime' => filemtime($imgFilepath),
359 'filename' => basename($imgFilepath)
360 );
361 }
362 }
363 }
364
365
366
367
368
369
370
371
372
373
374
375 /**************************
376 *
377 * Export / Init Page tree
378 *
379 *************************/
380
381 /**
382 * Sets the page-tree array in the export header and returns the array in a flattened version
383 *
384 * @param array Hierarchy of ids, the page tree: array([uid] => array("uid" => [uid], "subrow" => array(.....)), [uid] => ....)
385 * @return array The hierarchical page tree converted to a one-dimensional list of pages
386 */
387 function setPageTree($idH) {
388 $this->dat['header']['pagetree'] = $this->unsetExcludedSections($idH);
389 return $this->flatInversePageTree($this->dat['header']['pagetree']);
390 }
391
392 /**
393 * Removes entries in the page tree which are found in ->excludeMap[]
394 *
395 * @param array Page uid hierarchy
396 * @return array Modified input array
397 * @access private
398 * @see setPageTree()
399 */
400 function unsetExcludedSections($idH) {
401 if (is_array($idH)) {
402 reset($idH);
403 while(list($k,$v) = each($idH)) {
404 if ($this->excludeMap['pages:'.$idH[$k]['uid']]) {
405 unset($idH[$k]);
406 } elseif (is_array($idH[$k]['subrow'])) {
407 $idH[$k]['subrow'] = $this->unsetExcludedSections($idH[$k]['subrow']);
408 }
409 }
410 }
411 return $idH;
412 }
413
414 /**
415 * Recursively flattening the idH array (for setPageTree() function)
416 *
417 * @param array Page uid hierarchy
418 * @param array Accumulation array of pages (internal, don't set from outside)
419 * @return array Array with uid-uid pairs for all pages in the page tree.
420 * @see flatInversePageTree_pid()
421 */
422 function flatInversePageTree($idH,$a=array()) {
423 if (is_array($idH)) {
424 $idH = array_reverse($idH);
425 reset($idH);
426 while(list($k,$v) = each($idH)) {
427 $a[$v['uid']] = $v['uid'];
428 if (is_array($v['subrow'])) {
429 $a = $this->flatInversePageTree($v['subrow'],$a);
430 }
431 }
432 }
433 return $a;
434 }
435
436 /**
437 * Recursively flattening the idH array (for setPageTree() function), setting PIDs as values
438 *
439 * @param array Page uid hierarchy
440 * @param array Accumulation array of pages (internal, don't set from outside)
441 * @param integer PID value (internal)
442 * @return array Array with uid-pid pairs for all pages in the page tree.
443 * @see flatInversePageTree()
444 */
445 function flatInversePageTree_pid($idH,$a=array(),$pid=-1) {
446 if (is_array($idH)) {
447 $idH = array_reverse($idH);
448 reset($idH);
449 while(list($k,$v) = each($idH)) {
450 $a[$v['uid']] = $pid;
451 if (is_array($v['subrow'])) {
452 $a = $this->flatInversePageTree_pid($v['subrow'],$a,$v['uid']);
453 }
454 }
455 }
456 return $a;
457 }
458
459
460
461
462
463
464
465
466
467
468
469 /**************************
470 *
471 * Export
472 *
473 *************************/
474
475 /**
476 * Adds the record $row from $table.
477 * No checking for relations done here. Pure data.
478 *
479 * @param string Table name
480 * @param array Record row.
481 * @param integer (Internal) if the record is added as a relation, this is set to the "level" it was on.
482 * @return void
483 */
484 function export_addRecord($table,$row,$relationLevel=0) {
485
486 t3lib_BEfunc::workspaceOL($table,$row);
487
488 if (strcmp($table,'') && is_array($row) && $row['uid']>0 && !$this->excludeMap[$table.':'.$row['uid']]) {
489 if ($this->checkPID($table==='pages' ? $row['uid'] : $row['pid'])) {
490 if (!isset($this->dat['records'][$table.':'.$row['uid']])) {
491
492 // Prepare header info:
493 $headerInfo = array();
494 $headerInfo['uid'] = $row['uid'];
495 $headerInfo['pid'] = $row['pid'];
496 $headerInfo['title'] = t3lib_div::fixed_lgd_cs(t3lib_BEfunc::getRecordTitle($table,$row),40);
497 $headerInfo['size'] = strlen(serialize($row));
498 if ($relationLevel) {
499 $headerInfo['relationLevel'] = $relationLevel;
500 }
501
502 // If record content is not too large in size, set the header content and add the rest:
503 if ($headerInfo['size']<$this->maxRecordSize) {
504
505 // Set the header summary:
506 $this->dat['header']['records'][$table][$row['uid']] = $headerInfo;
507
508 // Create entry in the PID lookup:
509 $this->dat['header']['pid_lookup'][$row['pid']][$table][$row['uid']]=1;
510
511 // Initialize reference index object:
512 $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
513 $refIndexObj->WSOL = TRUE; // Yes to workspace overlays for exporting....
514
515 // Data:
516 $this->dat['records'][$table.':'.$row['uid']] = array();
517 $this->dat['records'][$table.':'.$row['uid']]['data'] = $row;
518 $this->dat['records'][$table.':'.$row['uid']]['rels'] = $refIndexObj->getRelations($table,$row);
519 $this->errorLog = array_merge($this->errorLog,$refIndexObj->errorLog); // Merge error logs.
520
521 // Add information about the relations in the record in the header:
522 $this->dat['header']['records'][$table][$row['uid']]['rels'] = $this->flatDBrels($this->dat['records'][$table.':'.$row['uid']]['rels']);
523
524 // Add information about the softrefs to header:
525 $this->dat['header']['records'][$table][$row['uid']]['softrefs'] = $this->flatSoftRefs($this->dat['records'][$table.':'.$row['uid']]['rels']);
526
527 } else $this->error('Record '.$table.':'.$row['uid'].' was larger than maxRecordSize ('.t3lib_div::formatSize($this->maxRecordSize).')');
528 } else $this->error('Record '.$table.':'.$row['uid'].' already added.');
529 } else $this->error('Record '.$table.':'.$row['uid'].' was outside your DB mounts!');
530 }
531 }
532
533 /**
534 * This analyses the existing added records, finds all database relations to records and adds these records to the export file.
535 * 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...
536 * Call this BEFORE the ext_addFilesFromRelations (so files from added relations are also included of course)
537 *
538 * @param integer Recursion level
539 * @return array overview of relations found and added: Keys [table]:[uid], values array with table and id
540 * @see export_addFilesFromRelations()
541 */
542 function export_addDBRelations($relationLevel=0) {
543 global $TCA;
544
545 // Initialize:
546 $addR = array();
547
548 // Traverse all "rels" registered for "records"
549 if (is_array($this->dat['records'])) {
550 reset($this->dat['records']);
551 while(list($k) = each($this->dat['records'])) {
552 if (is_array($this->dat['records'][$k])) {
553 reset($this->dat['records'][$k]['rels']);
554 while(list($fieldname,$vR) = each($this->dat['records'][$k]['rels'])) {
555 #debug($vR);
556 // For all DB types of relations:
557 if ($vR['type']=='db') {
558 foreach($vR['itemArray'] as $fI) {
559 $this->export_addDBRelations_registerRelation($fI, $addR);
560 }
561 }
562
563 // For all flex/db types of relations:
564 if ($vR['type']=='flex') {
565 // DB relations in flex form fields:
566 if (is_array($vR['flexFormRels']['db'])) {
567 foreach($vR['flexFormRels']['db'] as $subList) {
568 foreach($subList as $fI) {
569 $this->export_addDBRelations_registerRelation($fI, $addR);
570 }
571 }
572 }
573 // DB oriented soft references in flex form fields:
574 if (is_array($vR['flexFormRels']['softrefs'])) {
575 foreach($vR['flexFormRels']['softrefs'] as $subList) {
576 foreach($subList['keys'] as $spKey => $elements) {
577 foreach($elements as $el) {
578 if ($el['subst']['type'] === 'db' && $this->includeSoftref($el['subst']['tokenID'])) {
579 list($tempTable, $tempUid) = explode(':', $el['subst']['recordRef']);
580 $fI = array(
581 'table' => $tempTable,
582 'id' => $tempUid
583 );
584 $this->export_addDBRelations_registerRelation($fI, $addR, $el['subst']['tokenID']);
585 }
586 }
587 }
588 }
589 }
590 }
591
592 // In any case, if there are soft refs:
593 if (is_array($vR['softrefs']['keys'])) {
594 foreach($vR['softrefs']['keys'] as $spKey => $elements) {
595 foreach($elements as $el) {
596 if ($el['subst']['type'] === 'db' && $this->includeSoftref($el['subst']['tokenID'])) {
597 list($tempTable, $tempUid) = explode(':', $el['subst']['recordRef']);
598 $fI = array(
599 'table' => $tempTable,
600 'id' => $tempUid
601 );
602 $this->export_addDBRelations_registerRelation($fI, $addR, $el['subst']['tokenID']);
603 }
604 }
605 }
606 }
607 }
608 }
609 }
610 } else $this->error('There were no records available.');
611
612 // Now, if there were new records to add, do so:
613 if (count($addR)) {
614 foreach($addR as $fI) {
615
616 // Get and set record:
617 $row = t3lib_BEfunc::getRecord($fI['table'],$fI['id']);
618 if (is_array($row)) {
619 $this->export_addRecord($fI['table'],$row,$relationLevel+1);
620 }
621
622 // Set status message
623 if ($fI['id']>0) { // Relation pointers always larger than zero except certain "select" types with negative values pointing to uids - but that is not supported here.
624 $rId = $fI['table'].':'.$fI['id'];
625 if (!isset($this->dat['records'][$rId])) {
626 $this->dat['records'][$rId] = 'NOT_FOUND';
627 $this->error('Relation record '.$rId.' was not found!');
628 }
629 }
630 }
631 }
632
633 // Return overview of relations found and added
634 return $addR;
635 }
636
637 /**
638 * Helper function for export_addDBRelations()
639 *
640 * @param array Array with table/id keys to add
641 * @param array Add array, passed by reference to be modified
642 * @param string Softref Token ID, if applicable.
643 * @return void
644 * @see export_addDBRelations()
645 */
646 function export_addDBRelations_registerRelation($fI, &$addR, $tokenID='') {
647 global $TCA;
648
649 $rId = $fI['table'].':'.$fI['id'];
650 if (isset($TCA[$fI['table']])
651 && !$this->isTableStatic($fI['table'])
652 && !$this->isExcluded($fI['table'],$fI['id'])
653 && (!$tokenID || $this->includeSoftref($tokenID))
654 && $this->inclRelation($fI['table'])
655 ) {
656 if (!isset($this->dat['records'][$rId])) {
657 // Set this record to be included since it is not already.
658 $addR[$rId] = $fI;
659 }
660 }
661 }
662
663 /**
664 * This adds all files in relations.
665 * Call this method AFTER adding all records including relations.
666 *
667 * @return void
668 * @see export_addDBRelations()
669 */
670 function export_addFilesFromRelations() {
671
672 // Traverse all "rels" registered for "records"
673 if (is_array($this->dat['records'])) {
674 reset($this->dat['records']);
675 while(list($k)=each($this->dat['records'])) {
676 if (is_array($this->dat['records'][$k]['rels'])) {
677 reset($this->dat['records'][$k]['rels']);
678 while(list($fieldname,$vR)=each($this->dat['records'][$k]['rels'])) {
679
680 // For all file type relations:
681 if ($vR['type']=='file') {
682 foreach($vR['newValueFiles'] as $key => $fI) {
683 $this->export_addFile($fI, $k, $fieldname);
684 // Remove the absolute reference to the file so it doesn't expose absolute paths from source server:
685 unset($this->dat['records'][$k]['rels'][$fieldname]['newValueFiles'][$key]['ID_absFile']);
686 }
687 }
688
689 // For all flex type relations:
690 if ($vR['type']=='flex') {
691 if (is_array($vR['flexFormRels']['file'])) {
692 foreach($vR['flexFormRels']['file'] as $key => $subList) {
693 foreach($subList as $subKey => $fI) {
694 $this->export_addFile($fI, $k, $fieldname);
695 // Remove the absolute reference to the file so it doesn't expose absolute paths from source server:
696 unset($this->dat['records'][$k]['rels'][$fieldname]['flexFormRels']['file'][$key][$subKey]['ID_absFile']);
697 }
698 }
699 }
700
701 // DB oriented soft references in flex form fields:
702 if (is_array($vR['flexFormRels']['softrefs'])) {
703 foreach($vR['flexFormRels']['softrefs'] as $key => $subList) {
704 foreach($subList['keys'] as $spKey => $elements) {
705 foreach($elements as $subKey => $el) {
706 if ($el['subst']['type'] === 'file' && $this->includeSoftref($el['subst']['tokenID'])) {
707
708 // Create abs path and ID for file:
709 $ID_absFile = t3lib_div::getFileAbsFileName(PATH_site.$el['subst']['relFileName']);
710 $ID = md5($ID_absFile);
711
712 if ($ID_absFile) {
713 if (!$this->dat['files'][$ID]) {
714 $fI = array(
715 'filename' => basename($ID_absFile),
716 'ID_absFile' => $ID_absFile,
717 'ID' => $ID,
718 'relFileName' => $el['subst']['relFileName']
719 );
720 $this->export_addFile($fI, '_SOFTREF_');
721 }
722 $this->dat['records'][$k]['rels'][$fieldname]['flexFormRels']['softrefs'][$key]['keys'][$spKey][$subKey]['file_ID'] = $ID;
723 }
724 }
725 }
726 }
727 }
728 }
729 }
730
731 // In any case, if there are soft refs:
732 if (is_array($vR['softrefs']['keys'])) {
733 foreach($vR['softrefs']['keys'] as $spKey => $elements) {
734 foreach($elements as $subKey => $el) {
735 if ($el['subst']['type'] === 'file' && $this->includeSoftref($el['subst']['tokenID'])) {
736
737 // Create abs path and ID for file:
738 $ID_absFile = t3lib_div::getFileAbsFileName(PATH_site.$el['subst']['relFileName']);
739 $ID = md5($ID_absFile);
740
741 if ($ID_absFile) {
742 if (!$this->dat['files'][$ID]) {
743 $fI = array(
744 'filename' => basename($ID_absFile),
745 'ID_absFile' => $ID_absFile,
746 'ID' => $ID,
747 'relFileName' => $el['subst']['relFileName']
748 );
749 $this->export_addFile($fI, '_SOFTREF_');
750 }
751 $this->dat['records'][$k]['rels'][$fieldname]['softrefs']['keys'][$spKey][$subKey]['file_ID'] = $ID;
752 }
753 }
754 }
755 }
756 }
757 }
758 }
759 }
760 } else $this->error('There were no records available.');
761 }
762
763 /**
764 * Adds a files content to the export memory
765 *
766 * @param array 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!)
767 * @param string If the file is related to a record, this is the id on the form [table]:[id]. Information purposes only.
768 * @param string If the file is related to a record, this is the field name it was related to. Information purposes only.
769 * @return void
770 */
771 function export_addFile($fI, $recordRef='', $fieldname='') {
772 if (@is_file($fI['ID_absFile'])) {
773 if (filesize($fI['ID_absFile']) < $this->maxFileSize) {
774 $fileRec = array();
775 $fileRec['filesize'] = filesize($fI['ID_absFile']);
776 $fileRec['filename'] = basename($fI['ID_absFile']);
777 $fileRec['filemtime'] = filemtime($fI['ID_absFile']);
778 //for internal type file_reference
779 $fileRec['relFileRef'] = substr($fI['ID_absFile'], strlen(PATH_site));
780 if ($recordRef) {
781 $fileRec['record_ref'] = $recordRef.'/'.$fieldname;
782 }
783 if ($fI['relFileName']) {
784 $fileRec['relFileName'] = $fI['relFileName'];
785 }
786
787 // Setting this data in the header
788 $this->dat['header']['files'][$fI['ID']] = $fileRec;
789
790 // ... and for the recordlisting, why not let us know WHICH relations there was...
791 if ($recordRef && $recordRef!=='_SOFTREF_') {
792 $refParts = explode(':',$recordRef,2);
793 if (!is_array($this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'])) {
794 $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'] = array();
795 }
796 $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'][] = $fI['ID'];
797 }
798
799 // ... and finally add the heavy stuff:
800 $fileRec['content'] = t3lib_div::getUrl($fI['ID_absFile']);
801 $fileRec['content_md5'] = md5($fileRec['content']);
802 $this->dat['files'][$fI['ID']] = $fileRec;
803
804
805 // For soft references, do further processing:
806 if ($recordRef === '_SOFTREF_') {
807
808 // RTE files?
809 if ($RTEoriginal = $this->getRTEoriginalFilename(basename($fI['ID_absFile']))) {
810 $RTEoriginal_absPath = dirname($fI['ID_absFile']).'/'.$RTEoriginal;
811 if (@is_file($RTEoriginal_absPath)) {
812
813 $RTEoriginal_ID = md5($RTEoriginal_absPath);
814
815 $fileRec = array();
816 $fileRec['filesize'] = filesize($RTEoriginal_absPath);
817 $fileRec['filename'] = basename($RTEoriginal_absPath);
818 $fileRec['filemtime'] = filemtime($RTEoriginal_absPath);
819 $fileRec['record_ref'] = '_RTE_COPY_ID:'.$fI['ID'];
820 $this->dat['header']['files'][$fI['ID']]['RTE_ORIG_ID'] = $RTEoriginal_ID;
821
822 // Setting this data in the header
823 $this->dat['header']['files'][$RTEoriginal_ID] = $fileRec;
824
825 // ... and finally add the heavy stuff:
826 $fileRec['content'] = t3lib_div::getUrl($RTEoriginal_absPath);
827 $fileRec['content_md5'] = md5($fileRec['content']);
828 $this->dat['files'][$RTEoriginal_ID] = $fileRec;
829 } else {
830 $this->error('RTE original file "'.substr($RTEoriginal_absPath,strlen(PATH_site)).'" was not found!');
831 }
832 }
833
834 // Files with external media?
835 // This is only done with files grabbed by a softreference parser since it is deemed improbable that hard-referenced files should undergo this treatment.
836 $html_fI = pathinfo(basename($fI['ID_absFile']));
837 if ($this->includeExtFileResources && t3lib_div::inList($this->extFileResourceExtensions,strtolower($html_fI['extension']))) {
838 $uniquePrefix = '###'.md5(time()).'###';
839
840 if (strtolower($html_fI['extension'])==='css') {
841 $prefixedMedias = explode($uniquePrefix, preg_replace('/(url[[:space:]]*\([[:space:]]*["\']?)([^"\')]*)(["\']?[[:space:]]*\))/i', '\1'.$uniquePrefix.'\2'.$uniquePrefix.'\3', $fileRec['content']));
842 } else { // html, htm:
843 $htmlParser = t3lib_div::makeInstance('t3lib_parsehtml');
844 $prefixedMedias = explode($uniquePrefix, $htmlParser->prefixResourcePath($uniquePrefix,$fileRec['content'],array(),$uniquePrefix));
845 }
846
847 $htmlResourceCaptured = FALSE;
848 foreach($prefixedMedias as $k => $v) {
849 if ($k%2) {
850 $EXTres_absPath = t3lib_div::resolveBackPath(dirname($fI['ID_absFile']).'/'.$v);
851 $EXTres_absPath = t3lib_div::getFileAbsFileName($EXTres_absPath);
852 if ($EXTres_absPath && t3lib_div::isFirstPartOfStr($EXTres_absPath,PATH_site.$this->fileadminFolderName.'/') && @is_file($EXTres_absPath)) {
853
854 $htmlResourceCaptured = TRUE;
855 $EXTres_ID = md5($EXTres_absPath);
856 $this->dat['header']['files'][$fI['ID']]['EXT_RES_ID'][] = $EXTres_ID;
857 $prefixedMedias[$k] = '{EXT_RES_ID:'.$EXTres_ID.'}';
858
859 // Add file to memory if it is not set already:
860 if (!isset($this->dat['header']['files'][$EXTres_ID])) {
861 $fileRec = array();
862 $fileRec['filesize'] = filesize($EXTres_absPath);
863 $fileRec['filename'] = basename($EXTres_absPath);
864 $fileRec['filemtime'] = filemtime($EXTres_absPath);
865 $fileRec['record_ref'] = '_EXT_PARENT_:'.$fI['ID'];
866
867 $fileRec['parentRelFileName'] = $v; // Media relative to the HTML file.
868
869 // Setting this data in the header
870 $this->dat['header']['files'][$EXTres_ID] = $fileRec;
871
872 // ... and finally add the heavy stuff:
873 $fileRec['content'] = t3lib_div::getUrl($EXTres_absPath);
874 $fileRec['content_md5'] = md5($fileRec['content']);
875 $this->dat['files'][$EXTres_ID] = $fileRec;
876 }
877 }
878 }
879 }
880
881 if ($htmlResourceCaptured) {
882 $this->dat['files'][$fI['ID']]['tokenizedContent'] = implode('', $prefixedMedias);
883 }
884 }
885 }
886
887 } else $this->error($fI['ID_absFile'].' was larger ('.t3lib_div::formatSize(filesize($fI['ID_absFile'])).') than the maxFileSize ('.t3lib_div::formatSize($this->maxFileSize).')! Skipping.');
888 } else $this->error($fI['ID_absFile'].' was not a file! Skipping.');
889 }
890
891 /**
892 * DB relations flattend to 1-dim array.
893 * The list will be unique, no table/uid combination will appear twice.
894 *
895 * @param array 2-dim Array of database relations organized by table key
896 * @return array 1-dim array where entries are table:uid and keys are array with table/id
897 */
898 function flatDBrels($dbrels) {
899 $list = array();
900
901 foreach($dbrels as $dat) {
902 if ($dat['type']=='db') {
903 foreach($dat['itemArray'] as $i) {
904 $list[$i['table'].':'.$i['id']] = $i;
905 }
906 }
907 if ($dat['type']=='flex' && is_array($dat['flexFormRels']['db'])) {
908 foreach($dat['flexFormRels']['db'] as $subList) {
909 foreach($subList as $i) {
910 $list[$i['table'].':'.$i['id']] = $i;
911 }
912 }
913 }
914 }
915 return $list;
916 }
917
918 /**
919 * Soft References flattend to 1-dim array.
920 *
921 * @param array 2-dim Array of database relations organized by table key
922 * @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
923 */
924 function flatSoftRefs($dbrels) {
925 $list = array();
926 #debug($dbrels);
927 foreach($dbrels as $field => $dat) {
928 if (is_array($dat['softrefs']['keys'])) {
929 foreach($dat['softrefs']['keys'] as $spKey => $elements) {
930 if (is_array($elements)) {
931 foreach($elements as $subKey => $el) {
932 $lKey = $field.':'.$spKey.':'.$subKey;
933 $list[$lKey] = array_merge(array('field' => $field, 'spKey' => $spKey),$el);
934
935 // 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!
936 if ($el['subst'] && $el['subst']['relFileName']) {
937 $list[$lKey]['file_ID'] = md5(PATH_site.$el['subst']['relFileName']);
938 }
939 }
940 }
941 }
942 }
943 if ($dat['type']=='flex' && is_array($dat['flexFormRels']['softrefs'])) {
944 foreach($dat['flexFormRels']['softrefs'] as $structurePath => $subSoftrefs) {
945 if (is_array($subSoftrefs['keys'])) {
946 foreach($subSoftrefs['keys'] as $spKey => $elements) {
947 foreach($elements as $subKey => $el) {
948 $lKey = $field.':'.$structurePath.':'.$spKey.':'.$subKey;
949 $list[$lKey] = array_merge(array('field' => $field, 'spKey' => $spKey, 'structurePath' => $structurePath),$el);
950
951 // 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!
952 if ($el['subst'] && $el['subst']['relFileName']) {
953 $list[$lKey]['file_ID'] = md5(PATH_site.$el['subst']['relFileName']);
954 }
955 }
956 }
957 }
958 }
959 }
960 }
961
962 #debug($list);
963 return $list;
964 }
965
966
967
968
969
970
971
972
973
974
975
976 /**************************
977 *
978 * File Output
979 *
980 *************************/
981
982 /**
983 * This compiles and returns the data content for an exported file
984 *
985 * @param string Type of output; "xml" gives xml, otherwise serialized array, possibly compressed.
986 * @return string The output file stream
987 */
988 function compileMemoryToFileContent($type='') {
989
990 if ($type=='xml') {
991 $out = $this->createXML();
992 } else {
993 $compress = $this->doOutputCompress();
994 $out = '';
995
996 // adding header:
997 $out.= $this->addFilePart(serialize($this->dat['header']),$compress);
998
999 // adding records:
1000 $out.= $this->addFilePart(serialize($this->dat['records']),$compress);
1001
1002 // adding files:
1003 $out.= $this->addFilePart(serialize($this->dat['files']),$compress);
1004 }
1005
1006 return $out;
1007 }
1008
1009 /**
1010 * Creates XML string from input array
1011 *
1012 * @return string XML content
1013 */
1014 function createXML() {
1015
1016 // Options:
1017 $options = array(
1018 'alt_options' => array(
1019 '/header' => array(
1020 'disableTypeAttrib' => TRUE,
1021 'clearStackPath' => TRUE,
1022 'parentTagMap' => array(
1023 'files' => 'file',
1024 'records' => 'table',
1025 'table' => 'rec',
1026 'rec:rels' => 'relations',
1027 'relations' => 'element',
1028 'filerefs' => 'file',
1029 'pid_lookup' => 'page_contents',
1030 'header:relStaticTables' => 'static_tables',
1031 'static_tables' => 'tablename',
1032 'excludeMap' => 'item',
1033 'softrefCfg' => 'softrefExportMode',
1034 'extensionDependencies' => 'extkey',
1035 'softrefs' => 'softref_element',
1036 ),
1037 'alt_options' => array(
1038 '/pagetree' => array(
1039 'disableTypeAttrib' => TRUE,
1040 'useIndexTagForNum' => 'node',
1041 'parentTagMap' => array(
1042 'node:subrow' => 'node'
1043 )
1044 ),
1045 '/pid_lookup/page_contents' => array(
1046 'disableTypeAttrib' => TRUE,
1047 'parentTagMap' => array(
1048 'page_contents' => 'table'
1049 ),
1050 'grandParentTagMap' => array(
1051 'page_contents/table' => 'item'
1052 )
1053 )
1054 )
1055 ),
1056 '/records' => array(
1057 'disableTypeAttrib' => TRUE,
1058 'parentTagMap' => array(
1059 'records' => 'tablerow',
1060 'tablerow:data' => 'fieldlist',
1061 'tablerow:rels' => 'related',
1062 'related' => 'field',
1063 'field:itemArray' => 'relations',
1064 'field:newValueFiles' => 'filerefs',
1065 'field:flexFormRels' => 'flexform',
1066 'relations' => 'element',
1067 'filerefs' => 'file',
1068 'flexform:db' => 'db_relations',
1069 'flexform:file' => 'file_relations',
1070 'flexform:softrefs' => 'softref_relations',
1071 'softref_relations' => 'structurePath',
1072 'db_relations' => 'path',
1073 'file_relations' => 'path',
1074 'path' => 'element',
1075 'keys' => 'softref_key',
1076 'softref_key' => 'softref_element',
1077 ),
1078 'alt_options' => array(
1079 '/records/tablerow/fieldlist' => array(
1080 'useIndexTagForAssoc' => 'field',
1081 )
1082 )
1083 ),
1084 '/files' => array(
1085 'disableTypeAttrib' => TRUE,
1086 'parentTagMap' => array(
1087 'files' => 'file',
1088 ),
1089 ),
1090 )
1091 );
1092
1093 // Creating XML file from $outputArray:
1094 $charset = $this->dat['header']['charset'] ? $this->dat['header']['charset'] : 'iso-8859-1';
1095 $XML = '<?xml version="1.0" encoding="'.$charset.'" standalone="yes" ?>'.chr(10);
1096 $XML.= t3lib_div::array2xml($this->dat,'',0,'T3RecordDocument',0,$options);
1097
1098 return $XML;
1099 }
1100
1101 /**
1102 * Returns true if the output should be compressed.
1103 *
1104 * @return boolean True if compression is possible AND requested.
1105 */
1106 function doOutputCompress() {
1107 return $this->compress && !$this->dontCompress;
1108 }
1109
1110 /**
1111 * Returns a content part for a filename being build.
1112 *
1113 * @param array Data to store in part
1114 * @param boolean Compress file?
1115 * @return string Content stream.
1116 */
1117 function addFilePart($data, $compress=FALSE) {
1118 if ($compress) $data = gzcompress($data);
1119 return md5($data).':'.
1120 ($compress?'1':'0').':'.
1121 str_pad(strlen($data),10,'0',STR_PAD_LEFT).':'.
1122 $data.':';
1123 }
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138 /***********************
1139 *
1140 * Import
1141 *
1142 ***********************/
1143
1144 /**
1145 * Imports the internal data array to $pid.
1146 *
1147 * @param integer Page ID in which to import the content
1148 * @return void ...
1149 */
1150 function importData($pid) {
1151
1152 // Set this flag to indicate that an import is being/has been done.
1153 $this->doesImport = 1;
1154
1155 // Initialize:
1156 // 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.
1157 $this->import_mapId = array();
1158 $this->import_newId = array();
1159 $this->import_newId_pids = array();
1160
1161 // Temporary files stack initialized:
1162 $this->unlinkFiles = array();
1163 $this->alternativeFileName = array();
1164 $this->alternativeFilePath = array();
1165
1166 // Write records, first pages, then the rest
1167 // Fields with "hard" relations to database, files and flexform fields are kept empty during this run
1168 $this->writeRecords_pages($pid);
1169 $this->writeRecords_records($pid);
1170
1171 // Finally all the file and DB record references must be fixed. This is done after all records have supposedly been written to database:
1172 // $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.
1173 $this->setRelations();
1174
1175 // 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):
1176 $this->setFlexFormRelations();
1177
1178 // Unlink temporary files:
1179 $this->unlinkTempFiles();
1180
1181 // Finally, traverse all records and process softreferences with substitution attributes.
1182 $this->processSoftReferences();
1183 }
1184
1185 /**
1186 * Writing pagetree/pages to database:
1187 *
1188 * @param integer 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
1189 * @return void
1190 * @see writeRecords_records()
1191 */
1192 function writeRecords_pages($pid) {
1193
1194 // First, write page structure if any:
1195 if (is_array($this->dat['header']['records']['pages'])) {
1196
1197 // $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.
1198 $pageRecords = $this->dat['header']['records']['pages'];
1199 $this->import_data = array();
1200 #debug($pageRecords);
1201 // First add page tree if any
1202 if (is_array($this->dat['header']['pagetree'])) {
1203 $pagesFromTree = $this->flatInversePageTree($this->dat['header']['pagetree']);
1204 foreach($pagesFromTree as $uid) {
1205 $thisRec = $this->dat['header']['records']['pages'][$uid];
1206 // PID: Set the main $pid, unless a NEW-id is found
1207 $setPid = isset($this->import_newId_pids[$thisRec['pid']]) ? $this->import_newId_pids[$thisRec['pid']] : $pid;
1208 $this->addSingle('pages',$uid,$setPid);
1209 unset($pageRecords[$uid]);
1210 }
1211 }
1212 #debug($pageRecords);
1213 // Then add all remaining pages not in tree on root level:
1214 if (count($pageRecords)) {
1215 $remainingPageUids = array_keys($pageRecords);
1216 foreach($remainingPageUids as $pUid) {
1217 $this->addSingle('pages',$pUid,$pid);
1218 }
1219 }
1220
1221 // Now write to database:
1222 $tce = $this->getNewTCE();
1223 $tce->suggestedInsertUids = $this->suggestedInsertUids;
1224 $tce->start($this->import_data,Array());
1225 $tce->process_datamap();
1226 #debug($this->import_data,'PAGES');
1227
1228 // post-processing: Registering new ids (end all tcemain sessions with this)
1229 $this->addToMapId($tce->substNEWwithIDs);
1230
1231 // In case of an update, order pages from the page tree correctly:
1232 if ($this->update && is_array($this->dat['header']['pagetree'])) {
1233 $this->writeRecords_pages_order($pid);
1234 }
1235 }
1236 }
1237
1238 /**
1239 * Organize all updated pages in page tree so they are related like in the import file
1240 * Only used for updates and when $this->dat['header']['pagetree'] is an array.
1241 *
1242 * @param integer Page id in which to import
1243 * @return void
1244 * @access private
1245 * @see writeRecords_pages(), writeRecords_records_order()
1246 */
1247 function writeRecords_pages_order($pid) {
1248 $cmd_data = array();
1249
1250 // Get uid-pid relations and traverse them in order to map to possible new IDs
1251 $pidsFromTree = $this->flatInversePageTree_pid($this->dat['header']['pagetree']);
1252 #debug($this->dat['header']['pagetree'],'pagetree');
1253 #debug($pidsFromTree,'$pidsFromTree');
1254 #debug($this->import_newId_pids,'import_newId_pids');
1255 foreach($pidsFromTree as $origPid => $newPid) {
1256 if ($newPid>=0 && $this->dontIgnorePid('pages', $origPid)) {
1257 if (substr($this->import_newId_pids[$origPid],0,3)==='NEW') { // If the page had a new id (because it was created) use that instead!
1258
1259 if ($this->import_mapId['pages'][$origPid]) {
1260 $mappedPid = $this->import_mapId['pages'][$origPid];
1261 $cmd_data['pages'][$mappedPid]['move'] = $newPid;
1262 }
1263 } else {
1264 $cmd_data['pages'][$origPid]['move'] = $newPid;
1265 }
1266 }
1267 }
1268
1269 // Execute the move commands if any:
1270 if (count($cmd_data)) {
1271 $tce = $this->getNewTCE();
1272 $tce->start(Array(),$cmd_data);
1273 $tce->process_cmdmap();
1274 }
1275 #debug($cmd_data,'$cmd_data');
1276 }
1277
1278 /**
1279 * Write all database records except pages (writtein in writeRecords_pages())
1280 *
1281 * @param integer Page id in which to import
1282 * @return void
1283 * @see writeRecords_pages()
1284 */
1285 function writeRecords_records($pid) {
1286 global $TCA;
1287
1288 // Write the rest of the records
1289 $this->import_data = array();
1290 if (is_array($this->dat['header']['records'])) {
1291 reset($this->dat['header']['records']);
1292 while(list($table,$recs) = each($this->dat['header']['records'])) {
1293 if ($table!='pages') {
1294 reset($recs);
1295 while(list($uid,$thisRec)=each($recs)) {
1296 // PID: Set the main $pid, unless a NEW-id is found
1297 $setPid = isset($this->import_mapId['pages'][$thisRec['pid']]) ? $this->import_mapId['pages'][$thisRec['pid']] : $pid;
1298 if (is_array($TCA[$table]) && $TCA[$table]['ctrl']['rootLevel']) {
1299 $setPid = 0;
1300 }
1301
1302 // Add record:
1303 $this->addSingle($table,$uid,$setPid);
1304 }
1305 }
1306 }
1307 } else $this->error('Error: No records defined in internal data array.');
1308
1309 // Now write to database:
1310 $tce = $this->getNewTCE();
1311 $tce->suggestedInsertUids = $this->suggestedInsertUids;
1312 $tce->reverseOrder=1; // Because all records are being submitted in their correct order with positive pid numbers - and so we should reverse submission order internally.
1313 $tce->start($this->import_data,Array());
1314 $tce->process_datamap();
1315 #debug($this->import_data,'RECORDS');
1316
1317 // post-processing: Removing files and registering new ids (end all tcemain sessions with this)
1318 $this->addToMapId($tce->substNEWwithIDs);
1319
1320 // In case of an update, order pages from the page tree correctly:
1321 if ($this->update) {
1322 $this->writeRecords_records_order($pid);
1323 }
1324 }
1325
1326 /**
1327 * Organize all updated record to their new positions.
1328 * Only used for updates
1329 *
1330 * @param integer Main PID into which we import.
1331 * @return void
1332 * @access private
1333 * @see writeRecords_records(), writeRecords_pages_order()
1334 */
1335 function writeRecords_records_order($mainPid) {
1336 $cmd_data = array();
1337
1338 if (is_array($this->dat['header']['pagetree'])) {
1339 $pagesFromTree = $this->flatInversePageTree($this->dat['header']['pagetree']);
1340 } else $pagesFromTree = array();
1341
1342 #debug($this->dat['header']['pid_lookup'],'pid_lookup');
1343 if (is_array($this->dat['header']['pid_lookup'])) {
1344 foreach($this->dat['header']['pid_lookup'] as $pid => $recList) {
1345 $newPid = isset($this->import_mapId['pages'][$pid]) ? $this->import_mapId['pages'][$pid] : $mainPid;
1346 #debug(array($pid,$newPid),'$pid / $newPid');
1347
1348 if (t3lib_div::testInt($newPid)) {
1349 foreach($recList as $tableName => $uidList) {
1350 if (($tableName!='pages' || !$pagesFromTree[$pid]) && is_array($uidList)) { // If $mainPid===$newPid then we are on root level and we can consider to move pages as well! (they will not be in the page tree!)
1351 $uidList = array_reverse(array_keys($uidList));
1352 foreach($uidList as $uid) {
1353 if ($this->dontIgnorePid($tableName, $uid)) {
1354 $cmd_data[$tableName][$uid]['move'] = $newPid;
1355 } else {
1356 #debug($tableName.':'.$uid,'removed');
1357 }
1358 }
1359 }
1360 }
1361 }
1362 }
1363 }
1364
1365 // Execute the move commands if any:
1366 if (count($cmd_data)) {
1367 $tce = $this->getNewTCE();
1368 $tce->start(Array(),$cmd_data);
1369 $tce->process_cmdmap();
1370 }
1371 #debug($cmd_data,'$cmd_data');
1372 }
1373
1374 /**
1375 * Adds a single record to the $importData array. Also copies files to tempfolder.
1376 * However all File/DB-references and flexform field contents are set to blank for now! That is done with setRelations() later
1377 *
1378 * @param string Table name (from import memory)
1379 * @param integer Record UID (from import memory)
1380 * @param integer Page id
1381 * @return void
1382 * @see writeRecords()
1383 */
1384 function addSingle($table,$uid,$pid) {
1385 if ($this->import_mode[$table.':'.$uid]!=='exclude') {
1386 $record = $this->dat['records'][$table.':'.$uid]['data'];
1387 if (is_array($record)) {
1388
1389 if ($this->update && $this->doesRecordExist($table,$uid) && $this->import_mode[$table.':'.$uid]!=='as_new') {
1390 $ID = $uid;
1391 } else {
1392 #debug($this->import_mode[$table.':'.$uid],$table.':'.$uid);
1393 $ID = uniqid('NEW');
1394 }
1395 $this->import_newId[$table.':'.$ID] = array('table' => $table, 'uid' => $uid);
1396 if ($table=='pages') $this->import_newId_pids[$uid] = $ID;
1397
1398 // Set main record data:
1399 $this->import_data[$table][$ID] = $record;
1400 $this->import_data[$table][$ID]['tx_impexp_origuid'] = $this->import_data[$table][$ID]['uid'];
1401
1402 // Reset permission data:
1403 if ($table==='pages') {
1404 // Have to reset the user/group IDs so pages are owned by importing user. Otherwise strange things may happen for non-admins!
1405 unset($this->import_data[$table][$ID]['perms_userid']);
1406 unset($this->import_data[$table][$ID]['perms_groupid']);
1407
1408 // user/group/everybody settings is kept - but these might still conflict with possibilities for writing the content!"
1409 #unset($this->import_data[$table][$ID]['perms_user']);
1410 #unset($this->import_data[$table][$ID]['perms_group']);
1411 #unset($this->import_data[$table][$ID]['perms_everybody']);
1412 }
1413
1414 // PID and UID:
1415 unset($this->import_data[$table][$ID]['uid']);
1416 if (t3lib_div::testInt($ID)) { // Updates:
1417 unset($this->import_data[$table][$ID]['pid']);
1418 } else { // Inserts:
1419 $this->import_data[$table][$ID]['pid'] = $pid;
1420
1421 if ((($this->import_mode[$table.':'.$uid]==='force_uid' && $this->update) || $this->force_all_UIDS) && $GLOBALS['BE_USER']->isAdmin()) {
1422 #debug($this->import_mode[$table.':'.$uid],$table.':'.$uid);
1423 $this->import_data[$table][$ID]['uid'] = $uid;
1424 $this->suggestedInsertUids[$table.':'.$uid] = 'DELETE';
1425 }
1426 }
1427
1428 // Setting db/file blank:
1429 reset($this->dat['records'][$table.':'.$uid]['rels']);
1430 while(list($field,$config) = each($this->dat['records'][$table.':'.$uid]['rels'])) {
1431 switch((string)$config['type']) {
1432 case 'db':
1433 case 'file':
1434 // Fixed later in ->setRelations() [because we need to know ALL newly created IDs before we can map relations!]
1435 // In the meantime we set NO values for relations:
1436 $this->import_data[$table][$ID][$field] = '';
1437 break;
1438 case 'flex':
1439 // Fixed later in setFlexFormRelations()
1440 // 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!)
1441 $this->import_data[$table][$ID][$field] = '';
1442 break;
1443 }
1444 }
1445 } elseif ($table.':'.$uid != 'pages:0') { // On root level we don't want this error message.
1446 $this->error('Error: no record was found in data array!',1);
1447 }
1448 }
1449 }
1450
1451 /**
1452 * Registers the substNEWids in memory.
1453 *
1454 * @param array $substNEWwithIDs from tcemain to be merged into internal mapping variable in this object
1455 * @return void
1456 * @see writeRecords()
1457 */
1458 function addToMapId($substNEWwithIDs) {
1459 reset($this->import_data);
1460 while(list($table,$recs)=each($this->import_data)) {
1461 reset($recs);
1462 while(list($id)=each($recs)) {
1463 $old_uid = $this->import_newId[$table.':'.$id]['uid'];
1464 if (isset($substNEWwithIDs[$id])) {
1465 $this->import_mapId[$table][$old_uid] = $substNEWwithIDs[$id];
1466 } elseif ($this->update) {
1467 $this->import_mapId[$table][$old_uid] = $id; // Map same ID to same ID....
1468 } else $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);
1469 }
1470 }
1471 }
1472
1473 /**
1474 * Returns a new $TCE object
1475 *
1476 * @return object $TCE object
1477 */
1478 function getNewTCE() {
1479 $tce = t3lib_div::makeInstance('t3lib_TCEmain');
1480 $tce->stripslashes_values = 0;
1481 $tce->dontProcessTransformations = 1;
1482 $tce->enableLogging = $this->enableLogging;
1483 $tce->alternativeFileName = $this->alternativeFileName;
1484 $tce->alternativeFilePath = $this->alternativeFilePath;
1485 return $tce;
1486 }
1487
1488 /**
1489 * Cleaning up all the temporary files stored in typo3temp/ folder
1490 *
1491 * @return void
1492 */
1493 function unlinkTempFiles() {
1494 foreach($this->unlinkFiles as $fileName) {
1495 if (t3lib_div::isFirstPartOfStr($fileName, PATH_site.'typo3temp/')) {
1496 t3lib_div::unlink_tempfile($fileName);
1497 clearstatcache();
1498 if (is_file($fileName)) {
1499 $this->error('Error: '.$fileName.' was NOT unlinked as it should have been!',1);
1500 }
1501 } else $this->error('Error: '.$fileName.' was not in temp-path. Not removed!',1);
1502 }
1503 $this->unlinkFiles = array();
1504 }
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518 /***************************
1519 *
1520 * Import / Relations setting
1521 *
1522 ***************************/
1523
1524 /**
1525 * 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)
1526 * Relations in flexform fields are processed in setFlexFormRelations() after this function
1527 *
1528 * @return void
1529 * @see setFlexFormRelations()
1530 */
1531 function setRelations() {
1532 global $TCA;
1533
1534 $updateData = array();
1535
1536 // import_newId contains a register of all records that was in the import memorys "records" key
1537 reset($this->import_newId);
1538 while(list($nId,$dat) = each($this->import_newId)) {
1539 $table = $dat['table'];
1540 $uid = $dat['uid']; // original UID - NOT the new one!
1541
1542 // If the record has been written and received a new id, then proceed:
1543 if (is_array($this->import_mapId[$table]) && isset($this->import_mapId[$table][$uid])) {
1544 $thisNewUid = t3lib_BEfunc::wsMapId($table,$this->import_mapId[$table][$uid]);
1545
1546 if (is_array($this->dat['records'][$table.':'.$uid]['rels'])) {
1547 reset($this->dat['records'][$table.':'.$uid]['rels']);
1548
1549 // Traverse relation fields of each record
1550 while(list($field,$config) = each($this->dat['records'][$table.':'.$uid]['rels'])) {
1551 switch((string)$config['type']) {
1552 case 'db':
1553 if (is_array($config['itemArray']) && count($config['itemArray'])) {
1554 $valArray = $this->setRelations_db($config['itemArray']);
1555 $updateData[$table][$thisNewUid][$field] = implode(',',$valArray); // List of [table]_[uid]
1556 }
1557 break;
1558 case 'file':
1559 if (is_array($config['newValueFiles']) && count($config['newValueFiles'])) {
1560 $valArr = array();
1561 foreach($config['newValueFiles'] as $fI) {
1562 $valArr[] = $this->import_addFileNameToBeCopied($fI);
1563 }
1564 $updateData[$table][$thisNewUid][$field] = implode(',',$valArr); // List of absolute files
1565 }
1566 break;
1567 }
1568 }
1569 } else $this->error('Error: no record was found in data array!',1);
1570 } else $this->error('Error: this records is NOT created it seems! ('.$table.':'.$uid.')',1);
1571 }
1572 if (count($updateData)) {
1573 $tce = $this->getNewTCE();
1574 $tce->start($updateData,Array());
1575 $tce->process_datamap();
1576 #debug($updateData,'setRelations()');
1577 }
1578 }
1579
1580 /**
1581 * Maps relations for database
1582 *
1583 * @param array Array of item sets (table/uid) from a dbAnalysis object
1584 * @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.
1585 */
1586 function setRelations_db($itemArray) {
1587 $valArray = array();
1588
1589 foreach($itemArray as $relDat) {
1590 if (is_array($this->import_mapId[$relDat['table']]) && isset($this->import_mapId[$relDat['table']][$relDat['id']])) {
1591
1592 #debug('FOUND: '.$relDat['table'].':'.$relDat['id'],1);
1593 $valArray[] = $relDat['table'].'_'.$this->import_mapId[$relDat['table']][$relDat['id']];
1594 } elseif ($this->isTableStatic($relDat['table']) || $this->isExcluded($relDat['table'], $relDat['id']) || $relDat['id']<0) { // 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.
1595
1596 #debug('STATIC: '.$relDat['table'].':'.$relDat['id'],1);
1597 $valArray[] = $relDat['table'].'_'.$relDat['id'];
1598 } else {
1599
1600 $this->error('Lost relation: '.$relDat['table'].':'.$relDat['id'],1);
1601 }
1602 }
1603
1604 return $valArray;
1605 }
1606
1607 /**
1608 * Writes the file from import array to temp dir and returns the filename of it.
1609 *
1610 * @param array 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"
1611 * @return string Absolute filename of the temporary filename of the file. In ->alternativeFileName the original name is set.
1612 */
1613 function import_addFileNameToBeCopied($fI) {
1614 if (is_array($this->dat['files'][$fI['ID']])) {
1615 $tmpFile = t3lib_div::tempnam('import_temp_');
1616 t3lib_div::writeFile($tmpFile,$this->dat['files'][$fI['ID']]['content']);
1617 clearstatcache();
1618 if (@is_file($tmpFile)) {
1619 $this->unlinkFiles[] = $tmpFile;
1620 if (filesize($tmpFile)==$this->dat['files'][$fI['ID']]['filesize']) {
1621 $this->alternativeFileName[$tmpFile] = $fI['filename'];
1622 $this->alternativeFilePath[$tmpFile] = $this->dat['files'][$fI['ID']]['relFileRef'];
1623
1624 return $tmpFile;
1625 } else $this->error('Error: temporary file '.$tmpFile.' had a size ('.filesize($tmpFile).') different from the original ('.$this->dat['files'][$fI['ID']]['filesize'].')',1);
1626 } else $this->error('Error: temporary file '.$tmpFile.' was not written as it should have been!',1);
1627 } else $this->error('Error: No file found for ID '.$fI['ID'],1);
1628 }
1629
1630 /**
1631 * 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.
1632 * 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!
1633 *
1634 * @return void
1635 * @see setRelations()
1636 */
1637 function setFlexFormRelations() {
1638 global $TCA;
1639
1640 $updateData = array();
1641 #debug($this->import_newId);
1642 // import_newId contains a register of all records that was in the import memorys "records" key
1643 reset($this->import_newId);
1644 while(list($nId,$dat) = each($this->import_newId)) {
1645 $table = $dat['table'];
1646 $uid = $dat['uid']; // original UID - NOT the new one!
1647
1648 // If the record has been written and received a new id, then proceed:
1649 if (is_array($this->import_mapId[$table]) && isset($this->import_mapId[$table][$uid])) {
1650 $thisNewUid = t3lib_BEfunc::wsMapId($table,$this->import_mapId[$table][$uid]);
1651 #debug($thisNewUid,'$thisNewUid');
1652 #debug($this->dat['records'][$table.':'.$uid]['rels']);
1653 if (is_array($this->dat['records'][$table.':'.$uid]['rels'])) {
1654 reset($this->dat['records'][$table.':'.$uid]['rels']);
1655 t3lib_div::loadTCA($table);
1656
1657 // Traverse relation fields of each record
1658 while(list($field,$config) = each($this->dat['records'][$table.':'.$uid]['rels'])) {
1659 switch((string)$config['type']) {
1660 case 'flex':
1661 // Get XML content and set as default value (string, non-processed):
1662 $updateData[$table][$thisNewUid][$field] = $this->dat['records'][$table.':'.$uid]['data'][$field];
1663
1664 // If there has been registered relations inside the flex form field, run processing on the content:
1665 if (count($config['flexFormRels']['db']) || count($config['flexFormRels']['file'])) {
1666 $origRecordRow = t3lib_BEfunc::getRecord($table,$thisNewUid,'*'); // This will fetch the new row for the element (which should be updated with any references to data structures etc.)
1667 #debug(array($updateData[$table][$thisNewUid][$field]),'flexXML');
1668 $conf = $TCA[$table]['columns'][$field]['config'];
1669 if (is_array($origRecordRow) && is_array($conf) && $conf['type']==='flex') {
1670 #debug($conf,'***');
1671 // Get current data structure and value array:
1672 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $origRecordRow, $table);
1673 $currentValueArray = t3lib_div::xml2array($updateData[$table][$thisNewUid][$field]);
1674 # debug($dataStructArray);
1675 # debug($currentValueArray);
1676 # debug($origRecordRow);
1677 # debug($currentValueArray['data'],'BE');
1678 // Do recursive processing of the XML data:
1679 $iteratorObj = t3lib_div::makeInstance('t3lib_TCEmain');
1680 $iteratorObj->callBackObj = &$this;
1681 $currentValueArray['data'] = $iteratorObj->checkValue_flex_procInData(
1682 $currentValueArray['data'],
1683 array(), // Not used.
1684 array(), // Not used.
1685 $dataStructArray,
1686 array($table,$thisNewUid,$field,$config), // Parameters.
1687 'remapListedDBRecords_flexFormCallBack'
1688 );
1689 #debug($currentValueArray['data'],'AF');
1690 // The return value is set as an array which means it will be processed by tcemain for file and DB references!
1691 if (is_array($currentValueArray['data'])) {
1692 $updateData[$table][$thisNewUid][$field] = $currentValueArray;
1693 }
1694 }
1695 }
1696 break;
1697 }
1698 }
1699 } else $this->error('Error: no record was found in data array!',1);
1700 } else $this->error('Error: this records is NOT created it seems! ('.$table.':'.$uid.')',1);
1701 }
1702 if (count($updateData)) {
1703 $tce = $this->getNewTCE();
1704 $tce->start($updateData,Array());
1705 $tce->process_datamap();
1706 #debug($updateData,'setFlexFormRelations()');
1707 }
1708 }
1709
1710 /**
1711 * Callback function for traversing the FlexForm structure in relation to remapping database relations
1712 *
1713 * @param array Set of parameters in numeric array: table, uid, field
1714 * @param array TCA config for field (from Data Structure of course)
1715 * @param string Field value (from FlexForm XML)
1716 * @param string Not used
1717 * @param string Not used
1718 * @param string Path of where the data structure of the element is found
1719 * @return array Array where the "value" key carries the value.
1720 * @see setFlexFormRelations()
1721 */
1722 function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) {
1723
1724 // Extract parameters:
1725 list($table,$uid,$field,$config) = $pParams;
1726
1727 // In case the $path is used as index without a trailing slash we will remove that
1728 if (!is_array($config['flexFormRels']['db'][$path]) && is_array($config['flexFormRels']['db'][rtrim($path, '/')])) {
1729 $path = rtrim($path, '/');
1730 }
1731 if (is_array($config['flexFormRels']['db'][$path])) {
1732 $valArray = $this->setRelations_db($config['flexFormRels']['db'][$path]);
1733 $dataValue = implode(',',$valArray);
1734 }
1735
1736 if (is_array($config['flexFormRels']['file'][$path])) {
1737 foreach($config['flexFormRels']['file'][$path] as $fI) {
1738 $valArr[] = $this->import_addFileNameToBeCopied($fI);
1739 }
1740 $dataValue = implode(',',$valArr);
1741 }
1742
1743 return array('value' => $dataValue);
1744 }
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756 /**************************
1757 *
1758 * Import / Soft References
1759 *
1760 *************************/
1761
1762 /**
1763 * Processing of soft references
1764 *
1765 * @return void
1766 */
1767 function processSoftReferences() {
1768 global $TCA;
1769
1770 // Initialize:
1771 $inData = array();
1772
1773 // Traverse records:
1774 if (is_array($this->dat['header']['records'])) {
1775 foreach($this->dat['header']['records'] as $table => $recs) {
1776 foreach($recs as $uid => $thisRec) {
1777
1778 // If there are soft references defined, traverse those:
1779 if (isset($TCA[$table]) && is_array($thisRec['softrefs'])) {
1780 t3lib_div::loadTCA($table);
1781
1782 // 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.
1783 $fieldsIndex = array();
1784 foreach($thisRec['softrefs'] as $softrefDef) {
1785
1786 // If a substitution token is set:
1787 if ($softrefDef['field'] && is_array($softrefDef['subst']) && $softrefDef['subst']['tokenID']) {
1788 $fieldsIndex[$softrefDef['field']][$softrefDef['subst']['tokenID']] = $softrefDef;
1789 }
1790 }
1791
1792 // The new id:
1793 $thisNewUid = t3lib_BEfunc::wsMapId($table,$this->import_mapId[$table][$uid]);
1794
1795 // Now, if there are any fields that require substitution to be done, lets go for that:
1796 foreach($fieldsIndex as $field => $softRefCfgs) {
1797 if (is_array($TCA[$table]['columns'][$field])) {
1798 $conf = $TCA[$table]['columns'][$field]['config'];
1799 if ($conf['type']==='flex') {
1800
1801 $origRecordRow = t3lib_BEfunc::getRecord($table,$thisNewUid,'*'); // This will fetch the new row for the element (which should be updated with any references to data structures etc.)
1802 if (is_array($origRecordRow)) {
1803
1804 // Get current data structure and value array:
1805 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $origRecordRow, $table);
1806 $currentValueArray = t3lib_div::xml2array($origRecordRow[$field]);
1807
1808 // Do recursive processing of the XML data:
1809 $iteratorObj = t3lib_div::makeInstance('t3lib_TCEmain');
1810 $iteratorObj->callBackObj = &$this;
1811 $currentValueArray['data'] = $iteratorObj->checkValue_flex_procInData(
1812 $currentValueArray['data'],
1813 array(), // Not used.
1814 array(), // Not used.
1815 $dataStructArray,
1816 array($table,$uid,$field,$softRefCfgs), // Parameters (using old UID on purpose!)
1817 'processSoftReferences_flexFormCallBack'
1818 );
1819
1820 // The return value is set as an array which means it will be processed by tcemain for file and DB references!
1821 if (is_array($currentValueArray['data'])) {
1822 $inData[$table][$thisNewUid][$field] = $currentValueArray;
1823 }
1824 }
1825 } else {
1826 // Get tokenizedContent string and proceed only if that is not blank:
1827 $tokenizedContent = $this->dat['records'][$table.':'.$uid]['rels'][$field]['softrefs']['tokenizedContent'];
1828 if (strlen($tokenizedContent) && is_array($softRefCfgs)) {
1829 $inData[$table][$thisNewUid][$field] = $this->processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid);
1830 }
1831 }
1832 }
1833 }
1834 }
1835 }
1836 }
1837 }
1838
1839 // Now write to database:
1840 $tce = $this->getNewTCE();
1841 $tce->enableLogging = TRUE;
1842 $tce->start($inData, Array());
1843 $tce->process_datamap();
1844 }
1845
1846 /**
1847 * Callback function for traversing the FlexForm structure in relation to remapping softreference relations
1848 *
1849 * @param array Set of parameters in numeric array: table, uid, field
1850 * @param array TCA config for field (from Data Structure of course)
1851 * @param string Field value (from FlexForm XML)
1852 * @param string Not used
1853 * @param string Not used
1854 * @param string Path of where the data structure where the element is found
1855 * @return array Array where the "value" key carries the value.
1856 * @see setFlexFormRelations()
1857 */
1858 function processSoftReferences_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) {
1859
1860 // Extract parameters:
1861 list($table,$origUid,$field,$softRefCfgs) = $pParams;
1862
1863 if (is_array($softRefCfgs)) {
1864
1865 // First, find all soft reference configurations for this structure path (they are listed flat in the header):
1866 $thisSoftRefCfgList = array();
1867 foreach($softRefCfgs as $sK => $sV) {
1868 if ($sV['structurePath']===$path) {
1869 $thisSoftRefCfgList[$sK] = $sV;
1870 }
1871 }
1872
1873 // If any was found, do processing:
1874 if (count($thisSoftRefCfgList)) {
1875
1876 // Get tokenizedContent string and proceed only if that is not blank:
1877 $tokenizedContent = $this->dat['records'][$table.':'.$origUid]['rels'][$field]['flexFormRels']['softrefs'][$path]['tokenizedContent'];
1878 if (strlen($tokenizedContent)) {
1879 $dataValue = $this->processSoftReferences_substTokens($tokenizedContent, $thisSoftRefCfgList, $table, $origUid);
1880 }
1881 }
1882 }
1883
1884 // Return
1885 return array('value' => $dataValue);
1886 }
1887
1888 /**
1889 * Substition of softreference tokens
1890 *
1891 * @param string Content of field with soft reference tokens in.
1892 * @param array Soft reference configurations
1893 * @param string Table for which the processing occurs
1894 * @param string UID of record from table
1895 * @return string The input content with tokens substituted according to entries in softRefCfgs
1896 */
1897 function processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid) {
1898
1899 // traverse each softref type for this field:
1900 foreach($softRefCfgs as $cfg) {
1901
1902 // Get token ID:
1903 $tokenID = $cfg['subst']['tokenID'];
1904
1905 // Default is current token value:
1906 $insertValue = $cfg['subst']['tokenValue'];
1907
1908 // Based on mode:
1909 switch((string)$this->softrefCfg[$tokenID]['mode']) {
1910 case 'exclude':
1911 // Exclude is a simple passthrough of the value
1912 break;
1913 case 'editable':
1914 // Editable always picks up the value from this input array:
1915 $insertValue = $this->softrefInputValues[$tokenID];
1916 break;
1917 default:
1918 // Mapping IDs/creating files: Based on type, look up new value:
1919 switch((string)$cfg['subst']['type']) {
1920 case 'db':
1921 default:
1922 // Trying to map database element if found in the mapID array:
1923 list($tempTable,$tempUid) = explode(':',$cfg['subst']['recordRef']);
1924 if (isset($this->import_mapId[$tempTable][$tempUid])) {
1925 $insertValue = t3lib_BEfunc::wsMapId($tempTable,$this->import_mapId[$tempTable][$tempUid]);
1926
1927 // 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!
1928 if ($tempTable==='pages' && !t3lib_div::testInt($cfg['subst']['tokenValue'])) {
1929 $recWithUniqueValue = t3lib_BEfunc::getRecord($tempTable,$insertValue, 'alias');
1930 if ($recWithUniqueValue['alias']) {
1931 $insertValue = $recWithUniqueValue['alias'];
1932 }
1933 }
1934 }
1935 break;
1936 break;
1937 case 'file':
1938 // Create / Overwrite file:
1939 $insertValue = $this->processSoftReferences_saveFile($cfg['subst']['relFileName'], $cfg, $table, $uid);
1940 break;
1941 }
1942 break;
1943 }
1944
1945 // Finally, swap the soft reference token in tokenized content with the insert value:
1946 $tokenizedContent = str_replace('{softref:'.$tokenID.'}', $insertValue, $tokenizedContent);
1947 }
1948
1949 return $tokenizedContent;
1950 }
1951
1952 /**
1953 * Process a soft reference file
1954 *
1955 * @param string Old Relative filename
1956 * @param array soft reference configuration array
1957 * @param string Table for which the processing occurs
1958 * @param string UID of record from table
1959 * @return string New relative filename (value to insert instead of the softref token)
1960 */
1961 function processSoftReferences_saveFile($relFileName, $cfg, $table, $uid) {
1962
1963 if ($fileHeaderInfo = $this->dat['header']['files'][$cfg['file_ID']]) {
1964 // Initialize; Get directory prefix for file and find possible RTE filename
1965 $dirPrefix = dirname($relFileName).'/';
1966 $rteOrigName = $this->getRTEoriginalFilename(basename($relFileName));
1967
1968 // If filename looks like an RTE file, and the directory is in "uploads/", then process as a RTE file!
1969 if ($rteOrigName && t3lib_div::isFirstPartOfStr($dirPrefix,'uploads/')) { // RTE:
1970
1971 // First, find unique RTE file name:
1972 if (@is_dir(PATH_site.$dirPrefix)) {
1973
1974 // 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...
1975 $fileProcObj = &$this->getFileProcObj();
1976 $origDestName = $fileProcObj->getUniqueName($rteOrigName, PATH_site.$dirPrefix);
1977
1978 // Create copy file name:
1979 $pI = pathinfo($relFileName);
1980 $copyDestName = dirname($origDestName).'/RTEmagicC_'.substr(basename($origDestName),10).'.'.$pI['extension'];
1981 if (!@is_file($copyDestName) && !@is_file($origDestName)
1982 && $origDestName===t3lib_div::getFileAbsFileName($origDestName) && $copyDestName===t3lib_div::getFileAbsFileName($copyDestName)) {
1983
1984 if ($this->dat['header']['files'][$fileHeaderInfo['RTE_ORIG_ID']]) {
1985
1986 // Write the copy and original RTE file to the respective filenames:
1987 $this->writeFileVerify($copyDestName, $cfg['file_ID'], TRUE);
1988 $this->writeFileVerify($origDestName, $fileHeaderInfo['RTE_ORIG_ID'], TRUE);
1989
1990 // Return the relative path of the copy file name:
1991 return substr($copyDestName, strlen(PATH_site));
1992 } else $this->error('ERROR: Could not find original file ID');
1993 } else $this->error('ERROR: The destination filenames "'.$copyDestName.'" and "'.$origDestName.'" either existed or have non-valid names');
1994 } else $this->error('ERROR: "'.PATH_site.$dirPrefix.'" was not a directory, so could not process file "'.$relFileName.'"');
1995
1996
1997 } elseif (t3lib_div::isFirstPartOfStr($dirPrefix, $this->fileadminFolderName.'/')) { // File in fileadmin/ folder:
1998
1999 // Create file (and possible resources)
2000 $newFileName = $this->processSoftReferences_saveFile_createRelFile($dirPrefix,basename($relFileName),$cfg['file_ID'], $table, $uid);
2001
2002 if (strlen($newFileName)) {
2003 $relFileName = $newFileName;
2004 } else $this->error('ERROR: No new file created for "'.$relFileName.'"');
2005 } else $this->error('ERROR: Sorry, cannot operate on non-RTE files which are outside the fileadmin folder.');
2006 } else $this->error('ERROR: Could not find file ID in header.');
2007
2008 // Return (new) filename relative to PATH_site:
2009 return $relFileName;
2010 }
2011
2012 /**
2013 * Create file in directory and return the new (unique) filename
2014 *
2015 * @param string Directory prefix, relative, with trailing slash
2016 * @param string Filename (without path)
2017 * @param string File ID from import memory
2018 * @param string Table for which the processing occurs
2019 * @param string UID of record from table
2020 * @return string New relative filename, if any
2021 */
2022 function processSoftReferences_saveFile_createRelFile($origDirPrefix, $fileName, $fileID, $table, $uid) {
2023
2024 // 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!
2025 if ($this->fileIDMap[$fileID]) {
2026 return substr($this->fileIDMap[$fileID],strlen(PATH_site));
2027 }
2028
2029 // Verify FileMount access to dir-prefix. Returns the best alternative relative path if any
2030 $dirPrefix = $this->verifyFolderAccess($origDirPrefix);
2031
2032 if ($dirPrefix && (!$this->update || $origDirPrefix===$dirPrefix) && $this->checkOrCreateDir($dirPrefix)) {
2033 $fileHeaderInfo = $this->dat['header']['files'][$fileID];
2034 $updMode = $this->update && $this->import_mapId[$table][$uid]===$uid && $this->import_mode[$table.':'.$uid]!=='as_new';
2035 // Create new name for file:
2036 if ($updMode) { // Must have same ID in map array (just for security, is not really needed) and NOT be set "as_new".
2037 $newName = PATH_site.$dirPrefix.$fileName;
2038 } else {
2039 // Create unique filename:
2040 $fileProcObj = &$this->getFileProcObj();
2041 $newName = $fileProcObj->getUniqueName($fileName, PATH_site.$dirPrefix);
2042 }
2043 #debug($newName,'$newName');
2044
2045 // Write main file:
2046 if ($this->writeFileVerify($newName, $fileID)) {
2047
2048 // If the resource was an HTML/CSS file with resources attached, we will write those as well!
2049 if (is_array($fileHeaderInfo['EXT_RES_ID'])) {
2050 #debug($fileHeaderInfo['EXT_RES_ID']);
2051 $tokenizedContent = $this->dat['files'][$fileID]['tokenizedContent'];
2052 $tokenSubstituted = FALSE;
2053
2054 $fileProcObj = &$this->getFileProcObj();
2055
2056 if ($updMode) {
2057 foreach($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
2058 if ($this->dat['files'][$res_fileID]['filename']) {
2059
2060 // Resolve original filename:
2061 $relResourceFileName = $this->dat['files'][$res_fileID]['parentRelFileName'];
2062 $absResourceFileName = t3lib_div::resolveBackPath(PATH_site.$origDirPrefix.$relResourceFileName);
2063 $absResourceFileName = t3lib_div::getFileAbsFileName($absResourceFileName);
2064 if ($absResourceFileName && t3lib_div::isFirstPartOfStr($absResourceFileName,PATH_site.$this->fileadminFolderName.'/')) {
2065 $destDir = substr(dirname($absResourceFileName).'/',strlen(PATH_site));
2066 if ($this->verifyFolderAccess($destDir, TRUE) && $this->checkOrCreateDir($destDir)) {
2067 $this->writeFileVerify($absResourceFileName, $res_fileID);
2068 } else $this->error('ERROR: Could not create file in directory "'.$destDir.'"');
2069 } else $this->error('ERROR: Could not resolve path for "'.$relResourceFileName.'"');
2070
2071 $tokenizedContent = str_replace('{EXT_RES_ID:'.$res_fileID.'}', $relResourceFileName, $tokenizedContent);
2072 $tokenSubstituted = TRUE;
2073 }
2074 }
2075 } else {
2076 // Create the resouces directory name (filename without extension, suffixed "_FILES")
2077 $resourceDir = dirname($newName).'/'.preg_replace('/\.[^.]*$/','',basename($newName)).'_FILES';
2078 if (t3lib_div::mkdir($resourceDir)) {
2079 foreach($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) {
2080 if ($this->dat['files'][$res_fileID]['filename']) {
2081 $absResourceFileName = $fileProcObj->getUniqueName($this->dat['files'][$res_fileID]['filename'], $resourceDir);
2082 $relResourceFileName = substr($absResourceFileName, strlen(dirname($resourceDir))+1);
2083 $this->writeFileVerify($absResourceFileName, $res_fileID);
2084
2085 $tokenizedContent = str_replace('{EXT_RES_ID:'.$res_fileID.'}', $relResourceFileName, $tokenizedContent);
2086 $tokenSubstituted = TRUE;
2087 }
2088 }
2089 }
2090 }
2091
2092 // If substitutions has been made, write the content to the file again:
2093 if ($tokenSubstituted) {
2094 t3lib_div::writeFile($newName, $tokenizedContent);
2095 }
2096 }
2097
2098 return substr($newName, strlen(PATH_site));
2099 }
2100 }
2101 }
2102
2103 /**
2104 * Writes a file from the import memory having $fileID to file name $fileName which must be an absolute path inside PATH_site
2105 *
2106 * @param string Absolute filename inside PATH_site to write to
2107 * @param string File ID from import memory
2108 * @param boolean Bypasses the checking against filemounts - only for RTE files!
2109 * @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.
2110 */
2111 function writeFileVerify($fileName, $fileID, $bypassMountCheck=FALSE) {
2112 $fileProcObj = &$this->getFileProcObj();
2113
2114 if ($fileProcObj->actionPerms['newFile']) {
2115 if ($fileProcObj->checkPathAgainstMounts($fileName) || $bypassMountCheck) { // Just for security, check again. Should actually not be necessary.
2116 $fI = t3lib_div::split_fileref($fileName);
2117 if ($fileProcObj->checkIfAllowed($fI['fileext'], $fI['path'], $fI['file']) || ($this->allowPHPScripts && $GLOBALS['BE_USER']->isAdmin())) {
2118 if (t3lib_div::getFileAbsFileName($fileName)) {
2119 if ($this->dat['files'][$fileID]) {
2120 t3lib_div::writeFile($fileName,$this->dat['files'][$fileID]['content']);
2121 $this->fileIDMap[$fileID] = $fileName;
2122 if (md5(t3lib_div::getUrl($fileName))==$this->dat['files'][$fileID]['content_md5']) {
2123 return TRUE;
2124 } else $this->error('ERROR: File content "'.$fileName.'" was corrupted');
2125 } else $this->error('ERROR: File ID "'.$fileID.'" could not be found');
2126 } else $this->error('ERROR: Filename "'.$fileName.'" was not a valid relative file path!');
2127 } else $this->error('ERROR: Filename "'.$fileName.'" failed against extension check or deny-pattern!');
2128 } else $this->error('ERROR: Filename "'.$fileName.'" was not allowed in destination path!');
2129 } else $this->error('ERROR: You did not have sufficient permissions to write the file "'.$fileName.'"');
2130 }
2131
2132 /**
2133 * Returns true if directory exists and if it doesn't it will create directory and return true if that succeeded.
2134 *
2135 * @param string Directory to create. Having a trailing slash. Must be in fileadmin/. Relative to PATH_site
2136 * @return boolean True, if directory exists (was created)
2137 */
2138 function checkOrCreateDir($dirPrefix) {
2139
2140 // Split dir path and remove first directory (which should be "fileadmin")
2141 $filePathParts = explode('/', $dirPrefix);
2142 $firstDir = array_shift($filePathParts);
2143
2144 if ($firstDir===$this->fileadminFolderName && t3lib_div::getFileAbsFileName($dirPrefix)) {
2145
2146 $pathAcc = '';
2147 foreach($filePathParts as $dirname) {
2148 $pathAcc.='/'.$dirname;
2149 if (strlen($dirname)) {
2150 if (!@is_dir(PATH_site.$this->fileadminFolderName.$pathAcc)) {
2151 if (!t3lib_div::mkdir(PATH_site.$this->fileadminFolderName.$pathAcc)) {
2152 $this->error('ERROR: Directory could not be created....B');
2153 return FALSE;
2154 }
2155 }
2156 } elseif ($dirPrefix===$this->fileadminFolderName.$pathAcc) {
2157 return TRUE;
2158 } else $this->error('ERROR: Directory could not be created....A');
2159 }
2160 }
2161 }
2162
2163 /**
2164 * Verifies that the input path (relative to PATH_site) is found in the backend users filemounts.
2165 * If it doesn't it will try to find another relative filemount for the user and return an alternative path prefix for the file.
2166 *
2167 * @param string Path relative to PATH_site
2168 * @param boolean If set, Do not look for alternative path! Just return false
2169 * @return string If a path is available that will be returned, otherwise false.
2170 */
2171 function verifyFolderAccess($dirPrefix, $noAlternative=FALSE) {
2172 $fileProcObj = &$this->getFileProcObj();
2173
2174 #$fileProcObj->mounts['1f390e42e1dc46f125310ead30c7bd9d']['path'] = '/var/www/typo3/dev/testsite-3.6.0/fileadmin/user_upload/';
2175
2176 // Check, if dirPrefix is inside a valid Filemount for user:
2177 $result = $fileProcObj->checkPathAgainstMounts(PATH_site.$dirPrefix);
2178
2179 // If not, try to find another relative filemount and use that instead:
2180 if (!$result) {
2181 if ($noAlternative) return FALSE;
2182
2183 // Find first web folder:
2184 $result = $fileProcObj->findFirstWebFolder();
2185
2186 // If that succeeded, return the path to it:
2187 if ($result) {
2188 // Remove the "fileadmin/" prefix of input path - and append the rest to the return value:
2189 if (t3lib_div::isFirstPartOfStr($dirPrefix,$this->fileadminFolderName.'/')) {
2190 $dirPrefix = substr($dirPrefix,strlen($this->fileadminFolderName.'/'));
2191 }
2192 return substr($fileProcObj->mounts[$result]['path'].$dirPrefix,strlen(PATH_site));
2193 }
2194 } else {
2195 return $dirPrefix;
2196 }
2197 }
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208 /**************************
2209 *
2210 * File Input
2211 *
2212 *************************/
2213
2214 /**
2215 * Loads the header section/all of the $filename into memory
2216 *
2217 * @param string Filename, absolute
2218 * @param boolean If set, all information is loaded (header, records and files). Otherwise the default is to read only the header information
2219 * @return boolean True if the operation went well
2220 */
2221 function loadFile($filename,$all=0) {
2222 if (@is_file($filename)) {
2223 $fI = pathinfo($filename);
2224 if (strtolower($fI['extension'])=='xml') {
2225 // XML:
2226 $xmlContent = t3lib_div::getUrl($filename);
2227 if (strlen($xmlContent)) {
2228 $this->dat = t3lib_div::xml2array($xmlContent,'',TRUE);
2229 if (is_array($this->dat)) {
2230 if ($this->dat['_DOCUMENT_TAG']==='T3RecordDocument' && is_array($this->dat['header']) && is_array($this->dat['records'])) {
2231 $this->loadInit();
2232 return TRUE;
2233 } else $this->error('XML file did not contain proper XML for TYPO3 Import');
2234 } else $this->error('XML could not be parsed: '.$this->dat);
2235 } else $this->error('Error opening file: '.$filename);
2236 } else {
2237 // T3D
2238 if($fd = fopen($filename,'rb')) {
2239 $this->dat['header'] = $this->getNextFilePart($fd,1,'header');
2240 if ($all) {
2241 $this->dat['records'] = $this->getNextFilePart($fd,1,'records');
2242 $this->dat['files'] = $this->getNextFilePart($fd,1,'files');
2243 }
2244 $this->loadInit();
2245 return TRUE;
2246 } else $this->error('Error opening file: '.$filename);
2247 fclose($fd);
2248 }
2249 } else $this->error('Filename not found: '.$filename);
2250
2251 return FALSE;
2252 }
2253
2254 /**
2255 * Returns the next content part form the fileresource (t3d), $fd
2256 *
2257 * @param pointer File pointer
2258 * @param boolean If set, the returned content is unserialized into an array, otherwise you get the raw string
2259 * @param string For error messages this indicates the section of the problem.
2260 * @return string Data string
2261 * @access private
2262 * @see loadFile()
2263 */
2264 function getNextFilePart($fd,$unserialize=0,$name='') {
2265 $initStrLen = 32+1+1+1+10+1;
2266
2267 // getting header data
2268 $initStr = fread($fd, $initStrLen);
2269 $initStrDat = explode(':',$initStr);
2270 if (strstr($initStrDat[0],'Warning') == FALSE) {
2271 if (!strcmp($initStrDat[3],'')) {
2272 $datString = fread($fd,intval($initStrDat[2]));
2273 fread($fd,1);
2274 if (!strcmp(md5($datString), $initStrDat[0])) {
2275 if ($initStrDat[1]) {
2276 if ($this->compress) {
2277 $datString = gzuncompress($datString);
2278 } else $this->error('Content read error: This file requires decompression, but this server does not offer gzcompress()/gzuncompress() functions.',1);
2279 }
2280 return $unserialize ? unserialize($datString) : $datString;
2281 } else $this->error('MD5 check failed ('.$name.')');
2282 } else $this->error('File read error: InitString had a wrong length. ('.$name.')');
2283 } else $this->error('File read error: Warning message in file. ('.$initStr.fgets($fd).')');
2284 }
2285
2286 /**
2287 * Loads T3D file content into the $this->dat array
2288 * (This function can be used to test the output strings from ->compileMemoryToFileContent())
2289 *
2290 * @param string File content
2291 * @return void
2292 */
2293 function loadContent($filecontent) {
2294 $pointer = 0;
2295
2296 $this->dat['header'] = $this->getNextContentPart($filecontent,$pointer,1,'header');
2297 $this->dat['records'] = $this->getNextContentPart($filecontent,$pointer,1,'records');
2298 $this->dat['files'] = $this->getNextContentPart($filecontent,$pointer,1,'files');
2299 $this->loadInit();
2300 }
2301
2302 /**
2303 * Returns the next content part from the $filecontent
2304 *
2305 * @param string File content string
2306 * @param integer File pointer (where to read from)
2307 * @param boolean If set, the returned content is unserialized into an array, otherwise you get the raw string
2308 * @param string For error messages this indicates the section of the problem.
2309 * @return string Data string
2310 */
2311 function getNextContentPart($filecontent,&$pointer,$unserialize=0,$name='') {
2312 $initStrLen = 32+1+1+1+10+1;
2313 // getting header data
2314 $initStr = substr($filecontent,$pointer,$initStrLen);
2315 $pointer+= $initStrLen;
2316 $initStrDat = explode(':',$initStr);
2317 if (!strcmp($initStrDat[3],'')) {
2318 $datString = substr($filecontent,$pointer,intval($initStrDat[2]));
2319 $pointer+= intval($initStrDat[2])+1;
2320 if (!strcmp(md5($datString),$initStrDat[0])) {
2321 if ($initStrDat[1]) {
2322 if ($this->compress) {
2323 $datString = gzuncompress($datString);
2324 } else $this->error('Content read error: This file requires decompression, but this server does not offer gzcompress()/gzuncompress() functions.',1);
2325 }
2326 return $unserialize ? unserialize($datString) : $datString;
2327 } else $this->error('MD5 check failed ('.$name.')');
2328 } else $this->error('Content read error: InitString had a wrong length. ('.$name.')');
2329 }
2330
2331 /**
2332 * Setting up the object based on the recently loaded ->dat array
2333 *
2334 * @return void
2335 */
2336 function loadInit() {
2337 $this->relStaticTables = (array)$this->dat['header']['relStaticTables'];
2338 $this->excludeMap = (array)$this->dat['header']['excludeMap'];
2339
2340 $this->softrefCfg = (array)$this->dat['header']['softrefCfg'];
2341 $this->extensionDependencies = (array)$this->dat['header']['extensionDependencies'];
2342
2343 $this->fixCharsets();
2344 }
2345
2346 /**
2347 * Fix charset of import memory if different from system charset
2348 *
2349 * @return void
2350 * @see loadInit()
2351 */
2352 function fixCharsets() {
2353 global $LANG;
2354
2355 $importCharset = $this->dat['header']['charset'];
2356 if ($importCharset) {
2357 if ($importCharset!==$LANG->charSet) {
2358 $this->error('CHARSET: Converting charset of input file ('.$importCharset.') to the system charset ('.$LANG->charSet.')');
2359
2360 // convert meta data:
2361 if (is_array($this->dat['header']['meta'])) {
2362 $LANG->csConvObj->convArray($this->dat['header']['meta'],$importCharset,$LANG->charSet);
2363 }
2364 // convert record headers:
2365 if (is_array($this->dat['header']['records'])) {
2366 $LANG->csConvObj->convArray($this->dat['header']['records'],$importCharset,$LANG->charSet);
2367 }
2368 // convert records themselves:
2369 if (is_array($this->dat['records'])) {
2370 $LANG->csConvObj->convArray($this->dat['records'],$importCharset,$LANG->charSet);
2371 }
2372 }
2373 } else {
2374 $this->error('CHARSET: No charset found in import file!');
2375 }
2376 }
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396 /********************************************************
2397 *
2398 * Visual rendering of import/export memory, $this->dat
2399 *
2400 ********************************************************/
2401
2402 /**
2403 * Displays an overview of the header-content.
2404 *
2405 * @return string HTML content
2406 */
2407 function displayContentOverview() {
2408 global $LANG;
2409
2410 // Check extension dependencies:
2411 if (is_array($this->dat['header']['extensionDependencies'])) {
2412 foreach($this->dat['header']['extensionDependencies'] as $extKey) {
2413 if (!t3lib_extMgm::isLoaded($extKey)) {
2414 $this->error('DEPENDENCY: The extension with key "'.$extKey.'" must be installed!');
2415 }
2416 }
2417 }
2418
2419 // Probably this is done to save memory space?
2420 unset($this->dat['files']);
2421
2422 // Traverse header:
2423 $this->remainHeader = $this->dat['header'];
2424 if (is_array($this->remainHeader)) {
2425
2426 // If there is a page tree set, show that:
2427 if (is_array($this->dat['header']['pagetree'])) {
2428 reset($this->dat['header']['pagetree']);
2429 $lines = array();
2430 $this->traversePageTree($this->dat['header']['pagetree'],$lines);
2431
2432 $rows = array();
2433 $rows[] = '
2434 <tr class="bgColor5 tableheader">
2435 <td>'.$LANG->getLL('impexpcore_displaycon_controls',1).'</td>
2436 <td>'.$LANG->getLL('impexpcore_displaycon_title',1).'</td>
2437 <td>'.$LANG->getLL('impexpcore_displaycon_size',1).'</td>
2438 <td>'.$LANG->getLL('impexpcore_displaycon_message',1).'</td>
2439 '.($this->update ? '<td>'.$LANG->getLL('impexpcore_displaycon_updateMode',1).'</td>' : '').'
2440 '.($this->update ? '<td>'.$LANG->getLL('impexpcore_displaycon_currentPath',1).'</td>' : '').'
2441 '.($this->showDiff ? '<td>'.$LANG->getLL('impexpcore_displaycon_result',1).'</td>' : '').'
2442 </tr>';
2443
2444 foreach($lines as $r) {
2445 $rows[] = '
2446 <tr class="'.$r['class'].'">
2447 <td>'.$this->renderControls($r).'</td>
2448 <td nowrap="nowrap">'.$r['preCode'].$r['title'].'</td>
2449 <td nowrap="nowrap">'.t3lib_div::formatSize($r['size']).'</td>
2450 <td nowrap="nowrap">'.($r['msg'] && !$this->doesImport ? '<span class="typo3-red">'.htmlspecialchars($r['msg']).'</span>' : '').'</td>
2451 '.($this->update ? '<td nowrap="nowrap">'.$r['updateMode'].'</td>' : '').'
2452 '.($this->update ? '<td nowrap="nowrap">'.$r['updatePath'].'</td>' : '').'
2453 '.($this->showDiff ? '<td>'.$r['showDiffContent'].'</td>' : '').'
2454 </tr>';
2455 }
2456
2457 $out = '
2458 <strong>'.$LANG->getLL('impexpcore_displaycon_insidePagetree',1).'</strong>
2459 <br /><br />
2460 <table border="0" cellpadding="0" cellspacing="1">'.implode('',$rows).'</table>
2461 <br /><br />';
2462 }
2463
2464 // Print remaining records that were not contained inside the page tree:
2465 $lines = array();
2466 if (is_array($this->remainHeader['records'])) {
2467 if (is_array($this->remainHeader['records']['pages'])) {
2468 $this->traversePageRecords($this->remainHeader['records']['pages'], $lines);
2469 }
2470 $this->traverseAllRecords($this->remainHeader['records'], $lines);
2471
2472 if (count($lines)) {
2473 $rows = array();
2474 $rows[] = '
2475 <tr class="bgColor5 tableheader">
2476 <td>'.$LANG->getLL('impexpcore_displaycon_controls',1).'</td>
2477 <td>'.$LANG->getLL('impexpcore_displaycon_title',1).'</td>
2478 <td>'.$LANG->getLL('impexpcore_displaycon_size',1).'</td>
2479 <td>'.$LANG->getLL('impexpcore_displaycon_message',1).'</td>
2480 '.($this->update ? '<td>'.$LANG->getLL('impexpcore_displaycon_updateMode',1).'</td>' : '').'
2481 '.($this->update ? '<td>'.$LANG->getLL('impexpcore_displaycon_currentPath',1).'</td>' : '').'
2482 '.($this->showDiff ? '<td>'.$LANG->getLL('impexpcore_displaycon_result',1).'</td>' : '').'
2483 </tr>';
2484
2485 foreach($lines as $r) {
2486 $rows[] = '<tr class="'.$r['class'].'">
2487 <td>'.$this->renderControls($r).'</td>
2488 <td nowrap="nowrap">'.$r['preCode'].$r['title'].'</td>
2489 <td nowrap="nowrap">'.t3lib_div::formatSize($r['size']).'</td>
2490 <td nowrap="nowrap">'.($r['msg'] && !$this->doesImport ? '<span class="typo3-red">'.htmlspecialchars($r['msg']).'</span>' : '').'</td>
2491 '.($this->update ? '<td nowrap="nowrap">'.$r['updateMode'].'</td>' : '').'
2492 '.($this->update ? '<td nowrap="nowrap">'.$r['updatePath'].'</td>' : '').'
2493 '.($this->showDiff ? '<td>'.$r['showDiffContent'].'</td>' : '').'
2494 </tr>';
2495 }
2496
2497 $out.= '
2498 <strong>'.$LANG->getLL('impexpcore_singlereco_outsidePagetree',1).'</strong>
2499 <br /><br />
2500 <table border="0" cellpadding="0" cellspacing="1">'.implode('',$rows).'</table>';
2501 }
2502 }
2503 }
2504 return $out;
2505 }
2506
2507 /**
2508 * Go through page tree for display
2509 *
2510 * @param array Page tree array with uid/subrow (from ->dat[header][pagetree]
2511 * @param array Output lines array (is passed by reference and modified)
2512 * @param string Pre-HTML code
2513 * @return void
2514 */
2515 function traversePageTree($pT,&$lines,$preCode='') {
2516 reset($pT);
2517 while(list($k,$v) = each($pT)) {
2518
2519 // Add this page:
2520 $this->singleRecordLines('pages',$k,$lines,$preCode);
2521
2522 // Subrecords:
2523 if (is_array($this->dat['header']['pid_lookup'][$k])) {
2524 reset($this->dat['header']['pid_lookup'][$k]);
2525 while(list($t,$recUidArr)=each($this->dat['header']['pid_lookup'][$k])) {
2526 if ($t!='pages') {
2527 reset($recUidArr);
2528 while(list($ruid)=each($recUidArr)) {
2529 $this->singleRecordLines($t,$ruid,$lines,$preCode.'&nbsp;&nbsp;&nbsp;&nbsp;');
2530 }
2531 }
2532 }
2533 unset($this->remainHeader['pid_lookup'][$k]);
2534 }
2535
2536 // Subpages, called recursively:
2537 if (is_array($v['subrow'])) {
2538 $this->traversePageTree($v['subrow'],$lines,$preCode.'&nbsp;&nbsp;&nbsp;&nbsp;');
2539 }
2540 }
2541 }
2542
2543 /**
2544 * Go through remaining pages (not in tree)
2545 *
2546 * @param array Page tree array with uid/subrow (from ->dat[header][pagetree]
2547 * @param array Output lines array (is passed by reference and modified)
2548 * @return void
2549 */
2550 function traversePageRecords($pT,&$lines) {
2551 reset($pT);
2552 while(list($k,$rHeader)=each($pT)) {
2553 $this->singleRecordLines('pages',$k,$lines,'',1);
2554 // Subrecords:
2555 if (is_array($this->dat['header']['pid_lookup'][$k])) {
2556 reset($this->dat['header']['pid_lookup'][$k]);
2557 while(list($t,$recUidArr)=each($this->dat['header']['pid_lookup'][$k])) {
2558 if ($t!='pages') {
2559 reset($recUidArr);
2560 while(list($ruid)=each($recUidArr)) {
2561 $this->singleRecordLines($t,$ruid,$lines,'&nbsp;&nbsp;&nbsp;&nbsp;');
2562 }
2563 }
2564 }
2565 unset($this->remainHeader['pid_lookup'][$k]);
2566 }
2567 }
2568 }
2569
2570 /**
2571 * Go through ALL records (if the pages are displayed first, those will not be amoung these!)
2572 *
2573 * @param array Page tree array with uid/subrow (from ->dat[header][pagetree]
2574 * @param array Output lines array (is passed by reference and modified)
2575 * @return void
2576 */
2577 function traverseAllRecords($pT,&$lines) {
2578 reset($pT);
2579 while(list($t,$recUidArr)=each($pT)) {
2580 if ($t!='pages') {
2581 reset($recUidArr);
2582 while(list($ruid)=each($recUidArr)) {
2583 $this->singleRecordLines($t,$ruid,$lines,$preCode,1);
2584 }
2585 }
2586 }
2587 }
2588
2589 /**
2590 * Add entries for a single record
2591 *
2592 * @param string Table name
2593 * @param integer Record uid
2594 * @param array Output lines array (is passed by reference and modified)
2595 * @param string Pre-HTML code
2596 * @param boolean If you want import validation, you can set this so it checks if the import can take place on the specified page.
2597 * @return void
2598 */
2599 function singleRecordLines($table,$uid,&$lines,$preCode,$checkImportInPidRecord=0) {
2600 global $TCA,$BE_USER,$LANG;
2601
2602 // Get record:
2603 $record = $this->dat['header']['records'][$table][$uid];
2604 unset($this->remainHeader['records'][$table][$uid]);
2605 if (!is_array($record) && !($table==='pages' && !$uid)) $this->error('MISSING RECORD: '.$table.':'.$uid,1);
2606
2607 // Begin to create the line arrays information record, pInfo:
2608 $pInfo = array();
2609 $pInfo['ref'] = $table.':'.$uid;
2610 if ($table==='_SOFTREF_') { // Unknown table name:
2611 $pInfo['preCode'] = $preCode;
2612 $pInfo['title'] = '<em>'.$LANG->getLL('impexpcore_singlereco_softReferencesFiles',1).'</em>';
2613 } elseif (!isset($TCA[$table])) { // Unknown table name:
2614 $pInfo['preCode'] = $preCode;
2615 $pInfo['msg'] = "UNKNOWN TABLE '".$pInfo['ref']."'";
2616 $pInfo['title'] = '<em>'.htmlspecialchars($record['title']).'</em>';
2617 } else { // Otherwise, set table icon and title.
2618
2619 // Import Validation (triggered by $this->display_import_pid_record) will show messages if import is not possible of various items.
2620 if (is_array($this->display_import_pid_record)) {
2621 if ($checkImportInPidRecord) {
2622 if (!$BE_USER->doesUserHaveAccess($this->display_import_pid_record, $table=='pages'?8:16)) {
2623 $pInfo['msg'].="'".$pInfo['ref']."' cannot be INSERTED on this page! ";
2624 }
2625 if (!$this->checkDokType($table, $this->display_import_pid_record['doktype']) && !$TCA[$table]['ctrl']['rootLevel']) {
2626 $pInfo['msg'].="'".$table."' cannot be INSERTED on this page type (change to 'sysFolder'!) ";
2627 }
2628 }
2629 if (!$BE_USER->check('tables_modify',$table)) {$pInfo['msg'].="You are not allowed to CREATE '".$table."' tables! ";}
2630
2631 if ($TCA[$table]['ctrl']['readOnly']) {$pInfo['msg'].="TABLE '".$table."' is READ ONLY! ";}
2632 if ($TCA[$table]['ctrl']['adminOnly'] && !$BE_USER->isAdmin()) {$pInfo['msg'].="TABLE '".$table."' is ADMIN ONLY! ";}
2633 if ($TCA[$table]['ctrl']['is_static']) {$pInfo['msg'].="TABLE '".$table."' is a STATIC TABLE! ";}
2634 if ($TCA[$table]['ctrl']['rootLevel']) {$pInfo['msg'].="TABLE '".$table."' will be inserted on ROOT LEVEL! ";}
2635
2636 $diffInverse = FALSE;
2637 if ($this->update) {
2638 $diffInverse = TRUE; // In case of update-PREVIEW we swap the diff-sources.
2639 $recInf = $this->doesRecordExist($table, $uid, $this->showDiff ? '*' : '');
2640 $pInfo['updatePath']= $recInf ? htmlspecialchars($this->getRecordPath($recInf['pid'])) : '<b>NEW!</b>';
2641
2642 // Mode selector:
2643 $optValues = array();
2644 $optValues[] = $recInf ? $LANG->getLL('impexpcore_singlereco_update') : $LANG->getLL('impexpcore_singlereco_insert');
2645 if ($recInf) $optValues['as_new'] = $LANG->getLL('impexpcore_singlereco_importAsNew');
2646 if ($recInf) {
2647 if (!$this->global_ignore_pid) {
2648 $optValues['ignore_pid'] = $LANG->getLL('impexpcore_singlereco_ignorePid');
2649 } else {
2650 $optValues['respect_pid'] = $LANG->getLL('impexpcore_singlereco_respectPid');
2651 }
2652 }
2653 if (!$recInf && $GLOBALS['BE_USER']->isAdmin()) $optValues['force_uid'] = sprintf($LANG->getLL('impexpcore_singlereco_forceUidSAdmin'),$uid);
2654 $optValues['exclude'] = $LANG->getLL('impexpcore_singlereco_exclude');
2655
2656 $pInfo['updateMode'] = $this->renderSelectBox('tx_impexp[import_mode]['.$table.':'.$uid.']',$this->import_mode[$table.':'.$uid],$optValues);
2657 }
2658
2659 // Diff vieiw:
2660 if ($this->showDiff) {
2661 // For IMPORTS, get new id:
2662 if ($newUid = $this->import_mapId[$table][$uid]) {
2663 $diffInverse = FALSE;
2664 $recInf = $this->doesRecordExist($table, $newUid, '*');
2665 t3lib_BEfunc::workspaceOL($table,$recInf);
2666 }
2667 if (is_array($recInf)) {
2668 $pInfo['showDiffContent'] = $this->compareRecords($recInf, $this->dat['records'][$table.':'.$uid]['data'], $table, $diffInverse);
2669 }
2670 }
2671 }
2672
2673 $pInfo['preCode'] = $preCode.t3lib_iconworks::getIconImage($table,$this->dat['records'][$table.':'.$uid]['data'],$GLOBALS['BACK_PATH'],'align="top" title="'.htmlspecialchars($table.':'.$uid).'"');
2674 $pInfo['title'] = htmlspecialchars($record['title']);
2675
2676 // View page:
2677 if ($table==='pages') {
2678 $viewID = $this->mode === 'export' ? $uid : ($this->doesImport ? $this->import_mapId['pages'][$uid] : 0);
2679 if ($viewID) {
2680 $pInfo['title'] = '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($viewID, $GLOBALS['BACK_PATH'])).'return false;">'.$pInfo['title'].'</a>';
2681 }
2682 }
2683 }
2684 $pInfo['class'] = $table=='pages' ? 'bgColor4-20' : 'bgColor4';
2685 $pInfo['type'] = 'record';
2686 $pInfo['size'] = $record['size'];
2687 $lines[] = $pInfo;
2688
2689 // File relations:
2690 if (is_array($record['filerefs'])) {
2691 $this->addFiles($record['filerefs'],$lines,$preCode);
2692 }
2693
2694 // DB relations
2695 if (is_array($record['rels'])) {
2696 $this->addRelations($record['rels'],$lines,$preCode);
2697 }
2698
2699 // Soft ref
2700 if (count($record['softrefs'])) {
2701 $preCode_A = $preCode.'&nbsp;&nbsp;&nbsp;&nbsp;';
2702 $preCode_B = $preCode.'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
2703 foreach($record['softrefs'] as $info) {
2704 $pInfo = array();
2705 $pInfo['preCode'] = $preCode_A.'<img'.t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'],'gfx/rel_softref.png','width="13" height="12"').' align="top" alt="" />';
2706 $pInfo['title'] = '<em>'.$info['field'].', "'.$info['spKey'].'" </em>: <span title="'.htmlspecialchars($info['matchString']).'">'.htmlspecialchars(t3lib_div::fixed_lgd_cs($info['matchString'],60)).'</span>';
2707 if ($info['subst']['type']) {
2708 if (strlen($info['subst']['title'])) {
2709 $pInfo['title'].= '<br/>'.$preCode_B.'<b>'.$LANG->getLL('impexpcore_singlereco_title',1).'</b> '.htmlspecialchars(t3lib_div::fixed_lgd_cs($info['subst']['title'],60));
2710 }
2711 if (strlen($info['subst']['description'])) {
2712 $pInfo['title'].= '<br/>'.$preCode_B.'<b>'.$LANG->getLL('impexpcore_singlereco_descr',1).'</b> '.htmlspecialchars(t3lib_div::fixed_lgd_cs($info['subst']['description'],60));
2713 }
2714 $pInfo['title'].= '<br/>'.$preCode_B.
2715 ($info['subst']['type'] == 'file' ? $LANG->getLL('impexpcore_singlereco_filename',1).' <b>'.$info['subst']['relFileName'].'</b>' : '').
2716 ($info['subst']['type'] == 'string' ? $LANG->getLL('impexpcore_singlereco_value',1).' <b>'.$info['subst']['tokenValue'].'</b>' : '').
2717 ($info['subst']['type'] == 'db' ? $LANG->getLL('impexpcore_softrefsel_record',1).' <b>'.$info['subst']['recordRef'].'</b>' : '');
2718 }
2719 $pInfo['ref'] = 'SOFTREF';
2720 $pInfo['size'] = '';
2721 $pInfo['class'] = 'bgColor3';
2722 $pInfo['type'] = 'softref';
2723 $pInfo['_softRefInfo'] = $info;
2724 $pInfo['type'] = 'softref';
2725 if ($info['error'] && !t3lib_div::inList('editable,exclude',$this->softrefCfg[$info['subst']['tokenID']]['mode'])) {
2726 $pInfo['msg'].= $info['error'];
2727 }
2728 $lines[] = $pInfo;
2729
2730 // Add relations:
2731 if ($info['subst']['type'] == 'db') {
2732 list($tempTable, $tempUid) = explode(':', $info['subst']['recordRef']);
2733 $this->addRelations(array(array('table' => $tempTable, 'id' => $tempUid, 'tokenID' => $info['subst']['tokenID'])),$lines,$preCode_B,array(), '');
2734 }
2735
2736 // Add files:
2737 if ($info['subst']['type'] == 'file') {
2738 #debug($info);
2739 $this->addFiles(array($info['file_ID']),$lines,$preCode_B, '', $info['subst']['tokenID']);
2740 }
2741 }
2742 }
2743 }
2744
2745 /**
2746 * Add DB relations entries for a record's rels-array
2747 *
2748 * @param array Array of relations
2749 * @param array Output lines array (is passed by reference and modified)
2750 * @param string Pre-HTML code
2751 * @param array Recursivity check stack
2752 * @param string Alternative HTML color class to use.
2753 * @return void
2754 * @access private
2755 * @see singleRecordLines()
2756 */
2757 function addRelations($rels,&$lines,$preCode,$recurCheck=array(),$htmlColorClass='') {
2758
2759 foreach($rels as $dat) {
2760 $table = $dat['table'];
2761 $uid = $dat['id'];
2762 $pInfo = array();
2763 $Iprepend = '';
2764 $staticFixed = FALSE;
2765 $pInfo['ref'] = $table.':'.$uid;
2766 if (!in_array($pInfo['ref'],$recurCheck)) {
2767 if ($uid > 0) {
2768 $record = $this->dat['header']['records'][$table][$uid];
2769 if (!is_array($record)) {
2770 if ($this->isTableStatic($table) || $this->isExcluded($table, $uid) || ($dat['tokenID'] && !$this->includeSoftref($dat['tokenID']))) {
2771 $pInfo['title'] = htmlspecialchars('STATIC: '.$pInfo['ref']);
2772 $Iprepend = '_static';
2773 $staticFixed = TRUE;
2774 } else {
2775 $doesRE = $this->doesRecordExist($table,$uid);
2776 $lostPath = $this->getRecordPath($table==='pages' ? $doesRE['uid'] : $doesRE['pid']);
2777
2778 $pInfo['title'] = htmlspecialchars($pInfo['ref']);
2779 $pInfo['title'] = '<span title="'.htmlspecialchars($lostPath).'">'.$pInfo['title'].'</span>';
2780
2781 $pInfo['msg'] = 'LOST RELATION'.(!$doesRE ? ' (Record not found!)' : ' (Path: '.$lostPath.')');
2782 $Iprepend = '_lost';
2783 # debug('MISSING relation: '.$table.':'.$uid,1);
2784 }
2785 } else {
2786 $pInfo['title'] = htmlspecialchars($record['title']);
2787 $pInfo['title'] = '<span title="'.htmlspecialchars($this->getRecordPath($table==='pages' ? $record['uid'] : $record['pid'])).'">'.$pInfo['title'].'</span>';
2788
2789 # $pInfo['size'] = $record['size'];
2790 }
2791 } else { // 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
2792 $pInfo['title'] = htmlspecialchars('FIXED: '.$pInfo['ref']);
2793 $staticFixed = TRUE;
2794 }
2795
2796 $pInfo['preCode'] = $preCode.'&nbsp;&nbsp;&nbsp;&nbsp;<img'.t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'],'gfx/rel_db'.$Iprepend.'.gif','width="13" height="12"').' align="top" title="'.htmlspecialchars($pInfo['ref']).'" alt="" />';
2797 $pInfo['class'] = $htmlColorClass ? $htmlColorClass : 'bgColor3';
2798 $pInfo['type'] = 'rel';
2799
2800 if (!$staticFixed || $this->showStaticRelations) {
2801 $lines[] = $pInfo;
2802 if (is_array($record) && is_array($record['rels'])) {
2803 $this->addRelations($record['rels'], $lines, $preCode.'&nbsp;&nbsp;', array_merge($recurCheck,array($pInfo['ref'])), $htmlColorClass);
2804 }
2805 }
2806 } else $this->error($pInfo['ref'].' was recursive...');
2807 }
2808 }
2809
2810