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