*** empty log message ***
[Packages/TYPO3.CMS.git] / typo3 / sysext / impexp / class.tx_impexp.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2004 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * TRD file Import/Export library (TYPO3 Record Document)
29 *
30 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
31 */
32 /**
33 * [CLASS/FUNCTION INDEX of SCRIPT]
34 *
35 *
36 *
37 * 98: class tx_impexp
38 * 124: function init($dontCompress=0)
39 * 140: function setMetaData($title,$description,$notes,$packager_username,$packager_name,$packager_email)
40 * 157: function addThumbnail($imgFilepath)
41 * 179: function setPageTree($idH)
42 * 191: function flatInversePageTree($idH,$a=array())
43 * 212: function export_addRecord($table,$row,$relationLevel=0)
44 * 247: function export_addDBRelations($relationLevel=0)
45 * 303: function export_addFilesFromRelations()
46 * 350: function getRelations($table,$row)
47 * 423: function flatDBrels($dbrels)
48 * 445: function loadContent($filecontent)
49 * 462: function getNextContentPart($filecontent,&$pointer,$unserialize=0,$name='')
50 * 489: function loadFile($filename,$all=0)
51 * 510: function getNextFilePart($fd,$unserialize=0,$name='')
52 * 535: function compileMemoryToFileContent()
53 * 556: function doOutputCompress()
54 * 567: function addFilePart($data,$compress=0)
55 * 578: function error($msg)
56 * 587: function printErrorLog()
57 * 597: function destPathFromUploadFolder ($folder)
58 * 616: function importData($pid)
59 * 720: function getNewTCE()
60 * 734: function setRelations()
61 * 785: function addToMapId($substNEWwithIDs)
62 * 804: function initImportVars()
63 * 815: function unlinkTempFiles()
64 * 838: function addSingle($table,$uid,$pid)
65 * 878: function import_addFileNameToBeCopied($fI)
66 * 902: function getTempPathFileName($fN)
67 * 923: function displayContentOverview ()
68 * 993: function traversePageTree($pT,&$lines,$preCode='')
69 * 1022: function traversePageRecords($pT,&$lines)
70 * 1049: function traverseallrecords($pT,&$lines)
71 * 1071: function singleRecordLines($table,$uid,&$lines,$preCode,$checkImportInPidRecord=0)
72 * 1138: function addRelations($rels,&$lines,$preCode,$recurCheck=array())
73 * 1180: function checkDokType($checkTable,$doktype)
74 *
75 * TOTAL FUNCTIONS: 36
76 * (This index is automatically created/updated by the extension 'extdeveval")
77 *
78 */
79
80 require_once(PATH_t3lib.'class.t3lib_tcemain.php');
81
82
83
84
85
86
87
88
89
90
91 /**
92 * TRD file Import/Export library (TYPO3 Record Document)
93 *
94 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
95 * @package TYPO3
96 * @subpackage tx_impexp
97 */
98 class tx_impexp {
99 var $maxFileSize = 1000000; // 1MB max file size
100 var $maxRecordSize = 1000000; // 1MB max record size
101 var $maxExportSize = 10000000; // 10MB max export size
102
103 var $dat = array();
104 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.
105
106
107
108 // Internal, dynamic:
109 var $import_mapId = array();
110 var $import_newId = array();
111 var $import_newId_pids = array();
112 var $errorLog = array();
113
114 var $relExclTables = array(); // add table names here which will not be included into export if found as relations.
115 var $relOnlyTables = array(); // add table names here which are THE ONLY ones which will be included into export if found as relations. (activated if array is not empty)
116
117 var $compress=0;
118 var $dontCompress=0;
119
120
121 /**
122 * Init
123 *
124 * @param [type] $dontCompress: ...
125 * @return [type] ...
126 */
127 function init($dontCompress=0) {
128 $this->compress = function_exists('gzcompress');
129 $this->dontCompress = $dontCompress;
130 }
131
132 /**
133 * Sets meta data
134 *
135 * @param [type] $title: ...
136 * @param [type] $description: ...
137 * @param [type] $notes: ...
138 * @param [type] $packager_username: ...
139 * @param [type] $packager_name: ...
140 * @param [type] $packager_email: ...
141 * @return [type] ...
142 */
143 function setMetaData($title,$description,$notes,$packager_username,$packager_name,$packager_email) {
144 $this->dat['header']['meta']=array(
145 'title'=>$title,
146 'description'=>$description,
147 'notes'=>$notes,
148 'packager_username'=>$packager_username,
149 'packager_name'=>$packager_name,
150 'packager_email'=>$packager_email
151 );
152 }
153
154 /**
155 * Sets a thumbnail image to the exported file
156 *
157 * @param [type] $imgFilepath: ...
158 * @return [type] ...
159 */
160 function addThumbnail($imgFilepath) {
161 if (@is_file($imgFilepath)) {
162 $imgInfo = @getimagesize($imgFilepath);
163 if (is_array($imgInfo)) {
164 $fileContent = t3lib_div::getUrl($imgFilepath);
165 $this->dat['header']['thumbnail']=array(
166 'imgInfo' => $imgInfo,
167 'content' => $fileContent,
168 'filesize' => strlen($fileContent),
169 'filemtime' => filemtime($imgFilepath),
170 'filename' => basename($imgFilepath)
171 );
172 }
173 }
174 }
175
176 /**
177 * Sets the page-tree array in the header and returns the array in a flattend version
178 *
179 * @param [type] $idH: ...
180 * @return [type] ...
181 */
182 function setPageTree($idH) {
183 $this->dat['header']['pagetree']=$idH;
184 return $this->flatInversePageTree($idH);
185 }
186
187 /**
188 * Recursively flattening the idH array (for setPageTree() function)
189 *
190 * @param [type] $idH: ...
191 * @param [type] $a: ...
192 * @return [type] ...
193 */
194 function flatInversePageTree($idH,$a=array()) {
195 if (is_array($idH)) {
196 $idH = array_reverse($idH);
197 reset($idH);
198 while(list($k,$v)=each($idH)) {
199 $a[$v['uid']]=$v['uid'];
200 if (is_array($v['subrow'])) $a=$this->flatInversePageTree($v['subrow'],$a);
201 }
202 }
203 return $a;
204 }
205
206 /**
207 * Adds the record $row from $table.
208 * No checking for relations done here. Pure data.
209 *
210 * @param [type] $table: ...
211 * @param [type] $row: ...
212 * @param [type] $relationLevel: ...
213 * @return [type] ...
214 */
215 function export_addRecord($table,$row,$relationLevel=0) {
216 if (strcmp($table,'') && is_array($row) && $row['uid']>0) {
217 if (!isset($this->dat['records'][$table.':'.$row['uid']])) {
218 // header info:
219 $headerInfo=array();
220 $headerInfo['uid']=$row['uid'];
221 $headerInfo['pid']=$row['pid'];
222 $headerInfo['title']=t3lib_div::fixed_lgd_cs(t3lib_BEfunc::getRecordTitle($table,$row),40);
223 $headerInfo['size']=strlen(serialize($row));
224 if ($relationLevel) $headerInfo['relationLevel'] = $relationLevel;
225 if ($headerInfo['size']<$this->maxRecordSize) {
226 $this->dat['header']['records'][$table][$row['uid']]=$headerInfo;
227
228 // pid lookup:
229 $this->dat['header']['pid_lookup'][$row['pid']][$table][$row['uid']]=1;
230
231 // data:
232 $this->dat['records'][$table.':'.$row['uid']]=array();
233 $this->dat['records'][$table.':'.$row['uid']]['data']=$row;
234 $this->dat['records'][$table.':'.$row['uid']]['rels']=$this->getRelations($table,$row);
235
236 $this->dat['header']['records'][$table][$row['uid']]['rels']=$this->flatDBrels($this->dat['records'][$table.':'.$row['uid']]['rels']);
237 } else $this->error('Record '.$table.':'.$row['uid'].' was larger than maxRecordSize ('.t3lib_div::formatSize($this->maxRecordSize).')');
238 } else $this->error('Record '.$table.':'.$row['uid'].' already added.');
239 }
240 }
241
242 /**
243 * This analyses the existing added records, finds all database relations to records and adds these records to the export file.
244 * This function can be called recursively until it returns an empty array. In principle it should not allow to infinite recursivity, but you better set a limit...
245 * Call this BEFORE the ext_addFilesFromRelations
246 *
247 * @param [type] $relationLevel: ...
248 * @return [type] ...
249 */
250 function export_addDBRelations($relationLevel=0) {
251 global $TCA;
252 #echo "<HR>";
253 $addR=array();
254 if (is_array($this->dat['records'])) {
255 reset($this->dat['records']);
256 while(list($k)=each($this->dat['records'])) {
257 if (is_array($this->dat['records'][$k])) {
258 reset($this->dat['records'][$k]['rels']);
259 while(list($fieldname,$vR)=each($this->dat['records'][$k]['rels'])) {
260 if ($vR['type']=='db') {
261 reset($vR['itemArray']);
262 while(list(,$fI)=each($vR['itemArray'])) {
263 $rId = $fI['table'].':'.$fI['id'];
264 if (isset($TCA[$fI['table']]) && !$TCA[$fI['table']]['ctrl']['is_static']
265 && !in_array($fI['table'],$this->relExclTables)
266 && (!count($this->relOnlyTables) || in_array($fI['table'],$this->relOnlyTables))
267 ) {
268 if (isset($this->dat['records'][$rId])) {
269 # debug($rId.": OK",1);
270 } else {
271 # debug($rId.": --",1);
272 $addR[$rId]=$fI;
273 }
274 }
275 }
276 }
277 }
278 }
279 }
280 } else $this->error('There were no records available.');
281
282 #debug($addR);
283 if (count($addR)) {
284 reset($addR);
285 while(list(,$fI)=each($addR)) {
286 $row = t3lib_BEfunc::getRecord($fI['table'],$fI['id']);
287 if (is_array($row)) {
288 $this->export_addRecord($fI['table'],$row,$relationLevel+1);
289 }
290 $rId = $fI['table'].':'.$fI['id'];
291 if (!isset($this->dat['records'][$rId])) {
292 $this->dat['records'][$rId]='NOT_FOUND';
293 $this->error('Relation record '.$rId.' was not found!');
294 }
295 }
296 }
297 return $addR;
298 }
299
300 /**
301 * This adds all files in relations.
302 * Call this method AFTER adding all records including relations.
303 *
304 * @return [type] ...
305 */
306 function export_addFilesFromRelations() {
307 if (is_array($this->dat['records'])) {
308 reset($this->dat['records']);
309 while(list($k)=each($this->dat['records'])) {
310 if (is_array($this->dat['records'][$k]['rels'])) {
311 reset($this->dat['records'][$k]['rels']);
312 while(list($fieldname,$vR)=each($this->dat['records'][$k]['rels'])) {
313 if ($vR['type']=='file') {
314 reset($vR['newValueFiles']);
315 while(list(,$fI)=each($vR['newValueFiles'])) {
316 if (@is_file($fI['ID_absFile'])) {
317 if (filesize($fI['ID_absFile'])<$this->maxFileSize) {
318 $fileRec=array();
319 $fileRec['filesize']=filesize($fI['ID_absFile']);
320 $fileRec['filename']=basename($fI['ID_absFile']);
321 $fileRec['filemtime']=filemtime($fI['ID_absFile']);
322 $fileRec['record_ref']=$k.'/'.$fieldname;
323
324 // Setting this data in the header
325 $this->dat['header']['files'][$fI['ID']]=$fileRec;
326
327 // ... and for the recordlisting, why not let us know WHICH relations there was...
328 $refParts=explode(':',$k,2);
329 if (!is_array($this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'])) $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs']=array();
330 $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'][]=$fI['ID'];
331
332 // ... and finally add the heavy stuff:
333 $fileRec['content']=t3lib_div::getUrl($fI['ID_absFile']);
334 $fileRec['content_md5']=md5($fileRec['content']);
335 $this->dat['files'][$fI['ID']] = $fileRec;
336 } else $this->error($fI['ID_absFile'].' was larger than the maxFileSize ('.t3lib_div::formatSize($this->maxFileSize).')! Skipping.');
337 } else $this->error($fI['ID_absFile'].' was not a file! Skipping.');
338 }
339 }
340 }
341 }
342 }
343 } else $this->error('There were no records available.');
344 }
345
346 /**
347 * Returns relation information for a $table/$row-array
348 *
349 * @param [type] $table: ...
350 * @param [type] $row: ...
351 * @return [type] ...
352 */
353 function getRelations($table,$row) {
354 global $TCA;
355 t3lib_div::loadTCA($table);
356 $uid=$row['uid'];
357 $nonFields = explode(',','uid,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,pid');
358
359 $outRow=array();
360 reset($row);
361 while (list($field,$value)=each($row)) {
362 if (!in_array($field,$nonFields) && is_array($TCA[$table]['columns'][$field])) {
363 $conf = $TCA[$table]['columns'][$field]['config'];
364
365 // Take care of files...
366 if ($conf['type']=='group' && $conf['internal_type']=='file') {
367 if ($conf['MM']) {
368 $theFileValues=array();
369 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
370 $dbAnalysis->start('','files',$conf['MM'],$uid);
371 reset($dbAnalysis->itemArray);
372 while (list($somekey,$someval)=each($dbAnalysis->itemArray)) {
373 // debug($someval['id']);
374 if ($someval['id']) {
375 $theFileValues[]=$someval['id'];
376 }
377 }
378 } else {
379 $theFileValues = explode(',',$value);
380 }
381 // debug($theFileValues);
382 reset($theFileValues);
383 $uploadFolder = $conf['uploadfolder'];
384 $dest = $this->destPathFromUploadFolder($uploadFolder);
385 $newValue = array();
386 $newValueFiles = array();
387 while (list(,$file)=each($theFileValues)) {
388 if (trim($file)) {
389 $realFile = $dest.'/'.trim($file);
390 if (@is_file($realFile)) {
391 $newValueFiles[] = array('fieldvalue'=>$file,'ID'=>md5($realFile),'ID_absFile'=>$realFile); // the order should be preserved here because
392 } else $this->error('Missing file: '.$realFile);
393 }
394 }
395 $outRow[$field]=array(
396 'type'=>'file',
397 'newValueFiles'=>$newValueFiles,
398 );
399 }
400 // db record lists:
401 if (($conf['type']=='group' && $conf['internal_type']=='db') || ($conf['type']=='select' && $conf['foreign_table'])) {
402 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table'];
403 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
404
405 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
406 $dbAnalysis->start($value,$allowedTables,$conf['MM'],$uid);
407
408 $outRow[$field]=array(
409 'type'=>'db',
410 # 'tableArray' => $dbAnalysis->tableArray,
411 'itemArray' => $dbAnalysis->itemArray,
412 # 'getValueArray' => $dbAnalysis->getValueArray($prependName)
413 );
414 }
415 }
416 }
417 return $outRow;
418 }
419
420 /**
421 * DB relations flattend to 1-dim array.
422 *
423 * @param [type] $dbrels: ...
424 * @return [type] ...
425 */
426 function flatDBrels($dbrels) {
427 $list=array();
428 reset($dbrels);
429 while(list($table,$dat)=each($dbrels)) {
430 if ($dat['type']=='db') {
431 reset($dat['itemArray']);
432 while(list(,$i)=each($dat['itemArray'])) {
433 $list[$i['table'].':'.$i['id']]=$i;
434 }
435 }
436 }
437 # if (count($list)) debug($list);
438 return $list;
439 # debug($dbrels);
440 }
441
442 /**
443 * Loads TRD file content into the $this->dat array
444 *
445 * @param [type] $filecontent: ...
446 * @return [type] ...
447 */
448 function loadContent($filecontent) {
449 $pointer = 0;
450
451 $this->dat['header'] = $this->getNextContentPart($filecontent,$pointer,1,'header');
452 $this->dat['records'] = $this->getNextContentPart($filecontent,$pointer,1,'records');
453 $this->dat['files'] = $this->getNextContentPart($filecontent,$pointer,1,'files');
454 }
455
456 /**
457 * Returns the next content part from the $filecontent
458 *
459 * @param [type] $filecontent: ...
460 * @param [type] $pointer: ...
461 * @param [type] $unserialize: ...
462 * @param [type] $name: ...
463 * @return [type] ...
464 */
465 function getNextContentPart($filecontent,&$pointer,$unserialize=0,$name='') {
466 $initStrLen = 32+1+1+1+10+1;
467 // getting header data
468 $initStr = substr($filecontent,$pointer,$initStrLen);
469 $pointer+=$initStrLen;
470 $initStrDat=explode(':',$initStr);
471 if (!strcmp($initStrDat[3],'')) {
472 $datString = substr($filecontent,$pointer,intval($initStrDat[2]));
473 $pointer+=intval($initStrDat[2])+1;
474 if (!strcmp(md5($datString),$initStrDat[0])) {
475 if ($initStrDat[1]) {
476 if ($this->compress) {
477 $datString=gzuncompress($datString);
478 } else debug('Content read error: This file requires decompression, but this server does not offer gzcompress()/gzuncompress() functions.',1);
479 }
480 return $unserialize ? unserialize($datString) : $datString;
481 } else debug('MD5 check failed ('.$name.')');
482 } else debug('Content read error: InitString had a wrong length. ('.$name.')');
483 }
484
485 /**
486 * Loads the header section/all of the $filename into memory
487 *
488 * @param [type] $filename: ...
489 * @param [type] $all: ...
490 * @return [type] ...
491 */
492 function loadFile($filename,$all=0) {
493 if (@is_file($filename)) {
494 if($fd = fopen($filename,'rb')) {
495 $this->dat['header']=$this->getNextFilePart($fd,1,'header');
496 if ($all) {
497 $this->dat['records']=$this->getNextFilePart($fd,1,'records');
498 $this->dat['files']=$this->getNextFilePart($fd,1,'files');
499 }
500 } else debug('Error opening file: '.$filename);
501 fclose($fd);
502 } else debug('Filename not found: '.$filename);
503 }
504
505 /**
506 * Returns the next content part form the fileresource, $fd
507 *
508 * @param [type] $fd: ...
509 * @param [type] $unserialize: ...
510 * @param [type] $name: ...
511 * @return [type] ...
512 */
513 function getNextFilePart($fd,$unserialize=0,$name='') {
514 $initStrLen = 32+1+1+1+10+1;
515
516 // getting header data
517 $initStr = fread($fd,$initStrLen);
518 $initStrDat=explode(':',$initStr);
519 if (!strcmp($initStrDat[3],'')) {
520 $datString = fread($fd,intval($initStrDat[2]));
521 fread($fd,1);
522 if (!strcmp(md5($datString),$initStrDat[0])) {
523 if ($initStrDat[1]) {
524 if ($this->compress) {
525 $datString=gzuncompress($datString);
526 } else debug('Content read error: This file requires decompression, but this server does not offer gzcompress()/gzuncompress() functions.',1);
527 }
528 return $unserialize ? unserialize($datString) : $datString;
529 } else debug('MD5 check failed ('.$name.')');
530 } else debug('File read error: InitString had a wrong length. ('.$name.')');
531 }
532
533 /**
534 * This compiles and returns the data content for an exported file
535 *
536 * @return [type] ...
537 */
538 function compileMemoryToFileContent() {
539 $compress=$this->doOutputCompress();
540 $out='';
541
542 // adding header:
543 $out.=$this->addFilePart(serialize($this->dat['header']),$compress);
544
545 // adding records:
546 $out.=$this->addFilePart(serialize($this->dat['records']),$compress);
547
548 // adding files:
549 $out.=$this->addFilePart(serialize($this->dat['files']),$compress);
550
551 return $out;
552 }
553
554 /**
555 * Returns true if the output should be compressed.
556 *
557 * @return [type] ...
558 */
559 function doOutputCompress() {
560 return $this->compress && !$this->dontCompress;
561 }
562
563 /**
564 * Returns a content part for a filename being build.
565 *
566 * @param [type] $data: ...
567 * @param [type] $compress: ...
568 * @return [type] ...
569 */
570 function addFilePart($data,$compress=0) {
571 if ($compress) $data=gzcompress($data);
572 return md5($data).':'.($compress?'1':'0').':'.str_pad(strlen($data),10,'0',STR_PAD_LEFT).':'.$data.':';
573 }
574
575 /**
576 * Sets error message.
577 *
578 * @param [type] $msg: ...
579 * @return [type] ...
580 */
581 function error($msg) {
582 $this->errorLog[]=$msg;
583 }
584
585 /**
586 * Returns a table with the error-messages.
587 *
588 * @return [type] ...
589 */
590 function printErrorLog() {
591 return t3lib_div::view_array($this->errorLog);
592 }
593
594 /**
595 * Returns destination path to an upload folder given by $folder
596 *
597 * @param [type] $folder: ...
598 * @return [type] ...
599 */
600 function destPathFromUploadFolder ($folder) {
601 return PATH_site.$folder;
602 }
603
604
605
606
607
608
609
610
611
612
613 /**
614 * Imports the internal data array to $pid.
615 *
616 * @param [type] $pid: ...
617 * @return [type] ...
618 */
619 function importData($pid) {
620 global $TCA;
621 #debug($this->dat['header']);
622 #debug($this->dat['records']);
623
624 // 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.
625 $this->import_mapId=array();
626 $this->import_newId=array();
627 $this->import_newId_pids=array();
628
629
630
631 // BEGIN pages session
632 if (is_array($this->dat['header']['records']['pages'])) {
633 // $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.
634 $pageRecords = $this->dat['header']['records']['pages'];
635 $this->initImportVars(); // Init each tcemain session with this!
636 // First add page tree if any
637 if (is_array($this->dat['header']['pagetree'])) {
638 $pagesFromTree=$this->flatInversePageTree($this->dat['header']['pagetree']);
639 reset($pagesFromTree);
640 while(list(,$uid)=each($pagesFromTree)) {
641 $thisRec = $this->dat['header']['records']['pages'][$uid];
642 // PID: Set the main $pid, unless a NEW-id is found
643 $setPid = isset($this->import_newId_pids[$thisRec['pid']]) ? $this->import_newId_pids[$thisRec['pid']] : $pid;
644 $this->addSingle('pages',$uid,$setPid);
645 unset($pageRecords[$uid]);
646 }
647 }
648 #debug($pageRecords);
649 // Then add all remaining pages.
650 if (count($pageRecords)) {
651 reset($pageRecords);
652 while(list($table,$recs)=each($pageRecords)) {
653 reset($recs);
654 while(list($uid)=each($recs)) {
655 $this->addSingle($table,$uid,$pid);
656 }
657 }
658 }
659
660 // Now write to database:
661 $tce = $this->getNewTCE();
662 $tce->start($this->import_data,Array());
663 $tce->process_datamap();
664
665 // post-processing: Removing files and registering new ids (end all tcemain sessions with this)
666 $this->addToMapId($tce->substNEWwithIDs);
667 $this->unlinkTempFiles();
668 }
669
670 #debug($this->import_mapId);
671 // BEGIN tcemain session (rest except pages)
672 $this->initImportVars(); // Init each tcemain session with this!
673 if (is_array($this->dat['header']['records'])) {
674 reset($this->dat['header']['records']);
675 while(list($table,$recs)=each($this->dat['header']['records'])) {
676 if ($table!='pages') {
677 reset($recs);
678 while(list($uid,$thisRec)=each($recs)) {
679 // PID: Set the main $pid, unless a NEW-id is found
680 $setPid = isset($this->import_mapId['pages'][$thisRec['pid']]) ? $this->import_mapId['pages'][$thisRec['pid']] : $pid;
681 if (is_array($TCA[$table]) && $TCA[$table]['ctrl']['rootLevel']) {
682 $setPid=0;
683 }
684 #debug($setPid);
685 #debug($thisRec);
686 $this->addSingle($table,$uid,$setPid);
687 }
688 }
689 }
690 } else debug('Error: No records defined in internal data array.');
691
692 #debug($this->unlinkFiles);
693 #debug($this->alternativeFileName);
694 #debug($this->import_data);
695
696 // Now write to database:
697 $tce = $this->getNewTCE();
698 $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.
699 $tce->start($this->import_data,Array());
700 $tce->process_datamap();
701
702
703 // post-processing: Removing files and registering new ids (end all tcemain sessions with this)
704 $this->addToMapId($tce->substNEWwithIDs);
705 $this->unlinkTempFiles();
706
707 #debug($this->import_newId);
708 #debug($tce->substNEWwithIDs);
709 # $tce->clear_cacheCmd($cacheCmd);
710 // END tcemain sessions
711
712
713 // Finally all the database record references must be fixed. This is done after all records have supposedly been written to database:
714 // $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.
715 $this->setRelations();
716 }
717
718 /**
719 * [Describe function...]
720 *
721 * @return [type] ...
722 */
723 function getNewTCE() {
724 $tce = t3lib_div::makeInstance('t3lib_TCEmain');
725 $tce->stripslashes_values=0;
726 $tce->dontProcessTransformations=1;
727 $tce->enableLogging=0;
728 $tce->alternativeFileName = $this->alternativeFileName;
729 return $tce;
730 }
731
732 /**
733 * At the end of the import process all relations should be set properly (that is relations to imported records are all re-created so imported records are correctly related again)
734 *
735 * @return [type] ...
736 */
737 function setRelations() {
738 global $TCA;
739
740 $updateData=array();
741 reset($this->import_newId);
742 while(list($nId,$dat)=each($this->import_newId)) {
743 $table=$dat['table'];
744 $uid=$dat['uid']; // original UID - NOT the new one!
745 if (is_array($this->import_mapId[$table]) && isset($this->import_mapId[$table][$uid])) {
746 $thisNewUid = $this->import_mapId[$table][$uid];
747 if (is_array($this->dat['records'][$table.':'.$uid]['rels'])) {
748 reset($this->dat['records'][$table.':'.$uid]['rels']);
749 while(list($field,$config)=each($this->dat['records'][$table.':'.$uid]['rels'])) {
750 switch((string)$config['type']) {
751 case 'db':
752 if (count($config['itemArray'])) {
753 $valArray=array();
754 reset($config['itemArray']);
755 while(list(,$relDat)=each($config['itemArray'])) {
756 if (is_array($this->import_mapId[$relDat['table']]) && isset($this->import_mapId[$relDat['table']][$relDat['id']])) {
757 #debug('FOUND: '.$relDat['table'].':'.$relDat['id'],1);
758 $valArray[]=$relDat['table'].'_'.$this->import_mapId[$relDat['table']][$relDat['id']];
759 } elseif (is_array($TCA[$relDat['table']]) && $TCA[$relDat['table']]['ctrl']['is_static']) {
760 #debug('STATIC: '.$relDat['table'].':'.$relDat['id'],1);
761 $valArray[]=$relDat['table'].'_'.$relDat['id'];
762 } else {
763 debug('Lost relation: '.$relDat['table'].':'.$relDat['id'],1);
764 }
765 }
766 $updateData[$table][$thisNewUid][$field]=implode(',',$valArray);
767 }
768 break;
769 }
770 }
771 } else debug('Error: no record was found in data array!',1);
772 } else debug('Error: this records is NOT created it seems! ('.$table.':'.$uid.')',1);
773 }
774 if (count($updateData)) {
775 #debug($updateData);
776 $tce = $this->getNewTCE();
777 $tce->start($updateData,Array());
778 $tce->process_datamap();
779 }
780 }
781
782 /**
783 * Registers the substNEWids in memory.
784 *
785 * @param [type] $substNEWwithIDs: ...
786 * @return [type] ...
787 */
788 function addToMapId($substNEWwithIDs) {
789 reset($this->import_data);
790 while(list($table,$recs)=each($this->import_data)) {
791 reset($recs);
792 while(list($id)=each($recs)) {
793 $old_uid = $this->import_newId[$id]['uid'];
794 if (isset($substNEWwithIDs[$id])) {
795 $this->import_mapId[$table][$old_uid]=$substNEWwithIDs[$id];
796 } else debug('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);
797 }
798 }
799
800 }
801
802 /**
803 * Initializes the import proces variables. This should be done for each time you have a session calling tcemain (remember also to unlink files after these sessions).
804 *
805 * @return [type] ...
806 */
807 function initImportVars() {
808 $this->import_data=array();
809 $this->unlinkFiles=array();
810 $this->alternativeFileName=array();
811 }
812
813 /**
814 * Cleaning up all the temporary files stored in typo3temp/ folder
815 *
816 * @return [type] ...
817 */
818 function unlinkTempFiles() {
819 $tempPath = $this->getTempPathFileName('');
820 #debug($tempPath);
821 reset($this->unlinkFiles);
822 while(list(,$fileName)=each($this->unlinkFiles)) {
823 if (t3lib_div::isFirstPartOfStr($fileName,$tempPath)) {
824 unlink($fileName);
825 clearstatcache();
826 if (is_file($fileName)) {
827 debug('Error: '.$fileName.' was NOT unlinked as it should have been!',1);
828 }
829 } else debug('Error: '.$fileName.' was not in temp-path. Not removed!',1);
830 }
831 }
832
833 /**
834 * Adds a single record to the $importData array. Also copies files to tempfolder. However all references are set to blank for now.
835 *
836 * @param [type] $table: ...
837 * @param [type] $uid: ...
838 * @param [type] $pid: ...
839 * @return [type] ...
840 */
841 function addSingle($table,$uid,$pid) {
842 $record = $this->dat['records'][$table.':'.$uid]['data'];
843 if (is_array($record)) {
844 $ID = uniqid('NEW');
845 $this->import_newId[$ID] = array('table'=>$table,'uid'=>$uid);
846 if ($table=='pages') $this->import_newId_pids[$uid]=$ID;
847
848 $this->import_data[$table][$ID]=$record;
849 $this->import_data[$table][$ID]['tx_impexp_origuid']=$this->import_data[$table][$ID]['uid'];
850 unset($this->import_data[$table][$ID]['uid']);
851 $this->import_data[$table][$ID]['pid']=$pid;
852
853 reset($this->dat['records'][$table.':'.$uid]['rels']);
854 while(list($field,$config)=each($this->dat['records'][$table.':'.$uid]['rels'])) {
855 $this->import_data[$table][$ID][$field]='';
856 switch((string)$config['type']) {
857 case 'db':
858 #debug($config);
859 // ... later
860 break;
861 case 'file':
862 $valArr=array();
863 reset($config['newValueFiles']);
864 while(list(,$fI)=each($config['newValueFiles'])) {
865 $valArr[]=$this->import_addFileNameToBeCopied($fI);
866 #debug($fI);
867 }
868 $this->import_data[$table][$ID][$field]=implode(',',$valArr);
869 break;
870 }
871 }
872 } else debug('Error: no record was found in data array!',1);
873 }
874
875 /**
876 * Writes the file from import array to temp dir and returns the filename of it.
877 *
878 * @param [type] $fI: ...
879 * @return [type] ...
880 */
881 function import_addFileNameToBeCopied($fI) {
882 if (is_array($this->dat['files'][$fI['ID']])) {
883 $tmpFile=$this->getTempPathFileName('import_'.$GLOBALS['EXEC_TIME'].'_'.$fI['ID'].'.tmp');
884 if (!@is_file($tmpFile)) {
885 t3lib_div::writeFile($tmpFile,$this->dat['files'][$fI['ID']]['content']);
886 clearstatcache();
887 if (@is_file($tmpFile)) {
888 if (filesize($tmpFile)==$this->dat['files'][$fI['ID']]['filesize']) {
889 $this->unlinkFiles[]=$tmpFile;
890 $this->alternativeFileName[$tmpFile]=$fI['fieldvalue'];
891 #debug($tmpFile,1);
892 return $tmpFile;
893 } else debug('Error: temporary file '.$tmpFile.' had a size ('.filesize($tmpFile).') different from the original ('.$this->dat['files'][$fI['ID']]['filesize'].')',1);
894 } else debug('Error: temporary file '.$tmpFile.' was not written as it should have been!',1);
895 } else debug('Error: temporary file '.$tmpFile.' existed already!',1);
896 } else debug('Error: No file found for ID '.$fI['ID'],1);
897 }
898
899 /**
900 * Returns the absolute path to typo3temp/ (for writing temporary files from the import ...)
901 *
902 * @param [type] $fN: ...
903 * @return [type] ...
904 */
905 function getTempPathFileName($fN) {
906 return PATH_site.'typo3temp/'.$fN;
907 }
908
909
910
911
912
913
914
915
916
917
918
919
920
921 /**
922 * Displays an overview of the header-content.
923 *
924 * @return [type] ...
925 */
926 function displayContentOverview () {
927 # unset($this->dat['records']);
928 unset($this->dat['files']);
929 # debug($this->dat['header']);
930
931 $this->remainHeader = $this->dat['header'];
932 if (is_array($this->remainHeader)) {
933
934 if (is_array($this->dat['header']['pagetree'])) {
935 reset($this->dat['header']['pagetree']);
936 $lines=array();
937 $this->traversePageTree($this->dat['header']['pagetree'],$lines);
938
939 $rows=array();
940 # debug($lines);
941 reset($lines);
942 while(list(,$r)=each($lines)) {
943 $rows[]='<tr bgcolor="'.$r['bgColor'].'">
944 <td nowrap="nowrap">'.$r['preCode'].$r['title'].'</td>
945 <td nowrap="nowrap">'.t3lib_div::formatSize($r['size']).'</td>
946 <td nowrap="nowrap">'.($r['msg']?'<span class="typo3-red">'.$r['msg'].'</span>':'').'</td>
947 </tr>';
948 }
949 $rows[]='<tr>
950 <td><img src="clear.gif" width="300" height="1" alt="" /></td>
951 <td></td>
952 <td></td>
953 </tr>';
954 $out = '<strong>Inside pagetree:</strong><br /><br /><table border="0" cellpadding="0" cellspacing="0">'.implode('',$rows).'</table><br /><br />';
955 }
956
957
958 $lines=array();
959 if (is_array($this->remainHeader['records']['pages'])) {
960 $this->traversePageRecords($this->remainHeader['records']['pages'],$lines);
961 }
962 $this->traverseAllRecords($this->remainHeader['records'],$lines);
963
964 if (count($lines)) {
965 $rows=array();
966 # debug($lines);
967 reset($lines);
968 while(list(,$r)=each($lines)) {
969 $rows[]='<tr bgcolor="'.$r['bgColor'].'">
970 <td nowrap="nowrap">'.$r['preCode'].$r['title'].'</td>
971 <td nowrap="nowrap">'.t3lib_div::formatSize($r['size']).'</td>
972 <td nowrap="nowrap">'.($r['msg']?'<span class="typo3-red">'.$r['msg'].'</span>':'').'</td>
973 </tr>';
974 }
975 $rows[]='<tr>
976 <td><img src="clear.gif" width="300" height="1" alt="" /></td>
977 <td></td>
978 <td></td>
979 </tr>';
980 $out.= '<strong>Outside pagetree:</strong><br /><br /><table border="0" cellpadding="0" cellspacing="0">'.implode('',$rows).'</table>';
981 }
982
983 #debug($this->remainHeader);
984 }
985 return $out;
986 }
987
988 /**
989 * Go through page tree for display
990 *
991 * @param [type] $pT: ...
992 * @param [type] $lines: ...
993 * @param [type] $preCode: ...
994 * @return [type] ...
995 */
996 function traversePageTree($pT,&$lines,$preCode='') {
997 reset($pT);
998 while(list($k,$v)=each($pT)) {
999 $this->singleRecordLines('pages',$k,$lines,$preCode);
1000 // Subrecords:
1001 if (is_array($this->dat['header']['pid_lookup'][$k])) {
1002 reset($this->dat['header']['pid_lookup'][$k]);
1003 while(list($t,$recUidArr)=each($this->dat['header']['pid_lookup'][$k])) {
1004 if ($t!='pages') {
1005 reset($recUidArr);
1006 while(list($ruid)=each($recUidArr)) {
1007 $this->singleRecordLines($t,$ruid,$lines,$preCode.'&nbsp;&nbsp;&nbsp;&nbsp;');
1008 }
1009 }
1010 }
1011 unset($this->remainHeader['pid_lookup'][$k]);
1012 }
1013 // Subpages:
1014 if (is_array($v['subrow'])) $this->traversePageTree($v['subrow'],$lines,$preCode.'&nbsp;&nbsp;&nbsp;&nbsp;');
1015 }
1016 }
1017
1018 /**
1019 * Go through remaining pages (not in tree)
1020 *
1021 * @param [type] $pT: ...
1022 * @param [type] $lines: ...
1023 * @return [type] ...
1024 */
1025 function traversePageRecords($pT,&$lines) {
1026 reset($pT);
1027 while(list($k,$rHeader)=each($pT)) {
1028 $this->singleRecordLines('pages',$k,$lines,'',1);
1029 // Subrecords:
1030 if (is_array($this->dat['header']['pid_lookup'][$k])) {
1031 reset($this->dat['header']['pid_lookup'][$k]);
1032 while(list($t,$recUidArr)=each($this->dat['header']['pid_lookup'][$k])) {
1033 if ($t!='pages') {
1034 reset($recUidArr);
1035 while(list($ruid)=each($recUidArr)) {
1036 $this->singleRecordLines($t,$ruid,$lines,$preCode.'&nbsp;&nbsp;&nbsp;&nbsp;');
1037 }
1038 }
1039 }
1040 unset($this->remainHeader['pid_lookup'][$k]);
1041 }
1042 }
1043 }
1044
1045 /**
1046 * Go through ALL records (if the pages are displayed first, those will not be amoung these!)
1047 *
1048 * @param [type] $pT: ...
1049 * @param [type] $lines: ...
1050 * @return [type] ...
1051 */
1052 function traverseallrecords($pT,&$lines) {
1053 reset($pT);
1054 while(list($t,$recUidArr)=each($pT)) {
1055 if ($t!='pages') {
1056 reset($recUidArr);
1057 while(list($ruid)=each($recUidArr)) {
1058 $this->singleRecordLines($t,$ruid,$lines,$preCode,1);
1059 }
1060 }
1061 }
1062 }
1063
1064 /**
1065 * Add entries for a single record
1066 *
1067 * @param [type] $table: ...
1068 * @param [type] $uid: ...
1069 * @param [type] $lines: ...
1070 * @param [type] $preCode: ...
1071 * @param [type] $checkImportInPidRecord: ...
1072 * @return [type] ...
1073 */
1074 function singleRecordLines($table,$uid,&$lines,$preCode,$checkImportInPidRecord=0) {
1075 global $TCA,$BE_USER;
1076
1077 $record = $this->dat['header']['records'][$table][$uid];
1078 unset($this->remainHeader['records'][$table][$uid]);
1079 if (!is_array($record)) debug('MISSING RECORD: '.$table.':'.$uid,1);
1080
1081 $pInfo=array();
1082 $pInfo['ref']=$table.':'.$uid;
1083 if (!isset($TCA[$table])) {
1084 $pInfo['preCode']=$preCode;
1085 $pInfo['msg']="UNKNOWN TABLE '".$pInfo['ref']."'";
1086 $pInfo['title']='<em>'.htmlspecialchars($record['title']).'</em>';
1087 } else {
1088 if (is_array($this->display_import_pid_record)) {
1089 if ($checkImportInPidRecord) {
1090 if (!$BE_USER->doesUserHaveAccess($this->display_import_pid_record,$table=='pages'?8:16)) {
1091 $pInfo['msg'].="'".$pInfo['ref']."' cannot be INSERTED on this page! ";
1092 }
1093 if (!$this->checkDokType($table,$this->display_import_pid_record['doktype']) && !$TCA[$table]['ctrl']['rootLevel']) {
1094 $pInfo['msg'].="'".$table."' cannot be INSERTED on this page type (change to 'sysFolder'!) ";
1095 }
1096 }
1097 if (!$BE_USER->check('tables_modify',$table)) {$pInfo['msg'].="You are not allowed to CREATE '".$table."' tables! ";}
1098
1099 if ($TCA[$table]['ctrl']['readOnly']) {$pInfo['msg'].="TABLE '".$table."' is READ ONLY! ";}
1100 if ($TCA[$table]['ctrl']['adminOnly'] && !$BE_USER->isAdmin()) {$pInfo['msg'].="TABLE '".$table."' is ADMIN ONLY! ";}
1101 if ($TCA[$table]['ctrl']['is_static']) {$pInfo['msg'].="TABLE '".$table."' is a STATIC TABLE! ";}
1102 if ($TCA[$table]['ctrl']['rootLevel']) {$pInfo['msg'].="TABLE '".$table."' will be inserted on ROOT LEVEL! ";}
1103 }
1104 $pInfo['preCode']=$preCode.t3lib_iconworks::getIconImage($table,$this->dat['records'][$table.':'.$uid]['data'],$GLOBALS['BACK_PATH'],'align="top" title="'.htmlspecialchars($table.':'.$uid).'"');
1105 $pInfo['title']=htmlspecialchars($record['title']);
1106 }
1107 $pInfo['bgColor']=$table=='pages' ? t3lib_div::modifyHTMLColor($GLOBALS['TBE_TEMPLATE']->bgColor4,-10,-10,-10) : t3lib_div::modifyHTMLColor($GLOBALS['TBE_TEMPLATE']->bgColor4,20,20,20);
1108 $pInfo['size']=$record['size'];
1109 $lines[]=$pInfo;
1110 // Files
1111 if (is_array($record['filerefs'])) {
1112 reset($record['filerefs']);
1113 while(list(,$ID)=each($record['filerefs'])) {
1114 $fI=$this->dat['header']['files'][$ID];
1115 if (!is_array($fI)) debug('MISSING FILE: '.$ID,1);
1116 $pInfo=array();
1117 $pInfo['preCode']=$preCode.'&nbsp;&nbsp;&nbsp;&nbsp;<img src="'.$GLOBALS['BACK_PATH'].'t3lib/gfx/rel_file.gif" width="13" height="12" align="top" alt="" />';
1118 $pInfo['title']=htmlspecialchars($fI['filename']);
1119 $pInfo['ref']='FILE';
1120 $pInfo['size']=$fI['filesize'];
1121 $pInfo['bgColor']=t3lib_div::modifyHTMLColor($GLOBALS['TBE_TEMPLATE']->bgColor4,10,10,10);
1122 $lines[]=$pInfo;
1123 unset($this->remainHeader['files'][$ID]);
1124 }
1125 }
1126 // DB:
1127 if (is_array($record['rels'])) {
1128 $this->addRelations($record['rels'],$lines,$preCode);
1129 }
1130 }
1131
1132 /**
1133 * Add relations entries for a record's rels-array
1134 *
1135 * @param [type] $rels: ...
1136 * @param [type] $lines: ...
1137 * @param [type] $preCode: ...
1138 * @param [type] $recurCheck: ...
1139 * @return [type] ...
1140 */
1141 function addRelations($rels,&$lines,$preCode,$recurCheck=array()) {
1142 reset($rels);
1143 while(list(,$dat)=each($rels)) {
1144 $table=$dat['table'];
1145 $uid=$dat['id'];
1146 $pInfo=array();
1147 $Iprepend='';
1148 $pInfo['ref']=$table.':'.$uid;
1149 if (!in_array($pInfo['ref'],$recurCheck)) {
1150 $record = $this->dat['header']['records'][$table][$uid];
1151 if (!is_array($record)) {
1152 if (isset($GLOBALS['TCA'][$table]) && $GLOBALS['TCA'][$table]['ctrl']['is_static']) {
1153 $pInfo['title']=htmlspecialchars('STATIC: '.$pInfo['ref']);
1154 $Iprepend='_static';
1155 } else {
1156 $pInfo['title']=htmlspecialchars($pInfo['ref']);
1157 $pInfo['msg']='LOST RELATION';
1158 $Iprepend='_lost';
1159 # debug('MISSING relation: '.$table.':'.$uid,1);
1160 }
1161 } else {
1162 $pInfo['title']=htmlspecialchars($record['title']);
1163 $pInfo['size']=$record['size'];
1164 }
1165
1166 $pInfo['preCode']=$preCode.'&nbsp;&nbsp;&nbsp;&nbsp;<img src="'.$GLOBALS['BACK_PATH'].'t3lib/gfx/rel_db'.$Iprepend.'.gif" width="13" height="12" align="top" title="'.$pInfo['ref'].'" alt="" />';
1167 $pInfo['bgColor']=t3lib_div::modifyHTMLColor($GLOBALS['TBE_TEMPLATE']->bgColor4,10,10,10);
1168 $lines[]=$pInfo;
1169 if (is_array($record) && is_array($record['rels'])) {
1170 $this->addRelations($record['rels'],$lines,$preCode.'&nbsp;&nbsp;',array_merge($recurCheck,array($pInfo['ref'])));
1171 }
1172 } else debug($pInfo['ref'].' was recursive...');
1173 }
1174 }
1175
1176 /**
1177 * Verifies that a table is allowed on a certain doktype of a page
1178 *
1179 * @param [type] $checkTable: ...
1180 * @param [type] $doktype: ...
1181 * @return [type] ...
1182 */
1183 function checkDokType($checkTable,$doktype) {
1184 global $PAGES_TYPES;
1185 $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables'];
1186 $allowedArray = t3lib_div::trimExplode(',',$allowedTableList,1);
1187 if (strstr($allowedTableList,'*') || in_array($checkTable,$allowedArray)) { // If all tables or the table is listed as a allowed type, return true
1188 return true;
1189 }
1190 }
1191 }
1192
1193
1194
1195 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/impexp/class.tx_impexp.php']) {
1196 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/impexp/class.tx_impexp.php']);
1197 }
1198 ?>