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