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