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