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