|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com) 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * A copy is found in the textfile GPL.txt and important notices to the license 00017 * from the author is found in LICENSE.txt distributed with these scripts. 00018 * 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00027 /** 00028 * T3D file Import/Export library (TYPO3 Record Document) 00029 * 00030 * @author Kasper Skårhøj <kasperYYYY@typo3.com> 00031 */ 00032 /** 00033 * [CLASS/FUNCTION INDEX of SCRIPT] 00034 * 00035 * 00036 * 00037 * 198: class tx_impexp 00038 * 00039 * SECTION: Initialize 00040 * 261: function init($dontCompress=0,$mode='') 00041 * 00042 * SECTION: Export / Init + Meta Data 00043 * 292: function setHeaderBasics() 00044 * 316: function setCharset($charset) 00045 * 331: function setMetaData($title,$description,$notes,$packager_username,$packager_name,$packager_email) 00046 * 351: function addThumbnail($imgFilepath) 00047 * 00048 * SECTION: Export / Init Page tree 00049 * 389: function setPageTree($idH) 00050 * 402: function unsetExcludedSections($idH) 00051 * 424: function flatInversePageTree($idH,$a=array()) 00052 * 447: function flatInversePageTree_pid($idH,$a=array(),$pid=-1) 00053 * 00054 * SECTION: Export 00055 * 486: function export_addRecord($table,$row,$relationLevel=0) 00056 * 544: function export_addDBRelations($relationLevel=0) 00057 * 648: function export_addDBRelations_registerRelation($fI, &$addR, $tokenID='') 00058 * 672: function export_addFilesFromRelations() 00059 * 773: function export_addFile($fI, $recordRef='', $fieldname='') 00060 * 898: function flatDBrels($dbrels) 00061 * 924: function flatSoftRefs($dbrels) 00062 * 00063 * SECTION: File Output 00064 * 988: function compileMemoryToFileContent($type='') 00065 * 1014: function createXML() 00066 * 1106: function doOutputCompress() 00067 * 1117: function addFilePart($data, $compress=FALSE) 00068 * 00069 * SECTION: Import 00070 * 1150: function importData($pid) 00071 * 1191: function writeRecords_pages($pid) 00072 * 1246: function writeRecords_pages_order($pid) 00073 * 1284: function writeRecords_records($pid) 00074 * 1334: function writeRecords_records_order($mainPid) 00075 * 1383: function addSingle($table,$uid,$pid) 00076 * 1457: function addToMapId($substNEWwithIDs) 00077 * 1477: function getNewTCE() 00078 * 1491: function unlinkTempFiles() 00079 * 00080 * SECTION: Import / Relations setting 00081 * 1529: function setRelations() 00082 * 1584: function setRelations_db($itemArray) 00083 * 1611: function import_addFileNameToBeCopied($fI) 00084 * 1634: function setFlexFormRelations() 00085 * 1718: function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) 00086 * 00087 * SECTION: Import / Soft References 00088 * 1760: function processSoftReferences() 00089 * 1851: function processSoftReferences_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) 00090 * 1890: function processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid) 00091 * 1954: function processSoftReferences_saveFile($relFileName, $cfg, $table, $uid) 00092 * 2015: function processSoftReferences_saveFile_createRelFile($origDirPrefix, $fileName, $fileID, $table, $uid) 00093 * 2104: function writeFileVerify($fileName, $fileID, $bypassMountCheck=FALSE) 00094 * 2131: function checkOrCreateDir($dirPrefix) 00095 * 2164: function verifyFolderAccess($dirPrefix, $noAlternative=FALSE) 00096 * 00097 * SECTION: File Input 00098 * 2214: function loadFile($filename,$all=0) 00099 * 2257: function getNextFilePart($fd,$unserialize=0,$name='') 00100 * 2284: function loadContent($filecontent) 00101 * 2302: function getNextContentPart($filecontent,&$pointer,$unserialize=0,$name='') 00102 * 2327: function loadInit() 00103 * 2343: function fixCharsets() 00104 * 00105 * SECTION: Visual rendering of import/export memory, $this->dat 00106 * 2398: function displayContentOverview() 00107 * 2506: function traversePageTree($pT,&$lines,$preCode='') 00108 * 2541: function traversePageRecords($pT,&$lines) 00109 * 2568: function traverseAllRecords($pT,&$lines) 00110 * 2590: function singleRecordLines($table,$uid,&$lines,$preCode,$checkImportInPidRecord=0) 00111 * 2748: function addRelations($rels,&$lines,$preCode,$recurCheck=array(),$htmlColorClass='') 00112 * 2813: function addFiles($rels,&$lines,$preCode,$htmlColorClass='',$tokenID='') 00113 * 2931: function checkDokType($checkTable,$doktype) 00114 * 2947: function renderControls($r) 00115 * 2975: function softrefSelector($cfg) 00116 * 00117 * SECTION: Helper functions of kinds 00118 * 3051: function isTableStatic($table) 00119 * 3065: function inclRelation($table) 00120 * 3080: function isExcluded($table,$uid) 00121 * 3092: function includeSoftref($tokenID) 00122 * 3102: function checkPID($pid) 00123 * 3119: function dontIgnorePid($table, $uid) 00124 * 3132: function doesRecordExist($table,$uid,$fields='') 00125 * 3142: function getRecordPath($pid) 00126 * 3159: function renderSelectBox($prefix,$value,$optValues) 00127 * 3183: function compareRecords($databaseRecord, $importRecord, $table, $inverseDiff=FALSE) 00128 * 3250: function getRTEoriginalFilename($string) 00129 * 3267: function &getFileProcObj() 00130 * 00131 * SECTION: Error handling 00132 * 3299: function error($msg) 00133 * 3308: function printErrorLog() 00134 * 00135 * TOTAL FUNCTIONS: 72 00136 * (This index is automatically created/updated by the extension "extdeveval") 00137 * 00138 */ 00139 /** 00140 * EXAMPLE for using the impexp-class for exporting stuff: 00141 * 00142 * // Create and initialize: 00143 * $this->export = t3lib_div::makeInstance('tx_impexp'); 00144 * $this->export->init(); 00145 * // Set which tables relations we will allow: 00146 * $this->export->relOnlyTables[]="tt_news"; // exclusively includes. See comment in the class 00147 * 00148 * // Adding records: 00149 * $this->export->export_addRecord("pages",$this->pageinfo); 00150 * $this->export->export_addRecord("pages",t3lib_BEfunc::getRecord("pages",38)); 00151 * $this->export->export_addRecord("pages",t3lib_BEfunc::getRecord("pages",39)); 00152 * $this->export->export_addRecord("tt_content",t3lib_BEfunc::getRecord("tt_content",12)); 00153 * $this->export->export_addRecord("tt_content",t3lib_BEfunc::getRecord("tt_content",74)); 00154 * $this->export->export_addRecord("sys_template",t3lib_BEfunc::getRecord("sys_template",20)); 00155 * 00156 * // Adding all the relations (recursively in 5 levels so relations has THEIR relations registered as well) 00157 * for($a=0;$a<5;$a++) { 00158 * $addR = $this->export->export_addDBRelations($a); 00159 * if (!count($addR)) break; 00160 * } 00161 * 00162 * // Finally load all the files. 00163 * $this->export->export_addFilesFromRelations(); // MUST be after the DBrelations are set so that file from ALL added records are included! 00164 * 00165 * // Now the internal DAT array is ready to export: 00166 * #debug($this->export->dat); 00167 * 00168 * // Write export 00169 * $out = $this->export->compileMemoryToFileContent(); 00170 * #t3lib_div::writeFile(PATH_site."fileadmin/relations.t3d",$out); 00171 * #debug(strlen($out)); 00172 */ 00173 00174 @ini_set('max_execution_time',600); 00175 @ini_set('memory_limit','256m'); 00176 00177 00178 00179 00180 00181 00182 00183 /** 00184 * T3D file Import/Export library (TYPO3 Record Document) 00185 * 00186 * @author Kasper Skårhøj <kasperYYYY@typo3.com> 00187 * @package TYPO3 00188 * @subpackage tx_impexp 00189 */ 00190 class tx_impexp { 00191 00192 // Configuration, general 00193 var $showStaticRelations = FALSE; // If set, static relations (not exported) will be shown in overview as well 00194 var $fileadminFolderName = 'fileadmin'; // Name of the "fileadmin" folder where files for export/import should be located 00195 00196 var $mode = ''; // Whether "import" or "export" mode of object. Set through init() function 00197 var $update = FALSE; // Updates all records that has same UID instead of creating new! 00198 var $doesImport = FALSE; // Is set by importData() when an import has been done. 00199 00200 // Configuration, import 00201 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 00202 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. 00203 var $import_mode = array(); // Setting import modes during update state: as_new, exclude, force_uid 00204 var $global_ignore_pid = FALSE; // If set, PID correct is ignored globally 00205 var $force_all_UIDS = FALSE; // If set, all UID values are forced! (update or import) 00206 var $showDiff = FALSE; // If set, a diff-view column is added to the overview. 00207 var $allowPHPScripts = FALSE; // If set, and if the user is admin, allow the writing of PHP scripts to fileadmin/ area. 00208 var $enableLogging = FALSE; // Disable logging when importing 00209 var $softrefInputValues = array(); // Array of values to substitute in editable softreferences. 00210 var $fileIDMap = array(); // Mapping between the fileID from import memory and the final filenames they are written to. 00211 00212 // Configuration, export 00213 var $maxFileSize = 1000000; // 1MB max file size 00214 var $maxRecordSize = 1000000; // 1MB max record size 00215 var $maxExportSize = 10000000; // 10MB max export size 00216 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. 00217 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). 00218 var $excludeMap = array(); // Exclude map. Keys are table:uid pairs and if set, records are not added to the export. 00219 var $softrefCfg = array(); // Soft Reference Token ID modes. 00220 var $extensionDependencies = array(); // Listing extension dependencies. 00221 var $dontCompress = 0; // Set by user: If set, compression in t3d files is disabled 00222 var $includeExtFileResources = 0; // Boolean, if set, HTML file resources are included. 00223 var $extFileResourceExtensions = 'html,htm,css'; // Files with external media (HTML/css style references inside) 00224 00225 // Internal, dynamic: 00226 var $import_mapId = array(); // After records are written this array is filled with [table][original_uid] = [new_uid] 00227 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 00228 var $import_newId_pids = array(); // Page id map for page tree (import) 00229 var $import_data = array(); // Internal data accumulation for writing records during import 00230 var $errorLog = array(); // Error log. 00231 var $cache_getRecordPath = array(); // Cache for record paths 00232 var $checkPID_cache = array(); // Cache of checkPID values. 00233 00234 var $compress = 0; // Set internally if the gzcompress function exists 00235 var $dat = array(); // Internal import/export memory 00236 00237 /** 00238 * File processing object 00239 * 00240 * @var t3lib_extFileFunctions 00241 */ 00242 var $fileProcObj = ''; 00243 00244 00245 00246 /************************** 00247 * 00248 * Initialize 00249 * 00250 *************************/ 00251 00252 /** 00253 * Init the object, both import and export 00254 * 00255 * @param boolean If set, compression in t3d files is disabled 00256 * @param string Mode of usage, either "import" or "export" 00257 * @return void 00258 */ 00259 function init($dontCompress=0,$mode='') { 00260 $this->compress = function_exists('gzcompress'); 00261 $this->dontCompress = $dontCompress; 00262 00263 $this->mode = $mode; 00264 } 00265 00266 00267 00268 00269 00270 00271 00272 00273 00274 00275 00276 00277 00278 00279 /************************** 00280 * 00281 * Export / Init + Meta Data 00282 * 00283 *************************/ 00284 00285 /** 00286 * Set header basics 00287 * 00288 * @return void 00289 */ 00290 function setHeaderBasics() { 00291 00292 // Initializing: 00293 if (is_array($this->softrefCfg)) { 00294 foreach($this->softrefCfg as $key => $value) { 00295 if (!strlen($value['mode'])) unset($this->softrefCfg[$key]); 00296 } 00297 } 00298 00299 // Setting in header memory: 00300 $this->dat['header']['XMLversion'] = '1.0'; // Version of file format 00301 $this->dat['header']['meta'] = array(); // Initialize meta data array (to put it in top of file) 00302 $this->dat['header']['relStaticTables'] = $this->relStaticTables; // Add list of tables to consider static 00303 $this->dat['header']['excludeMap'] = $this->excludeMap; // The list of excluded records 00304 $this->dat['header']['softrefCfg'] = $this->softrefCfg; // Soft Reference mode for elements 00305 $this->dat['header']['extensionDependencies'] = $this->extensionDependencies; // List of extensions the import depends on. 00306 } 00307 00308 /** 00309 * Set charset 00310 * 00311 * @param string Charset for the content in the export. During import the character set will be converted if the target system uses another charset. 00312 * @return void 00313 */ 00314 function setCharset($charset) { 00315 $this->dat['header']['charset'] = $charset; 00316 } 00317 00318 /** 00319 * Sets meta data 00320 * 00321 * @param string Title of the export 00322 * @param string Description of the export 00323 * @param string Notes about the contents 00324 * @param string Backend Username of the packager (the guy making the export) 00325 * @param string Real name of the packager 00326 * @param string Email of the packager 00327 * @return void 00328 */ 00329 function setMetaData($title,$description,$notes,$packager_username,$packager_name,$packager_email) { 00330 $this->dat['header']['meta'] = array( 00331 'title' => $title, 00332 'description' => $description, 00333 'notes' => $notes, 00334 'packager_username' => $packager_username, 00335 'packager_name' => $packager_name, 00336 'packager_email' => $packager_email, 00337 'TYPO3_version' => TYPO3_version, 00338 'created' => strftime('%A %e. %B %Y', $GLOBALS['EXEC_TIME']), 00339 ); 00340 } 00341 00342 /** 00343 * Sets a thumbnail image to the exported file 00344 * 00345 * @param string Filename reference, gif, jpg, png. Absolute path. 00346 * @return void 00347 */ 00348 function addThumbnail($imgFilepath) { 00349 if (@is_file($imgFilepath)) { 00350 $imgInfo = @getimagesize($imgFilepath); 00351 if (is_array($imgInfo)) { 00352 $fileContent = t3lib_div::getUrl($imgFilepath); 00353 $this->dat['header']['thumbnail'] = array( 00354 'imgInfo' => $imgInfo, 00355 'content' => $fileContent, 00356 'filesize' => strlen($fileContent), 00357 'filemtime' => filemtime($imgFilepath), 00358 'filename' => basename($imgFilepath) 00359 ); 00360 } 00361 } 00362 } 00363 00364 00365 00366 00367 00368 00369 00370 00371 00372 00373 00374 /************************** 00375 * 00376 * Export / Init Page tree 00377 * 00378 *************************/ 00379 00380 /** 00381 * Sets the page-tree array in the export header and returns the array in a flattened version 00382 * 00383 * @param array Hierarchy of ids, the page tree: array([uid] => array("uid" => [uid], "subrow" => array(.....)), [uid] => ....) 00384 * @return array The hierarchical page tree converted to a one-dimensional list of pages 00385 */ 00386 function setPageTree($idH) { 00387 $this->dat['header']['pagetree'] = $this->unsetExcludedSections($idH); 00388 return $this->flatInversePageTree($this->dat['header']['pagetree']); 00389 } 00390 00391 /** 00392 * Removes entries in the page tree which are found in ->excludeMap[] 00393 * 00394 * @param array Page uid hierarchy 00395 * @return array Modified input array 00396 * @access private 00397 * @see setPageTree() 00398 */ 00399 function unsetExcludedSections($idH) { 00400 if (is_array($idH)) { 00401 foreach ($idH as $k => $v) { 00402 if ($this->excludeMap['pages:'.$idH[$k]['uid']]) { 00403 unset($idH[$k]); 00404 } elseif (is_array($idH[$k]['subrow'])) { 00405 $idH[$k]['subrow'] = $this->unsetExcludedSections($idH[$k]['subrow']); 00406 } 00407 } 00408 } 00409 return $idH; 00410 } 00411 00412 /** 00413 * Recursively flattening the idH array (for setPageTree() function) 00414 * 00415 * @param array Page uid hierarchy 00416 * @param array Accumulation array of pages (internal, don't set from outside) 00417 * @return array Array with uid-uid pairs for all pages in the page tree. 00418 * @see flatInversePageTree_pid() 00419 */ 00420 function flatInversePageTree($idH,$a=array()) { 00421 if (is_array($idH)) { 00422 $idH = array_reverse($idH); 00423 foreach ($idH as $k => $v) { 00424 $a[$v['uid']] = $v['uid']; 00425 if (is_array($v['subrow'])) { 00426 $a = $this->flatInversePageTree($v['subrow'],$a); 00427 } 00428 } 00429 } 00430 return $a; 00431 } 00432 00433 /** 00434 * Recursively flattening the idH array (for setPageTree() function), setting PIDs as values 00435 * 00436 * @param array Page uid hierarchy 00437 * @param array Accumulation array of pages (internal, don't set from outside) 00438 * @param integer PID value (internal) 00439 * @return array Array with uid-pid pairs for all pages in the page tree. 00440 * @see flatInversePageTree() 00441 */ 00442 function flatInversePageTree_pid($idH,$a=array(),$pid=-1) { 00443 if (is_array($idH)) { 00444 $idH = array_reverse($idH); 00445 foreach ($idH as $k => $v) { 00446 $a[$v['uid']] = $pid; 00447 if (is_array($v['subrow'])) { 00448 $a = $this->flatInversePageTree_pid($v['subrow'],$a,$v['uid']); 00449 } 00450 } 00451 } 00452 return $a; 00453 } 00454 00455 00456 00457 00458 00459 00460 00461 00462 00463 00464 00465 /************************** 00466 * 00467 * Export 00468 * 00469 *************************/ 00470 00471 /** 00472 * Adds the record $row from $table. 00473 * No checking for relations done here. Pure data. 00474 * 00475 * @param string Table name 00476 * @param array Record row. 00477 * @param integer (Internal) if the record is added as a relation, this is set to the "level" it was on. 00478 * @return void 00479 */ 00480 function export_addRecord($table,$row,$relationLevel=0) { 00481 00482 t3lib_BEfunc::workspaceOL($table,$row); 00483 00484 if (strcmp($table,'') && is_array($row) && $row['uid']>0 && !$this->excludeMap[$table.':'.$row['uid']]) { 00485 if ($this->checkPID($table==='pages' ? $row['uid'] : $row['pid'])) { 00486 if (!isset($this->dat['records'][$table.':'.$row['uid']])) { 00487 00488 // Prepare header info: 00489 $headerInfo = array(); 00490 $headerInfo['uid'] = $row['uid']; 00491 $headerInfo['pid'] = $row['pid']; 00492 $headerInfo['title'] = t3lib_div::fixed_lgd_cs(t3lib_BEfunc::getRecordTitle($table,$row),40); 00493 $headerInfo['size'] = strlen(serialize($row)); 00494 if ($relationLevel) { 00495 $headerInfo['relationLevel'] = $relationLevel; 00496 } 00497 00498 // If record content is not too large in size, set the header content and add the rest: 00499 if ($headerInfo['size']<$this->maxRecordSize) { 00500 00501 // Set the header summary: 00502 $this->dat['header']['records'][$table][$row['uid']] = $headerInfo; 00503 00504 // Create entry in the PID lookup: 00505 $this->dat['header']['pid_lookup'][$row['pid']][$table][$row['uid']]=1; 00506 00507 // Initialize reference index object: 00508 $refIndexObj = t3lib_div::makeInstance('t3lib_refindex'); 00509 $refIndexObj->WSOL = TRUE; // Yes to workspace overlays for exporting.... 00510 00511 // Data: 00512 $this->dat['records'][$table.':'.$row['uid']] = array(); 00513 $this->dat['records'][$table.':'.$row['uid']]['data'] = $row; 00514 $this->dat['records'][$table.':'.$row['uid']]['rels'] = $refIndexObj->getRelations($table,$row); 00515 $this->errorLog = array_merge($this->errorLog,$refIndexObj->errorLog); // Merge error logs. 00516 00517 // Add information about the relations in the record in the header: 00518 $this->dat['header']['records'][$table][$row['uid']]['rels'] = $this->flatDBrels($this->dat['records'][$table.':'.$row['uid']]['rels']); 00519 00520 // Add information about the softrefs to header: 00521 $this->dat['header']['records'][$table][$row['uid']]['softrefs'] = $this->flatSoftRefs($this->dat['records'][$table.':'.$row['uid']]['rels']); 00522 00523 } else $this->error('Record '.$table.':'.$row['uid'].' was larger than maxRecordSize ('.t3lib_div::formatSize($this->maxRecordSize).')'); 00524 } else $this->error('Record '.$table.':'.$row['uid'].' already added.'); 00525 } else $this->error('Record '.$table.':'.$row['uid'].' was outside your DB mounts!'); 00526 } 00527 } 00528 00529 /** 00530 * This analyses the existing added records, finds all database relations to records and adds these records to the export file. 00531 * 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... 00532 * Call this BEFORE the ext_addFilesFromRelations (so files from added relations are also included of course) 00533 * 00534 * @param integer Recursion level 00535 * @return array overview of relations found and added: Keys [table]:[uid], values array with table and id 00536 * @see export_addFilesFromRelations() 00537 */ 00538 function export_addDBRelations($relationLevel=0) { 00539 global $TCA; 00540 00541 // Initialize: 00542 $addR = array(); 00543 00544 // Traverse all "rels" registered for "records" 00545 if (is_array($this->dat['records'])) { 00546 foreach ($this->dat['records'] as $k => $value) { 00547 if (is_array($this->dat['records'][$k])) { 00548 foreach ($this->dat['records'][$k]['rels'] as $fieldname => $vR) { 00549 #debug($vR); 00550 // For all DB types of relations: 00551 if ($vR['type']=='db') { 00552 foreach($vR['itemArray'] as $fI) { 00553 $this->export_addDBRelations_registerRelation($fI, $addR); 00554 } 00555 } 00556 00557 // For all flex/db types of relations: 00558 if ($vR['type']=='flex') { 00559 // DB relations in flex form fields: 00560 if (is_array($vR['flexFormRels']['db'])) { 00561 foreach($vR['flexFormRels']['db'] as $subList) { 00562 foreach($subList as $fI) { 00563 $this->export_addDBRelations_registerRelation($fI, $addR); 00564 } 00565 } 00566 } 00567 // DB oriented soft references in flex form fields: 00568 if (is_array($vR['flexFormRels']['softrefs'])) { 00569 foreach($vR['flexFormRels']['softrefs'] as $subList) { 00570 foreach($subList['keys'] as $spKey => $elements) { 00571 foreach($elements as $el) { 00572 if ($el['subst']['type'] === 'db' && $this->includeSoftref($el['subst']['tokenID'])) { 00573 list($tempTable, $tempUid) = explode(':', $el['subst']['recordRef']); 00574 $fI = array( 00575 'table' => $tempTable, 00576 'id' => $tempUid 00577 ); 00578 $this->export_addDBRelations_registerRelation($fI, $addR, $el['subst']['tokenID']); 00579 } 00580 } 00581 } 00582 } 00583 } 00584 } 00585 00586 // In any case, if there are soft refs: 00587 if (is_array($vR['softrefs']['keys'])) { 00588 foreach($vR['softrefs']['keys'] as $spKey => $elements) { 00589 foreach($elements as $el) { 00590 if ($el['subst']['type'] === 'db' && $this->includeSoftref($el['subst']['tokenID'])) { 00591 list($tempTable, $tempUid) = explode(':', $el['subst']['recordRef']); 00592 $fI = array( 00593 'table' => $tempTable, 00594 'id' => $tempUid 00595 ); 00596 $this->export_addDBRelations_registerRelation($fI, $addR, $el['subst']['tokenID']); 00597 } 00598 } 00599 } 00600 } 00601 } 00602 } 00603 } 00604 } else $this->error('There were no records available.'); 00605 00606 // Now, if there were new records to add, do so: 00607 if (count($addR)) { 00608 foreach($addR as $fI) { 00609 00610 // Get and set record: 00611 $row = t3lib_BEfunc::getRecord($fI['table'],$fI['id']); 00612 if (is_array($row)) { 00613 $this->export_addRecord($fI['table'],$row,$relationLevel+1); 00614 } 00615 00616 // Set status message 00617 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. 00618 $rId = $fI['table'].':'.$fI['id']; 00619 if (!isset($this->dat['records'][$rId])) { 00620 $this->dat['records'][$rId] = 'NOT_FOUND'; 00621 $this->error('Relation record '.$rId.' was not found!'); 00622 } 00623 } 00624 } 00625 } 00626 00627 // Return overview of relations found and added 00628 return $addR; 00629 } 00630 00631 /** 00632 * Helper function for export_addDBRelations() 00633 * 00634 * @param array Array with table/id keys to add 00635 * @param array Add array, passed by reference to be modified 00636 * @param string Softref Token ID, if applicable. 00637 * @return void 00638 * @see export_addDBRelations() 00639 */ 00640 function export_addDBRelations_registerRelation($fI, &$addR, $tokenID='') { 00641 global $TCA; 00642 00643 $rId = $fI['table'].':'.$fI['id']; 00644 if (isset($TCA[$fI['table']]) 00645 && !$this->isTableStatic($fI['table']) 00646 && !$this->isExcluded($fI['table'],$fI['id']) 00647 && (!$tokenID || $this->includeSoftref($tokenID)) 00648 && $this->inclRelation($fI['table']) 00649 ) { 00650 if (!isset($this->dat['records'][$rId])) { 00651 // Set this record to be included since it is not already. 00652 $addR[$rId] = $fI; 00653 } 00654 } 00655 } 00656 00657 /** 00658 * This adds all files in relations. 00659 * Call this method AFTER adding all records including relations. 00660 * 00661 * @return void 00662 * @see export_addDBRelations() 00663 */ 00664 function export_addFilesFromRelations() { 00665 00666 // Traverse all "rels" registered for "records" 00667 if (is_array($this->dat['records'])) { 00668 foreach ($this->dat['records'] as $k => $value) { 00669 if (is_array($this->dat['records'][$k]['rels'])) { 00670 foreach ($this->dat['records'][$k]['rels'] as $fieldname => $vR) { 00671 00672 // For all file type relations: 00673 if ($vR['type']=='file') { 00674 foreach($vR['newValueFiles'] as $key => $fI) { 00675 $this->export_addFile($fI, $k, $fieldname); 00676 // Remove the absolute reference to the file so it doesn't expose absolute paths from source server: 00677 unset($this->dat['records'][$k]['rels'][$fieldname]['newValueFiles'][$key]['ID_absFile']); 00678 } 00679 } 00680 00681 // For all flex type relations: 00682 if ($vR['type']=='flex') { 00683 if (is_array($vR['flexFormRels']['file'])) { 00684 foreach($vR['flexFormRels']['file'] as $key => $subList) { 00685 foreach($subList as $subKey => $fI) { 00686 $this->export_addFile($fI, $k, $fieldname); 00687 // Remove the absolute reference to the file so it doesn't expose absolute paths from source server: 00688 unset($this->dat['records'][$k]['rels'][$fieldname]['flexFormRels']['file'][$key][$subKey]['ID_absFile']); 00689 } 00690 } 00691 } 00692 00693 // DB oriented soft references in flex form fields: 00694 if (is_array($vR['flexFormRels']['softrefs'])) { 00695 foreach($vR['flexFormRels']['softrefs'] as $key => $subList) { 00696 foreach($subList['keys'] as $spKey => $elements) { 00697 foreach($elements as $subKey => $el) { 00698 if ($el['subst']['type'] === 'file' && $this->includeSoftref($el['subst']['tokenID'])) { 00699 00700 // Create abs path and ID for file: 00701 $ID_absFile = t3lib_div::getFileAbsFileName(PATH_site.$el['subst']['relFileName']); 00702 $ID = md5($ID_absFile); 00703 00704 if ($ID_absFile) { 00705 if (!$this->dat['files'][$ID]) { 00706 $fI = array( 00707 'filename' => basename($ID_absFile), 00708 'ID_absFile' => $ID_absFile, 00709 'ID' => $ID, 00710 'relFileName' => $el['subst']['relFileName'] 00711 ); 00712 $this->export_addFile($fI, '_SOFTREF_'); 00713 } 00714 $this->dat['records'][$k]['rels'][$fieldname]['flexFormRels']['softrefs'][$key]['keys'][$spKey][$subKey]['file_ID'] = $ID; 00715 } 00716 } 00717 } 00718 } 00719 } 00720 } 00721 } 00722 00723 // In any case, if there are soft refs: 00724 if (is_array($vR['softrefs']['keys'])) { 00725 foreach($vR['softrefs']['keys'] as $spKey => $elements) { 00726 foreach($elements as $subKey => $el) { 00727 if ($el['subst']['type'] === 'file' && $this->includeSoftref($el['subst']['tokenID'])) { 00728 00729 // Create abs path and ID for file: 00730 $ID_absFile = t3lib_div::getFileAbsFileName(PATH_site.$el['subst']['relFileName']); 00731 $ID = md5($ID_absFile); 00732 00733 if ($ID_absFile) { 00734 if (!$this->dat['files'][$ID]) { 00735 $fI = array( 00736 'filename' => basename($ID_absFile), 00737 'ID_absFile' => $ID_absFile, 00738 'ID' => $ID, 00739 'relFileName' => $el['subst']['relFileName'] 00740 ); 00741 $this->export_addFile($fI, '_SOFTREF_'); 00742 } 00743 $this->dat['records'][$k]['rels'][$fieldname]['softrefs']['keys'][$spKey][$subKey]['file_ID'] = $ID; 00744 } 00745 } 00746 } 00747 } 00748 } 00749 } 00750 } 00751 } 00752 } else $this->error('There were no records available.'); 00753 } 00754 00755 /** 00756 * Adds a files content to the export memory 00757 * 00758 * @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!) 00759 * @param string If the file is related to a record, this is the id on the form [table]:[id]. Information purposes only. 00760 * @param string If the file is related to a record, this is the field name it was related to. Information purposes only. 00761 * @return void 00762 */ 00763 function export_addFile($fI, $recordRef='', $fieldname='') { 00764 if (@is_file($fI['ID_absFile'])) { 00765 if (filesize($fI['ID_absFile']) < $this->maxFileSize) { 00766 $fileRec = array(); 00767 $fileRec['filesize'] = filesize($fI['ID_absFile']); 00768 $fileRec['filename'] = basename($fI['ID_absFile']); 00769 $fileRec['filemtime'] = filemtime($fI['ID_absFile']); 00770 //for internal type file_reference 00771 $fileRec['relFileRef'] = substr($fI['ID_absFile'], strlen(PATH_site)); 00772 if ($recordRef) { 00773 $fileRec['record_ref'] = $recordRef.'/'.$fieldname; 00774 } 00775 if ($fI['relFileName']) { 00776 $fileRec['relFileName'] = $fI['relFileName']; 00777 } 00778 00779 // Setting this data in the header 00780 $this->dat['header']['files'][$fI['ID']] = $fileRec; 00781 00782 // ... and for the recordlisting, why not let us know WHICH relations there was... 00783 if ($recordRef && $recordRef!=='_SOFTREF_') { 00784 $refParts = explode(':',$recordRef,2); 00785 if (!is_array($this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'])) { 00786 $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'] = array(); 00787 } 00788 $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'][] = $fI['ID']; 00789 } 00790 00791 // ... and finally add the heavy stuff: 00792 $fileRec['content'] = t3lib_div::getUrl($fI['ID_absFile']); 00793 $fileRec['content_md5'] = md5($fileRec['content']); 00794 $this->dat['files'][$fI['ID']] = $fileRec; 00795 00796 00797 // For soft references, do further processing: 00798 if ($recordRef === '_SOFTREF_') { 00799 00800 // RTE files? 00801 if ($RTEoriginal = $this->getRTEoriginalFilename(basename($fI['ID_absFile']))) { 00802 $RTEoriginal_absPath = dirname($fI['ID_absFile']).'/'.$RTEoriginal; 00803 if (@is_file($RTEoriginal_absPath)) { 00804 00805 $RTEoriginal_ID = md5($RTEoriginal_absPath); 00806 00807 $fileRec = array(); 00808 $fileRec['filesize'] = filesize($RTEoriginal_absPath); 00809 $fileRec['filename'] = basename($RTEoriginal_absPath); 00810 $fileRec['filemtime'] = filemtime($RTEoriginal_absPath); 00811 $fileRec['record_ref'] = '_RTE_COPY_ID:'.$fI['ID']; 00812 $this->dat['header']['files'][$fI['ID']]['RTE_ORIG_ID'] = $RTEoriginal_ID; 00813 00814 // Setting this data in the header 00815 $this->dat['header']['files'][$RTEoriginal_ID] = $fileRec; 00816 00817 // ... and finally add the heavy stuff: 00818 $fileRec['content'] = t3lib_div::getUrl($RTEoriginal_absPath); 00819 $fileRec['content_md5'] = md5($fileRec['content']); 00820 $this->dat['files'][$RTEoriginal_ID] = $fileRec; 00821 } else { 00822 $this->error('RTE original file "'.substr($RTEoriginal_absPath,strlen(PATH_site)).'" was not found!'); 00823 } 00824 } 00825 00826 // Files with external media? 00827 // This is only done with files grabbed by a softreference parser since it is deemed improbable that hard-referenced files should undergo this treatment. 00828 $html_fI = pathinfo(basename($fI['ID_absFile'])); 00829 if ($this->includeExtFileResources && t3lib_div::inList($this->extFileResourceExtensions,strtolower($html_fI['extension']))) { 00830 $uniquePrefix = '###' . md5($GLOBALS['EXEC_TIME']) . '###'; 00831 00832 if (strtolower($html_fI['extension'])==='css') { 00833 $prefixedMedias = explode($uniquePrefix, preg_replace('/(url[[:space:]]*\([[:space:]]*["\']?)([^"\')]*)(["\']?[[:space:]]*\))/i', '\1'.$uniquePrefix.'\2'.$uniquePrefix.'\3', $fileRec['content'])); 00834 } else { // html, htm: 00835 $htmlParser = t3lib_div::makeInstance('t3lib_parsehtml'); 00836 $prefixedMedias = explode($uniquePrefix, $htmlParser->prefixResourcePath($uniquePrefix,$fileRec['content'],array(),$uniquePrefix)); 00837 } 00838 00839 $htmlResourceCaptured = FALSE; 00840 foreach($prefixedMedias as $k => $v) { 00841 if ($k%2) { 00842 $EXTres_absPath = t3lib_div::resolveBackPath(dirname($fI['ID_absFile']).'/'.$v); 00843 $EXTres_absPath = t3lib_div::getFileAbsFileName($EXTres_absPath); 00844 if ($EXTres_absPath && t3lib_div::isFirstPartOfStr($EXTres_absPath,PATH_site.$this->fileadminFolderName.'/') && @is_file($EXTres_absPath)) { 00845 00846 $htmlResourceCaptured = TRUE; 00847 $EXTres_ID = md5($EXTres_absPath); 00848 $this->dat['header']['files'][$fI['ID']]['EXT_RES_ID'][] = $EXTres_ID; 00849 $prefixedMedias[$k] = '{EXT_RES_ID:'.$EXTres_ID.'}'; 00850 00851 // Add file to memory if it is not set already: 00852 if (!isset($this->dat['header']['files'][$EXTres_ID])) { 00853 $fileRec = array(); 00854 $fileRec['filesize'] = filesize($EXTres_absPath); 00855 $fileRec['filename'] = basename($EXTres_absPath); 00856 $fileRec['filemtime'] = filemtime($EXTres_absPath); 00857 $fileRec['record_ref'] = '_EXT_PARENT_:'.$fI['ID']; 00858 00859 $fileRec['parentRelFileName'] = $v; // Media relative to the HTML file. 00860 00861 // Setting this data in the header 00862 $this->dat['header']['files'][$EXTres_ID] = $fileRec; 00863 00864 // ... and finally add the heavy stuff: 00865 $fileRec['content'] = t3lib_div::getUrl($EXTres_absPath); 00866 $fileRec['content_md5'] = md5($fileRec['content']); 00867 $this->dat['files'][$EXTres_ID] = $fileRec; 00868 } 00869 } 00870 } 00871 } 00872 00873 if ($htmlResourceCaptured) { 00874 $this->dat['files'][$fI['ID']]['tokenizedContent'] = implode('', $prefixedMedias); 00875 } 00876 } 00877 } 00878 00879 } else $this->error($fI['ID_absFile'].' was larger ('.t3lib_div::formatSize(filesize($fI['ID_absFile'])).') than the maxFileSize ('.t3lib_div::formatSize($this->maxFileSize).')! Skipping.'); 00880 } else $this->error($fI['ID_absFile'].' was not a file! Skipping.'); 00881 } 00882 00883 /** 00884 * DB relations flattend to 1-dim array. 00885 * The list will be unique, no table/uid combination will appear twice. 00886 * 00887 * @param array 2-dim Array of database relations organized by table key 00888 * @return array 1-dim array where entries are table:uid and keys are array with table/id 00889 */ 00890 function flatDBrels($dbrels) { 00891 $list = array(); 00892 00893 foreach($dbrels as $dat) { 00894 if ($dat['type']=='db') { 00895 foreach($dat['itemArray'] as $i) { 00896 $list[$i['table'].':'.$i['id']] = $i; 00897 } 00898 } 00899 if ($dat['type']=='flex' && is_array($dat['flexFormRels']['db'])) { 00900 foreach($dat['flexFormRels']['db'] as $subList) { 00901 foreach($subList as $i) { 00902 $list[$i['table'].':'.$i['id']] = $i; 00903 } 00904 } 00905 } 00906 } 00907 return $list; 00908 } 00909 00910 /** 00911 * Soft References flattend to 1-dim array. 00912 * 00913 * @param array 2-dim Array of database relations organized by table key 00914 * @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 00915 */ 00916 function flatSoftRefs($dbrels) { 00917 $list = array(); 00918 #debug($dbrels); 00919 foreach($dbrels as $field => $dat) { 00920 if (is_array($dat['softrefs']['keys'])) { 00921 foreach($dat['softrefs']['keys'] as $spKey => $elements) { 00922 if (is_array($elements)) { 00923 foreach($elements as $subKey => $el) { 00924 $lKey = $field.':'.$spKey.':'.$subKey; 00925 $list[$lKey] = array_merge(array('field' => $field, 'spKey' => $spKey),$el); 00926 00927 // 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! 00928 if ($el['subst'] && $el['subst']['relFileName']) { 00929 $list[$lKey]['file_ID'] = md5(PATH_site.$el['subst']['relFileName']); 00930 } 00931 } 00932 } 00933 } 00934 } 00935 if ($dat['type']=='flex' && is_array($dat['flexFormRels']['softrefs'])) { 00936 foreach($dat['flexFormRels']['softrefs'] as $structurePath => $subSoftrefs) { 00937 if (is_array($subSoftrefs['keys'])) { 00938 foreach($subSoftrefs['keys'] as $spKey => $elements) { 00939 foreach($elements as $subKey => $el) { 00940 $lKey = $field.':'.$structurePath.':'.$spKey.':'.$subKey; 00941 $list[$lKey] = array_merge(array('field' => $field, 'spKey' => $spKey, 'structurePath' => $structurePath),$el); 00942 00943 // 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! 00944 if ($el['subst'] && $el['subst']['relFileName']) { 00945 $list[$lKey]['file_ID'] = md5(PATH_site.$el['subst']['relFileName']); 00946 } 00947 } 00948 } 00949 } 00950 } 00951 } 00952 } 00953 00954 #debug($list); 00955 return $list; 00956 } 00957 00958 00959 00960 00961 00962 00963 00964 00965 00966 00967 00968 /************************** 00969 * 00970 * File Output 00971 * 00972 *************************/ 00973 00974 /** 00975 * This compiles and returns the data content for an exported file 00976 * 00977 * @param string Type of output; "xml" gives xml, otherwise serialized array, possibly compressed. 00978 * @return string The output file stream 00979 */ 00980 function compileMemoryToFileContent($type='') { 00981 00982 if ($type=='xml') { 00983 $out = $this->createXML(); 00984 } else { 00985 $compress = $this->doOutputCompress(); 00986 $out = ''; 00987 00988 // adding header: 00989 $out.= $this->addFilePart(serialize($this->dat['header']),$compress); 00990 00991 // adding records: 00992 $out.= $this->addFilePart(serialize($this->dat['records']),$compress); 00993 00994 // adding files: 00995 $out.= $this->addFilePart(serialize($this->dat['files']),$compress); 00996 } 00997 00998 return $out; 00999 } 01000 01001 /** 01002 * Creates XML string from input array 01003 * 01004 * @return string XML content 01005 */ 01006 function createXML() { 01007 01008 // Options: 01009 $options = array( 01010 'alt_options' => array( 01011 '/header' => array( 01012 'disableTypeAttrib' => TRUE, 01013 'clearStackPath' => TRUE, 01014 'parentTagMap' => array( 01015 'files' => 'file', 01016 'records' => 'table', 01017 'table' => 'rec', 01018 'rec:rels' => 'relations', 01019 'relations' => 'element', 01020 'filerefs' => 'file', 01021 'pid_lookup' => 'page_contents', 01022 'header:relStaticTables' => 'static_tables', 01023 'static_tables' => 'tablename', 01024 'excludeMap' => 'item', 01025 'softrefCfg' => 'softrefExportMode', 01026 'extensionDependencies' => 'extkey', 01027 'softrefs' => 'softref_element', 01028 ), 01029 'alt_options' => array( 01030 '/pagetree' => array( 01031 'disableTypeAttrib' => TRUE, 01032 'useIndexTagForNum' => 'node', 01033 'parentTagMap' => array( 01034 'node:subrow' => 'node' 01035 ) 01036 ), 01037 '/pid_lookup/page_contents' => array( 01038 'disableTypeAttrib' => TRUE, 01039 'parentTagMap' => array( 01040 'page_contents' => 'table' 01041 ), 01042 'grandParentTagMap' => array( 01043 'page_contents/table' => 'item' 01044 ) 01045 ) 01046 ) 01047 ), 01048 '/records' => array( 01049 'disableTypeAttrib' => TRUE, 01050 'parentTagMap' => array( 01051 'records' => 'tablerow', 01052 'tablerow:data' => 'fieldlist', 01053 'tablerow:rels' => 'related', 01054 'related' => 'field', 01055 'field:itemArray' => 'relations', 01056 'field:newValueFiles' => 'filerefs', 01057 'field:flexFormRels' => 'flexform', 01058 'relations' => 'element', 01059 'filerefs' => 'file', 01060 'flexform:db' => 'db_relations', 01061 'flexform:file' => 'file_relations', 01062 'flexform:softrefs' => 'softref_relations', 01063 'softref_relations' => 'structurePath', 01064 'db_relations' => 'path', 01065 'file_relations' => 'path', 01066 'path' => 'element', 01067 'keys' => 'softref_key', 01068 'softref_key' => 'softref_element', 01069 ), 01070 'alt_options' => array( 01071 '/records/tablerow/fieldlist' => array( 01072 'useIndexTagForAssoc' => 'field', 01073 ) 01074 ) 01075 ), 01076 '/files' => array( 01077 'disableTypeAttrib' => TRUE, 01078 'parentTagMap' => array( 01079 'files' => 'file', 01080 ), 01081 ), 01082 ) 01083 ); 01084 01085 // Creating XML file from $outputArray: 01086 $charset = $this->dat['header']['charset'] ? $this->dat['header']['charset'] : 'iso-8859-1'; 01087 $XML = '<?xml version="1.0" encoding="'.$charset.'" standalone="yes" ?>'.LF; 01088 $XML.= t3lib_div::array2xml($this->dat,'',0,'T3RecordDocument',0,$options); 01089 01090 return $XML; 01091 } 01092 01093 /** 01094 * Returns true if the output should be compressed. 01095 * 01096 * @return boolean True if compression is possible AND requested. 01097 */ 01098 function doOutputCompress() { 01099 return $this->compress && !$this->dontCompress; 01100 } 01101 01102 /** 01103 * Returns a content part for a filename being build. 01104 * 01105 * @param array Data to store in part 01106 * @param boolean Compress file? 01107 * @return string Content stream. 01108 */ 01109 function addFilePart($data, $compress=FALSE) { 01110 if ($compress) $data = gzcompress($data); 01111 return md5($data).':'. 01112 ($compress?'1':'0').':'. 01113 str_pad(strlen($data),10,'0',STR_PAD_LEFT).':'. 01114 $data.':'; 01115 } 01116 01117 01118 01119 01120 01121 01122 01123 01124 01125 01126 01127 01128 01129 01130 /*********************** 01131 * 01132 * Import 01133 * 01134 ***********************/ 01135 01136 /** 01137 * Imports the internal data array to $pid. 01138 * 01139 * @param integer Page ID in which to import the content 01140 * @return void ... 01141 */ 01142 function importData($pid) { 01143 01144 // Set this flag to indicate that an import is being/has been done. 01145 $this->doesImport = 1; 01146 01147 // Initialize: 01148 // 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. 01149 $this->import_mapId = array(); 01150 $this->import_newId = array(); 01151 $this->import_newId_pids = array(); 01152 01153 // Temporary files stack initialized: 01154 $this->unlinkFiles = array(); 01155 $this->alternativeFileName = array(); 01156 $this->alternativeFilePath = array(); 01157 01158 // Write records, first pages, then the rest 01159 // Fields with "hard" relations to database, files and flexform fields are kept empty during this run 01160 $this->writeRecords_pages($pid); 01161 $this->writeRecords_records($pid); 01162 01163 // Finally all the file and DB record references must be fixed. This is done after all records have supposedly been written to database: 01164 // $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. 01165 $this->setRelations(); 01166 01167 // 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): 01168 $this->setFlexFormRelations(); 01169 01170 // Unlink temporary files: 01171 $this->unlinkTempFiles(); 01172 01173 // Finally, traverse all records and process softreferences with substitution attributes. 01174 $this->processSoftReferences(); 01175 } 01176 01177 /** 01178 * Writing pagetree/pages to database: 01179 * 01180 * @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 01181 * @return void 01182 * @see writeRecords_records() 01183 */ 01184 function writeRecords_pages($pid) { 01185 01186 // First, write page structure if any: 01187 if (is_array($this->dat['header']['records']['pages'])) { 01188 01189 // $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. 01190 $pageRecords = $this->dat['header']['records']['pages']; 01191 $this->import_data = array(); 01192 01193 // First add page tree if any 01194 if (is_array($this->dat['header']['pagetree'])) { 01195 $pagesFromTree = $this->flatInversePageTree($this->dat['header']['pagetree']); 01196 foreach($pagesFromTree as $uid) { 01197 $thisRec = $this->dat['header']['records']['pages'][$uid]; 01198 // PID: Set the main $pid, unless a NEW-id is found 01199 $setPid = isset($this->import_newId_pids[$thisRec['pid']]) ? $this->import_newId_pids[$thisRec['pid']] : $pid; 01200 $this->addSingle('pages',$uid,$setPid); 01201 unset($pageRecords[$uid]); 01202 } 01203 } 01204 01205 // Then add all remaining pages not in tree on root level: 01206 if (count($pageRecords)) { 01207 $remainingPageUids = array_keys($pageRecords); 01208 foreach($remainingPageUids as $pUid) { 01209 $this->addSingle('pages',$pUid,$pid); 01210 } 01211 } 01212 01213 // Now write to database: 01214 $tce = $this->getNewTCE(); 01215 $this->callHook('before_writeRecordsPages', array( 01216 'tce' => &$tce, 01217 'data' => &$this->import_data, 01218 )); 01219 $tce->suggestedInsertUids = $this->suggestedInsertUids; 01220 $tce->start($this->import_data,Array()); 01221 $tce->process_datamap(); 01222 $this->callHook('after_writeRecordsPages', array( 01223 'tce' => &$tce 01224 )); 01225 01226 // post-processing: Registering new ids (end all tcemain sessions with this) 01227 $this->addToMapId($tce->substNEWwithIDs); 01228 01229 // In case of an update, order pages from the page tree correctly: 01230 if ($this->update && is_array($this->dat['header']['pagetree'])) { 01231 $this->writeRecords_pages_order($pid); 01232 } 01233 } 01234 } 01235 01236 /** 01237 * Organize all updated pages in page tree so they are related like in the import file 01238 * Only used for updates and when $this->dat['header']['pagetree'] is an array. 01239 * 01240 * @param integer Page id in which to import 01241 * @return void 01242 * @access private 01243 * @see writeRecords_pages(), writeRecords_records_order() 01244 */ 01245 function writeRecords_pages_order($pid) { 01246 $cmd_data = array(); 01247 01248 // Get uid-pid relations and traverse them in order to map to possible new IDs 01249 $pidsFromTree = $this->flatInversePageTree_pid($this->dat['header']['pagetree']); 01250 01251 foreach($pidsFromTree as $origPid => $newPid) { 01252 if ($newPid>=0 && $this->dontIgnorePid('pages', $origPid)) { 01253 if (substr($this->import_newId_pids[$origPid],0,3)==='NEW') { // If the page had a new id (because it was created) use that instead! 01254 01255 if ($this->import_mapId['pages'][$origPid]) { 01256 $mappedPid = $this->import_mapId['pages'][$origPid]; 01257 $cmd_data['pages'][$mappedPid]['move'] = $newPid; 01258 } 01259 } else { 01260 $cmd_data['pages'][$origPid]['move'] = $newPid; 01261 } 01262 } 01263 } 01264 01265 // Execute the move commands if any: 01266 if (count($cmd_data)) { 01267 $tce = $this->getNewTCE(); 01268 $this->callHook('before_writeRecordsPagesOrder', array( 01269 'tce' => &$tce, 01270 'data' => &$cmd_data, 01271 )); 01272 $tce->start(Array(),$cmd_data); 01273 $tce->process_cmdmap(); 01274 $this->callHook('after_writeRecordsPagesOrder', array( 01275 'tce' => &$tce, 01276 )); 01277 } 01278 01279 } 01280 01281 /** 01282 * Write all database records except pages (writtein in writeRecords_pages()) 01283 * 01284 * @param integer Page id in which to import 01285 * @return void 01286 * @see writeRecords_pages() 01287 */ 01288 function writeRecords_records($pid) { 01289 global $TCA; 01290 01291 // Write the rest of the records 01292 $this->import_data = array(); 01293 if (is_array($this->dat['header']['records'])) { 01294 foreach ($this->dat['header']['records'] as $table => $recs) { 01295 if ($table!='pages') { 01296 foreach ($recs as $uid => $thisRec) { 01297 // PID: Set the main $pid, unless a NEW-id is found 01298 $setPid = isset($this->import_mapId['pages'][$thisRec['pid']]) ? $this->import_mapId['pages'][$thisRec['pid']] : $pid; 01299 if (is_array($TCA[$table]) && $TCA[$table]['ctrl']['rootLevel']) { 01300 $setPid = 0; 01301 } 01302 01303 // Add record: 01304 $this->addSingle($table,$uid,$setPid); 01305 } 01306 } 01307 } 01308 } else $this->error('Error: No records defined in internal data array.'); 01309 01310 // Now write to database: 01311 $tce = $this->getNewTCE(); 01312 $this->callHook('before_writeRecordsRecords', array( 01313 'tce' => &$tce, 01314 'data' => &$this->import_data, 01315 )); 01316 $tce->suggestedInsertUids = $this->suggestedInsertUids; 01317 $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. 01318 $tce->start($this->import_data,Array()); 01319 $tce->process_datamap(); 01320 $this->callHook('after_writeRecordsRecords', array( 01321 'tce' => &$tce, 01322 )); 01323 01324 // post-processing: Removing files and registering new ids (end all tcemain sessions with this) 01325 $this->addToMapId($tce->substNEWwithIDs); 01326 01327 // In case of an update, order pages from the page tree correctly: 01328 if ($this->update) { 01329 $this->writeRecords_records_order($pid); 01330 } 01331 } 01332 01333 /** 01334 * Organize all updated record to their new positions. 01335 * Only used for updates 01336 * 01337 * @param integer Main PID into which we import. 01338 * @return void 01339 * @access private 01340 * @see writeRecords_records(), writeRecords_pages_order() 01341 */ 01342 function writeRecords_records_order($mainPid) { 01343 $cmd_data = array(); 01344 01345 if (is_array($this->dat['header']['pagetree'])) { 01346 $pagesFromTree = $this->flatInversePageTree($this->dat['header']['pagetree']); 01347 } else $pagesFromTree = array(); 01348 01349 if (is_array($this->dat['header']['pid_lookup'])) { 01350 foreach($this->dat['header']['pid_lookup'] as $pid => $recList) { 01351 $newPid = isset($this->import_mapId['pages'][$pid]) ? $this->import_mapId['pages'][$pid] : $mainPid; 01352 01353 if (t3lib_div::testInt($newPid)) { 01354 foreach($recList as $tableName => $uidList) { 01355 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!) 01356 $uidList = array_reverse(array_keys($uidList)); 01357 foreach($uidList as $uid) { 01358 if ($this->dontIgnorePid($tableName, $uid)) { 01359 $cmd_data[$tableName][$uid]['move'] = $newPid; 01360 } else { 01361 // nothing 01362 } 01363 } 01364 } 01365 } 01366 } 01367 } 01368 } 01369 01370 // Execute the move commands if any: 01371 if (count($cmd_data)) { 01372 $tce = $this->getNewTCE(); 01373 $this->callHook('before_writeRecordsRecordsOrder', array( 01374 'tce' => &$tce, 01375 'data' => &$cmd_data, 01376 )); 01377 $tce->start(Array(),$cmd_data); 01378 $tce->process_cmdmap(); 01379 $this->callHook('after_writeRecordsRecordsOrder', array( 01380 'tce' => &$tce, 01381 )); 01382 } 01383 } 01384 01385 /** 01386 * Adds a single record to the $importData array. Also copies files to tempfolder. 01387 * However all File/DB-references and flexform field contents are set to blank for now! That is done with setRelations() later 01388 * 01389 * @param string Table name (from import memory) 01390 * @param integer Record UID (from import memory) 01391 * @param integer Page id 01392 * @return void 01393 * @see writeRecords() 01394 */ 01395 function addSingle($table,$uid,$pid) { 01396 if ($this->import_mode[$table.':'.$uid]!=='exclude') { 01397 $record = $this->dat['records'][$table.':'.$uid]['data']; 01398 if (is_array($record)) { 01399 01400 if ($this->update && $this->doesRecordExist($table,$uid) && $this->import_mode[$table.':'.$uid]!=='as_new') { 01401 $ID = $uid; 01402 } else { 01403 #debug($this->import_mode[$table.':'.$uid],$table.':'.$uid); 01404 $ID = uniqid('NEW'); 01405 } 01406 $this->import_newId[$table.':'.$ID] = array('table' => $table, 'uid' => $uid); 01407 if ($table=='pages') $this->import_newId_pids[$uid] = $ID; 01408 01409 // Set main record data: 01410 $this->import_data[$table][$ID] = $record; 01411 $this->import_data[$table][$ID]['tx_impexp_origuid'] = $this->import_data[$table][$ID]['uid']; 01412 01413 // Reset permission data: 01414 if ($table==='pages') { 01415 // Have to reset the user/group IDs so pages are owned by importing user. Otherwise strange things may happen for non-admins! 01416 unset($this->import_data[$table][$ID]['perms_userid']); 01417 unset($this->import_data[$table][$ID]['perms_groupid']); 01418 01419 // user/group/everybody settings is kept - but these might still conflict with possibilities for writing the content!" 01420 #unset($this->import_data[$table][$ID]['perms_user']); 01421 #unset($this->import_data[$table][$ID]['perms_group']); 01422 #unset($this->import_data[$table][$ID]['perms_everybody']); 01423 } 01424 01425 // PID and UID: 01426 unset($this->import_data[$table][$ID]['uid']); 01427 if (t3lib_div::testInt($ID)) { // Updates: 01428 unset($this->import_data[$table][$ID]['pid']); 01429 } else { // Inserts: 01430 $this->import_data[$table][$ID]['pid'] = $pid; 01431 01432 if ((($this->import_mode[$table.':'.$uid]==='force_uid' && $this->update) || $this->force_all_UIDS) && $GLOBALS['BE_USER']->isAdmin()) { 01433 #debug($this->import_mode[$table.':'.$uid],$table.':'.$uid); 01434 $this->import_data[$table][$ID]['uid'] = $uid; 01435 $this->suggestedInsertUids[$table.':'.$uid] = 'DELETE'; 01436 } 01437 } 01438 01439 // Setting db/file blank: 01440 foreach ($this->dat['records'][$table.':'.$uid]['rels'] as $field => $config) { 01441 switch((string)$config['type']) { 01442 case 'db': 01443 case 'file': 01444 // Fixed later in ->setRelations() [because we need to know ALL newly created IDs before we can map relations!] 01445 // In the meantime we set NO values for relations: 01446 $this->import_data[$table][$ID][$field] = ''; 01447 break; 01448 case 'flex': 01449 // Fixed later in setFlexFormRelations() 01450 // 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!) 01451 $this->import_data[$table][$ID][$field] = ''; 01452 break; 01453 } 01454 } 01455 } elseif ($table.':'.$uid != 'pages:0') { // On root level we don't want this error message. 01456 $this->error('Error: no record was found in data array!',1); 01457 } 01458 } 01459 } 01460 01461 /** 01462 * Registers the substNEWids in memory. 01463 * 01464 * @param array $substNEWwithIDs from tcemain to be merged into internal mapping variable in this object 01465 * @return void 01466 * @see writeRecords() 01467 */ 01468 function addToMapId($substNEWwithIDs) { 01469 foreach ($this->import_data as $table => $recs) { 01470 foreach ($recs as $id => $value) { 01471 $old_uid = $this->import_newId[$table.':'.$id]['uid']; 01472 if (isset($substNEWwithIDs[$id])) { 01473 $this->import_mapId[$table][$old_uid] = $substNEWwithIDs[$id]; 01474 } elseif ($this->update) { 01475 $this->import_mapId[$table][$old_uid] = $id; // Map same ID to same ID.... 01476 } 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); 01477 } 01478 } 01479 } 01480 01481 /** 01482 * Returns a new $TCE object 01483 * 01484 * @return object $TCE object 01485 */ 01486 function getNewTCE() { 01487 $tce = t3lib_div::makeInstance('t3lib_TCEmain'); 01488 $tce->stripslashes_values = 0; 01489 $tce->dontProcessTransformations = 1; 01490 $tce->enableLogging = $this->enableLogging; 01491 $tce->alternativeFileName = $this->alternativeFileName; 01492 $tce->alternativeFilePath = $this->alternativeFilePath; 01493 return $tce; 01494 } 01495 01496 /** 01497 * Cleaning up all the temporary files stored in typo3temp/ folder 01498 * 01499 * @return void 01500 */ 01501 function unlinkTempFiles() { 01502 foreach($this->unlinkFiles as $fileName) { 01503 if (t3lib_div::isFirstPartOfStr($fileName, PATH_site.'typo3temp/')) { 01504 t3lib_div::unlink_tempfile($fileName); 01505 clearstatcache(); 01506 if (is_file($fileName)) { 01507 $this->error('Error: '.$fileName.' was NOT unlinked as it should have been!',1); 01508 } 01509 } else $this->error('Error: '.$fileName.' was not in temp-path. Not removed!',1); 01510 } 01511 $this->unlinkFiles = array(); 01512 } 01513 01514 01515 01516 01517 01518 01519 01520 01521 01522 01523 01524 01525 01526 /*************************** 01527 * 01528 * Import / Relations setting 01529 * 01530 ***************************/ 01531 01532 /** 01533 * 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) 01534 * Relations in flexform fields are processed in setFlexFormRelations() after this function 01535 * 01536 * @return void 01537 * @see setFlexFormRelations() 01538 */ 01539 function setRelations() { 01540 global $TCA; 01541 01542 $updateData = array(); 01543 01544 // import_newId contains a register of all records that was in the import memorys "records" key 01545 foreach ($this->import_newId as $nId => $dat) { 01546 $table = $dat['table']; 01547 $uid = $dat['uid']; // original UID - NOT the new one! 01548 01549 // If the record has been written and received a new id, then proceed: 01550 if (is_array($this->import_mapId[$table]) && isset($this->import_mapId[$table][$uid])) { 01551 $thisNewUid = t3lib_BEfunc::wsMapId($table,$this->import_mapId[$table][$uid]); 01552 01553 if (is_array($this->dat['records'][$table.':'.$uid]['rels'])) { 01554 01555 // Traverse relation fields of each record 01556 foreach ($this->dat['records'][$table.':'.$uid]['rels'] as $field => $config) { 01557 switch((string)$config['type']) { 01558 case 'db': 01559 if (is_array($config['itemArray']) && count($config['itemArray'])) { 01560 $valArray = $this->setRelations_db($config['itemArray']); 01561 $updateData[$table][$thisNewUid][$field] = implode(',',$valArray); // List of [table]_[uid] 01562 } 01563 break; 01564 case 'file': 01565 if (is_array($config['newValueFiles']) && count($config['newValueFiles'])) { 01566 $valArr = array(); 01567 foreach($config['newValueFiles'] as $fI) { 01568 $valArr[] = $this->import_addFileNameToBeCopied($fI); 01569 } 01570 $updateData[$table][$thisNewUid][$field] = implode(',',$valArr); // List of absolute files 01571 } 01572 break; 01573 } 01574 } 01575 } else $this->error('Error: no record was found in data array!',1); 01576 } else $this->error('Error: this records is NOT created it seems! ('.$table.':'.$uid.')',1); 01577 } 01578 if (count($updateData)) { 01579 $tce = $this->getNewTCE(); 01580 $this->callHook('before_setRelation', array( 01581 'tce' => &$tce, 01582 'data' => &$updateData, 01583 )); 01584 $tce->start($updateData,Array()); 01585 $tce->process_datamap(); 01586 $this->callHook('after_setRelations', array( 01587 'tce' => &$tce, 01588 )); 01589 } 01590 } 01591 01592 /** 01593 * Maps relations for database 01594 * 01595 * @param array Array of item sets (table/uid) from a dbAnalysis object 01596 * @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. 01597 */ 01598 function setRelations_db($itemArray) { 01599 $valArray = array(); 01600 01601 foreach($itemArray as $relDat) { 01602 if (is_array($this->import_mapId[$relDat['table']]) && isset($this->import_mapId[$relDat['table']][$relDat['id']])) { 01603 01604 #debug('FOUND: '.$relDat['table'].':'.$relDat['id']); 01605 $valArray[] = $relDat['table'].'_'.$this->import_mapId[$relDat['table']][$relDat['id']]; 01606 } 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. 01607 01608 #debug('STATIC: '.$relDat['table'].':'.$relDat['id']); 01609 $valArray[] = $relDat['table'].'_'.$relDat['id']; 01610 } else { 01611 01612 $this->error('Lost relation: '.$relDat['table'].':'.$relDat['id'],1); 01613 } 01614 } 01615 01616 return $valArray; 01617 } 01618 01619 /** 01620 * Writes the file from import array to temp dir and returns the filename of it. 01621 * 01622 * @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" 01623 * @return string Absolute filename of the temporary filename of the file. In ->alternativeFileName the original name is set. 01624 */ 01625 function import_addFileNameToBeCopied($fI) { 01626 if (is_array($this->dat['files'][$fI['ID']])) { 01627 $tmpFile = t3lib_div::tempnam('import_temp_'); 01628 t3lib_div::writeFile($tmpFile,$this->dat['files'][$fI['ID']]['content']); 01629 clearstatcache(); 01630 if (@is_file($tmpFile)) { 01631 $this->unlinkFiles[] = $tmpFile; 01632 if (filesize($tmpFile)==$this->dat['files'][$fI['ID']]['filesize']) { 01633 $this->alternativeFileName[$tmpFile] = $fI['filename']; 01634 $this->alternativeFilePath[$tmpFile] = $this->dat['files'][$fI['ID']]['relFileRef']; 01635 01636 return $tmpFile; 01637 } else $this->error('Error: temporary file '.$tmpFile.' had a size ('.filesize($tmpFile).') different from the original ('.$this->dat['files'][$fI['ID']]['filesize'].')',1); 01638 } else $this->error('Error: temporary file '.$tmpFile.' was not written as it should have been!',1); 01639 } else $this->error('Error: No file found for ID '.$fI['ID'],1); 01640 } 01641 01642 /** 01643 * 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. 01644 * 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! 01645 * 01646 * @return void 01647 * @see setRelations() 01648 */ 01649 function setFlexFormRelations() { 01650 global $TCA; 01651 01652 $updateData = array(); 01653 // import_newId contains a register of all records that was in the import memorys "records" key 01654 foreach ($this->import_newId as $nId => $dat) { 01655 $table = $dat['table']; 01656 $uid = $dat['uid']; // original UID - NOT the new one! 01657 01658 // If the record has been written and received a new id, then proceed: 01659 if (is_array($this->import_mapId[$table]) && isset($this->import_mapId[$table][$uid])) { 01660 $thisNewUid = t3lib_BEfunc::wsMapId($table,$this->import_mapId[$table][$uid]); 01661 if (is_array($this->dat['records'][$table.':'.$uid]['rels'])) { 01662 t3lib_div::loadTCA($table); 01663 01664 // Traverse relation fields of each record 01665 foreach ($this->dat['records'][$table.':'.$uid]['rels'] as $field => $config) { 01666 switch((string)$config['type']) { 01667 case 'flex': 01668 // Get XML content and set as default value (string, non-processed): 01669 $updateData[$table][$thisNewUid][$field] = $this->dat['records'][$table.':'.$uid]['data'][$field]; 01670 01671 // If there has been registered relations inside the flex form field, run processing on the content: 01672 if (count($config['flexFormRels']['db']) || count($config['flexFormRels']['file'])) { 01673 $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.) 01674 $conf = $TCA[$table]['columns'][$field]['config']; 01675 if (is_array($origRecordRow) && is_array($conf) && $conf['type']==='flex') { 01676 // Get current data structure and value array: 01677 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $origRecordRow, $table); 01678 $currentValueArray = t3lib_div::xml2array($updateData[$table][$thisNewUid][$field]); 01679 // Do recursive processing of the XML data: 01680 $iteratorObj = t3lib_div::makeInstance('t3lib_TCEmain'); 01681 $iteratorObj->callBackObj = $this; 01682 $currentValueArray['data'] = $iteratorObj->checkValue_flex_procInData( 01683 $currentValueArray['data'], 01684 array(), // Not used. 01685 array(), // Not used. 01686 $dataStructArray, 01687 array($table,$thisNewUid,$field,$config), // Parameters. 01688 'remapListedDBRecords_flexFormCallBack' 01689 ); 01690 // The return value is set as an array which means it will be processed by tcemain for file and DB references! 01691 if (is_array($currentValueArray['data'])) { 01692 $updateData[$table][$thisNewUid][$field] = $currentValueArray; 01693 } 01694 } 01695 } 01696 break; 01697 } 01698 } 01699 } else $this->error('Error: no record was found in data array!',1); 01700 } else $this->error('Error: this records is NOT created it seems! ('.$table.':'.$uid.')',1); 01701 } 01702 if (count($updateData)) { 01703 $tce = $this->getNewTCE(); 01704 $this->callHook('before_setFlexFormRelations', array( 01705 'tce' => &$tce, 01706 'data' => &$updateData, 01707 )); 01708 $tce->start($updateData,Array()); 01709 $tce->process_datamap(); 01710 $this->callHook('after_setFlexFormRelations', array( 01711 'tce' => &$tce, 01712 )); 01713 } 01714 } 01715 01716 /** 01717 * Callback function for traversing the FlexForm structure in relation to remapping database relations 01718 * 01719 * @param array Set of parameters in numeric array: table, uid, field 01720 * @param array TCA config for field (from Data Structure of course) 01721 * @param string Field value (from FlexForm XML) 01722 * @param string Not used 01723 * @param string Not used 01724 * @param string Path of where the data structure of the element is found 01725 * @return array Array where the "value" key carries the value. 01726 * @see setFlexFormRelations() 01727 */ 01728 function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) { 01729 01730 // Extract parameters: 01731 list($table,$uid,$field,$config) = $pParams; 01732 01733 // In case the $path is used as index without a trailing slash we will remove that 01734 if (!is_array($config['flexFormRels']['db'][$path]) && is_array($config['flexFormRels']['db'][rtrim($path, '/')])) { 01735 $path = rtrim($path, '/'); 01736 } 01737 if (is_array($config['flexFormRels']['db'][$path])) { 01738 $valArray = $this->setRelations_db($config['flexFormRels']['db'][$path]); 01739 $dataValue = implode(',',$valArray); 01740 } 01741 01742 if (is_array($config['flexFormRels']['file'][$path])) { 01743 foreach($config['flexFormRels']['file'][$path] as $fI) { 01744 $valArr[] = $this->import_addFileNameToBeCopied($fI); 01745 } 01746 $dataValue = implode(',',$valArr); 01747 } 01748 01749 return array('value' => $dataValue); 01750 } 01751 01752 01753 01754 01755 01756 01757 01758 01759 01760 01761 01762 /************************** 01763 * 01764 * Import / Soft References 01765 * 01766 *************************/ 01767 01768 /** 01769 * Processing of soft references 01770 * 01771 * @return void 01772 */ 01773 function processSoftReferences() { 01774 global $TCA; 01775 01776 // Initialize: 01777 $inData = array(); 01778 01779 // Traverse records: 01780 if (is_array($this->dat['header']['records'])) { 01781 foreach($this->dat['header']['records'] as $table => $recs) { 01782 foreach($recs as $uid => $thisRec) { 01783 01784 // If there are soft references defined, traverse those: 01785 if (isset($TCA[$table]) && is_array($thisRec['softrefs'])) { 01786 t3lib_div::loadTCA($table); 01787 01788 // 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. 01789 $fieldsIndex = array(); 01790 foreach($thisRec['softrefs'] as $softrefDef) { 01791 01792 // If a substitution token is set: 01793 if ($softrefDef['field'] && is_array($softrefDef['subst']) && $softrefDef['subst']['tokenID']) { 01794 $fieldsIndex[$softrefDef['field']][$softrefDef['subst']['tokenID']] = $softrefDef; 01795 } 01796 } 01797 01798 // The new id: 01799 $thisNewUid = t3lib_BEfunc::wsMapId($table,$this->import_mapId[$table][$uid]); 01800 01801 // Now, if there are any fields that require substitution to be done, lets go for that: 01802 foreach($fieldsIndex as $field => $softRefCfgs) { 01803 if (is_array($TCA[$table]['columns'][$field])) { 01804 $conf = $TCA[$table]['columns'][$field]['config']; 01805 if ($conf['type']==='flex') { 01806 01807 $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.) 01808 if (is_array($origRecordRow)) { 01809 01810 // Get current data structure and value array: 01811 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $origRecordRow, $table); 01812 $currentValueArray = t3lib_div::xml2array($origRecordRow[$field]); 01813 01814 // Do recursive processing of the XML data: 01815 $iteratorObj = t3lib_div::makeInstance('t3lib_TCEmain'); 01816 $iteratorObj->callBackObj = $this; 01817 $currentValueArray['data'] = $iteratorObj->checkValue_flex_procInData( 01818 $currentValueArray['data'], 01819 array(), // Not used. 01820 array(), // Not used. 01821 $dataStructArray, 01822 array($table,$uid,$field,$softRefCfgs), // Parameters (using old UID on purpose!) 01823 'processSoftReferences_flexFormCallBack' 01824 ); 01825 01826 // The return value is set as an array which means it will be processed by tcemain for file and DB references! 01827 if (is_array($currentValueArray['data'])) { 01828 $inData[$table][$thisNewUid][$field] = $currentValueArray; 01829 } 01830 } 01831 } else { 01832 // Get tokenizedContent string and proceed only if that is not blank: 01833 $tokenizedContent = $this->dat['records'][$table.':'.$uid]['rels'][$field]['softrefs']['tokenizedContent']; 01834 if (strlen($tokenizedContent) && is_array($softRefCfgs)) { 01835 $inData[$table][$thisNewUid][$field] = $this->processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid); 01836 } 01837 } 01838 } 01839 } 01840 } 01841 } 01842 } 01843 } 01844 01845 // Now write to database: 01846 $tce = $this->getNewTCE(); 01847 $this->callHook('before_processSoftReferences', array( 01848 'tce' => &$tce, 01849 'data' => &$inData, 01850 )); 01851 $tce->enableLogging = TRUE; 01852 $tce->start($inData, Array()); 01853 $tce->process_datamap(); 01854 $this->callHook('after_processSoftReferences', array( 01855 'tce' => &$tce, 01856 )); 01857 } 01858 01859 /** 01860 * Callback function for traversing the FlexForm structure in relation to remapping softreference relations 01861 * 01862 * @param array Set of parameters in numeric array: table, uid, field 01863 * @param array TCA config for field (from Data Structure of course) 01864 * @param string Field value (from FlexForm XML) 01865 * @param string Not used 01866 * @param string Not used 01867 * @param string Path of where the data structure where the element is found 01868 * @return array Array where the "value" key carries the value. 01869 * @see setFlexFormRelations() 01870 */ 01871 function processSoftReferences_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) { 01872 01873 // Extract parameters: 01874 list($table,$origUid,$field,$softRefCfgs) = $pParams; 01875 01876 if (is_array($softRefCfgs)) { 01877 01878 // First, find all soft reference configurations for this structure path (they are listed flat in the header): 01879 $thisSoftRefCfgList = array(); 01880 foreach($softRefCfgs as $sK => $sV) { 01881 if ($sV['structurePath']===$path) { 01882 $thisSoftRefCfgList[$sK] = $sV; 01883 } 01884 } 01885 01886 // If any was found, do processing: 01887 if (count($thisSoftRefCfgList)) { 01888 01889 // Get tokenizedContent string and proceed only if that is not blank: 01890 $tokenizedContent = $this->dat['records'][$table.':'.$origUid]['rels'][$field]['flexFormRels']['softrefs'][$path]['tokenizedContent']; 01891 if (strlen($tokenizedContent)) { 01892 $dataValue = $this->processSoftReferences_substTokens($tokenizedContent, $thisSoftRefCfgList, $table, $origUid); 01893 } 01894 } 01895 } 01896 01897 // Return 01898 return array('value' => $dataValue); 01899 } 01900 01901 /** 01902 * Substition of softreference tokens 01903 * 01904 * @param string Content of field with soft reference tokens in. 01905 * @param array Soft reference configurations 01906 * @param string Table for which the processing occurs 01907 * @param string UID of record from table 01908 * @return string The input content with tokens substituted according to entries in softRefCfgs 01909 */ 01910 function processSoftReferences_substTokens($tokenizedContent, $softRefCfgs, $table, $uid) { 01911 01912 // traverse each softref type for this field: 01913 foreach($softRefCfgs as $cfg) { 01914 01915 // Get token ID: 01916 $tokenID = $cfg['subst']['tokenID']; 01917 01918 // Default is current token value: 01919 $insertValue = $cfg['subst']['tokenValue']; 01920 01921 // Based on mode: 01922 switch((string)$this->softrefCfg[$tokenID]['mode']) { 01923 case 'exclude': 01924 // Exclude is a simple passthrough of the value 01925 break; 01926 case 'editable': 01927 // Editable always picks up the value from this input array: 01928 $insertValue = $this->softrefInputValues[$tokenID]; 01929 break; 01930 default: 01931 // Mapping IDs/creating files: Based on type, look up new value: 01932 switch((string)$cfg['subst']['type']) { 01933 case 'db': 01934 default: 01935 // Trying to map database element if found in the mapID array: 01936 list($tempTable,$tempUid) = explode(':',$cfg['subst']['recordRef']); 01937 if (isset($this->import_mapId[$tempTable][$tempUid])) { 01938 $insertValue = t3lib_BEfunc::wsMapId($tempTable,$this->import_mapId[$tempTable][$tempUid]); 01939 01940 // 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! 01941 if ($tempTable==='pages' && !t3lib_div::testInt($cfg['subst']['tokenValue'])) { 01942 $recWithUniqueValue = t3lib_BEfunc::getRecord($tempTable,$insertValue, 'alias'); 01943 if ($recWithUniqueValue['alias']) { 01944 $insertValue = $recWithUniqueValue['alias']; 01945 } 01946 } 01947 } 01948 break; 01949 break; 01950 case 'file': 01951 // Create / Overwrite file: 01952 $insertValue = $this->processSoftReferences_saveFile($cfg['subst']['relFileName'], $cfg, $table, $uid); 01953 break; 01954 } 01955 break; 01956 } 01957 01958 // Finally, swap the soft reference token in tokenized content with the insert value: 01959 $tokenizedContent = str_replace('{softref:'.$tokenID.'}', $insertValue, $tokenizedContent); 01960 } 01961 01962 return $tokenizedContent; 01963 } 01964 01965 /** 01966 * Process a soft reference file 01967 * 01968 * @param string Old Relative filename 01969 * @param array soft reference configuration array 01970 * @param string Table for which the processing occurs 01971 * @param string UID of record from table 01972 * @return string New relative filename (value to insert instead of the softref token) 01973 */ 01974 function processSoftReferences_saveFile($relFileName, $cfg, $table, $uid) { 01975 01976 if ($fileHeaderInfo = $this->dat['header']['files'][$cfg['file_ID']]) { 01977 // Initialize; Get directory prefix for file and find possible RTE filename 01978 $dirPrefix = dirname($relFileName).'/'; 01979 $rteOrigName = $this->getRTEoriginalFilename(basename($relFileName)); 01980 01981 // If filename looks like an RTE file, and the directory is in "uploads/", then process as a RTE file! 01982 if ($rteOrigName && t3lib_div::isFirstPartOfStr($dirPrefix,'uploads/')) { // RTE: 01983 01984 // First, find unique RTE file name: 01985 if (@is_dir(PATH_site.$dirPrefix)) { 01986 01987 // 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... 01988 $fileProcObj = $this->getFileProcObj(); 01989 $origDestName = $fileProcObj->getUniqueName($rteOrigName, PATH_site.$dirPrefix); 01990 01991 // Create copy file name: 01992 $pI = pathinfo($relFileName); 01993 $copyDestName = dirname($origDestName).'/RTEmagicC_'.substr(basename($origDestName),10).'.'.$pI['extension']; 01994 if (!@is_file($copyDestName) && !@is_file($origDestName) 01995 && $origDestName===t3lib_div::getFileAbsFileName($origDestName) && $copyDestName===t3lib_div::getFileAbsFileName($copyDestName)) { 01996 01997 if ($this->dat['header']['files'][$fileHeaderInfo['RTE_ORIG_ID']]) { 01998 01999 // Write the copy and original RTE file to the respective filenames: 02000 $this->writeFileVerify($copyDestName, $cfg['file_ID'], TRUE); 02001 $this->writeFileVerify($origDestName, $fileHeaderInfo['RTE_ORIG_ID'], TRUE); 02002 02003 // Return the relative path of the copy file name: 02004 return substr($copyDestName, strlen(PATH_site)); 02005 } else $this->error('ERROR: Could not find original file ID'); 02006 } else $this->error('ERROR: The destination filenames "'.$copyDestName.'" and "'.$origDestName.'" either existed or have non-valid names'); 02007 } else $this->error('ERROR: "'.PATH_site.$dirPrefix.'" was not a directory, so could not process file "'.$relFileName.'"'); 02008 02009 02010 } elseif (t3lib_div::isFirstPartOfStr($dirPrefix, $this->fileadminFolderName.'/')) { // File in fileadmin/ folder: 02011 02012 // Create file (and possible resources) 02013 $newFileName = $this->processSoftReferences_saveFile_createRelFile($dirPrefix,basename($relFileName),$cfg['file_ID'], $table, $uid); 02014 02015 if (strlen($newFileName)) { 02016 $relFileName = $newFileName; 02017 } else $this->error('ERROR: No new file created for "'.$relFileName.'"'); 02018 } else $this->error('ERROR: Sorry, cannot operate on non-RTE files which are outside the fileadmin folder.'); 02019 } else $this->error('ERROR: Could not find file ID in header.'); 02020 02021 // Return (new) filename relative to PATH_site: 02022 return $relFileName; 02023 } 02024 02025 /** 02026 * Create file in directory and return the new (unique) filename 02027 * 02028 * @param string Directory prefix, relative, with trailing slash 02029 * @param string Filename (without path) 02030 * @param string File ID from import memory 02031 * @param string Table for which the processing occurs 02032 * @param string UID of record from table 02033 * @return string New relative filename, if any 02034 */ 02035 function processSoftReferences_saveFile_createRelFile($origDirPrefix, $fileName, $fileID, $table, $uid) { 02036 02037 // 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! 02038 if ($this->fileIDMap[$fileID]) { 02039 return substr($this->fileIDMap[$fileID],strlen(PATH_site)); 02040 } 02041 02042 // Verify FileMount access to dir-prefix. Returns the best alternative relative path if any 02043 $dirPrefix = $this->verifyFolderAccess($origDirPrefix); 02044 02045 if ($dirPrefix && (!$this->update || $origDirPrefix===$dirPrefix) && $this->checkOrCreateDir($dirPrefix)) { 02046 $fileHeaderInfo = $this->dat['header']['files'][$fileID]; 02047 $updMode = $this->update && $this->import_mapId[$table][$uid]===$uid && $this->import_mode[$table.':'.$uid]!=='as_new'; 02048 // Create new name for file: 02049 if ($updMode) { // Must have same ID in map array (just for security, is not really needed) and NOT be set "as_new". 02050 $newName = PATH_site.$dirPrefix.$fileName; 02051 } else { 02052 // Create unique filename: 02053 $fileProcObj = $this->getFileProcObj(); 02054 $newName = $fileProcObj->getUniqueName($fileName, PATH_site.$dirPrefix); 02055 } 02056 #debug($newName,'$newName'); 02057 02058 // Write main file: 02059 if ($this->writeFileVerify($newName, $fileID)) { 02060 02061 // If the resource was an HTML/CSS file with resources attached, we will write those as well! 02062 if (is_array($fileHeaderInfo['EXT_RES_ID'])) { 02063 #debug($fileHeaderInfo['EXT_RES_ID']); 02064 $tokenizedContent = $this->dat['files'][$fileID]['tokenizedContent']; 02065 $tokenSubstituted = FALSE; 02066 02067 $fileProcObj = $this->getFileProcObj(); 02068 02069 if ($updMode) { 02070 foreach($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) { 02071 if ($this->dat['files'][$res_fileID]['filename']) { 02072 02073 // Resolve original filename: 02074 $relResourceFileName = $this->dat['files'][$res_fileID]['parentRelFileName']; 02075 $absResourceFileName = t3lib_div::resolveBackPath(PATH_site.$origDirPrefix.$relResourceFileName); 02076 $absResourceFileName = t3lib_div::getFileAbsFileName($absResourceFileName); 02077 if ($absResourceFileName && t3lib_div::isFirstPartOfStr($absResourceFileName,PATH_site.$this->fileadminFolderName.'/')) { 02078 $destDir = substr(dirname($absResourceFileName).'/',strlen(PATH_site)); 02079 if ($this->verifyFolderAccess($destDir, TRUE) && $this->checkOrCreateDir($destDir)) { 02080 $this->writeFileVerify($absResourceFileName, $res_fileID); 02081 } else $this->error('ERROR: Could not create file in directory "'.$destDir.'"'); 02082 } else $this->error('ERROR: Could not resolve path for "'.$relResourceFileName.'"'); 02083 02084 $tokenizedContent = str_replace('{EXT_RES_ID:'.$res_fileID.'}', $relResourceFileName, $tokenizedContent); 02085 $tokenSubstituted = TRUE; 02086 } 02087 } 02088 } else { 02089 // Create the resouces directory name (filename without extension, suffixed "_FILES") 02090 $resourceDir = dirname($newName).'/'.preg_replace('/\.[^.]*$/','',basename($newName)).'_FILES'; 02091 if (t3lib_div::mkdir($resourceDir)) { 02092 foreach($fileHeaderInfo['EXT_RES_ID'] as $res_fileID) { 02093 if ($this->dat['files'][$res_fileID]['filename']) { 02094 $absResourceFileName = $fileProcObj->getUniqueName($this->dat['files'][$res_fileID]['filename'], $resourceDir); 02095 $relResourceFileName = substr($absResourceFileName, strlen(dirname($resourceDir))+1); 02096 $this->writeFileVerify($absResourceFileName, $res_fileID); 02097 02098 $tokenizedContent = str_replace('{EXT_RES_ID:'.$res_fileID.'}', $relResourceFileName, $tokenizedContent); 02099 $tokenSubstituted = TRUE; 02100 } 02101 } 02102 } 02103 } 02104 02105 // If substitutions has been made, write the content to the file again: 02106 if ($tokenSubstituted) { 02107 t3lib_div::writeFile($newName, $tokenizedContent); 02108 } 02109 } 02110 02111 return substr($newName, strlen(PATH_site)); 02112 } 02113 } 02114 } 02115 02116 /** 02117 * Writes a file from the import memory having $fileID to file name $fileName which must be an absolute path inside PATH_site 02118 * 02119 * @param string Absolute filename inside PATH_site to write to 02120 * @param string File ID from import memory 02121 * @param boolean Bypasses the checking against filemounts - only for RTE files! 02122 * @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. 02123 */ 02124 function writeFileVerify($fileName, $fileID, $bypassMountCheck=FALSE) { 02125 $fileProcObj = $this->getFileProcObj(); 02126 02127 if ($fileProcObj->actionPerms['newFile']) { 02128 if ($fileProcObj->checkPathAgainstMounts($fileName) || $bypassMountCheck) { // Just for security, check again. Should actually not be necessary. 02129 $fI = t3lib_div::split_fileref($fileName); 02130 if ($fileProcObj->checkIfAllowed($fI['fileext'], $fI['path'], $fI['file']) || ($this->allowPHPScripts && $GLOBALS['BE_USER']->isAdmin())) { 02131 if (t3lib_div::getFileAbsFileName($fileName)) { 02132 if ($this->dat['files'][$fileID]) { 02133 t3lib_div::writeFile($fileName,$this->dat['files'][$fileID]['content']); 02134 $this->fileIDMap[$fileID] = $fileName; 02135 if (md5(t3lib_div::getUrl($fileName))==$this->dat['files'][$fileID]['content_md5']) { 02136 return TRUE; 02137 } else $this->error('ERROR: File content "'.$fileName.'" was corrupted'); 02138 } else $this->error('ERROR: File ID "'.$fileID.'" could not be found'); 02139 } else $this->error('ERROR: Filename "'.$fileName.'" was not a valid relative file path!'); 02140 } else $this->error('ERROR: Filename "'.$fileName.'" failed against extension check or deny-pattern!'); 02141 } else $this->error('ERROR: Filename "'.$fileName.'" was not allowed in destination path!'); 02142 } else $this->error('ERROR: You did not have sufficient permissions to write the file "'.$fileName.'"'); 02143 } 02144 02145 /** 02146 * Returns true if directory exists and if it doesn't it will create directory and return true if that succeeded. 02147 * 02148 * @param string Directory to create. Having a trailing slash. Must be in fileadmin/. Relative to PATH_site 02149 * @return boolean True, if directory exists (was created) 02150 */ 02151 function checkOrCreateDir($dirPrefix) { 02152 02153 // Split dir path and remove first directory (which should be "fileadmin") 02154 $filePathParts = explode('/', $dirPrefix); 02155 $firstDir = array_shift($filePathParts); 02156 02157 if ($firstDir===$this->fileadminFolderName && t3lib_div::getFileAbsFileName($dirPrefix)) { 02158 02159 $pathAcc = ''; 02160 foreach($filePathParts as $dirname) { 02161 $pathAcc.='/'.$dirname; 02162 if (strlen($dirname)) { 02163 if (!@is_dir(PATH_site.$this->fileadminFolderName.$pathAcc)) { 02164 if (!t3lib_div::mkdir(PATH_site.$this->fileadminFolderName.$pathAcc)) { 02165 $this->error('ERROR: Directory could not be created....B'); 02166 return FALSE; 02167 } 02168 } 02169 } elseif ($dirPrefix===$this->fileadminFolderName.$pathAcc) { 02170 return TRUE; 02171 } else $this->error('ERROR: Directory could not be created....A'); 02172 } 02173 } 02174 } 02175 02176 /** 02177 * Verifies that the input path (relative to PATH_site) is found in the backend users filemounts. 02178 * If it doesn't it will try to find another relative filemount for the user and return an alternative path prefix for the file. 02179 * 02180 * @param string Path relative to PATH_site 02181 * @param boolean If set, Do not look for alternative path! Just return false 02182 * @return string If a path is available that will be returned, otherwise false. 02183 */ 02184 function verifyFolderAccess($dirPrefix, $noAlternative=FALSE) { 02185 $fileProcObj = $this->getFileProcObj(); 02186 02187 #$fileProcObj->mounts['1f390e42e1dc46f125310ead30c7bd9d']['path'] = '/var/www/typo3/dev/testsite-3.6.0/fileadmin/user_upload/'; 02188 02189 // Check, if dirPrefix is inside a valid Filemount for user: 02190 $result = $fileProcObj->checkPathAgainstMounts(PATH_site.$dirPrefix); 02191 02192 // If not, try to find another relative filemount and use that instead: 02193 if (!$result) { 02194 if ($noAlternative) return FALSE; 02195 02196 // Find first web folder: 02197 $result = $fileProcObj->findFirstWebFolder(); 02198 02199 // If that succeeded, return the path to it: 02200 if ($result) { 02201 // Remove the "fileadmin/" prefix of input path - and append the rest to the return value: 02202 if (t3lib_div::isFirstPartOfStr($dirPrefix,$this->fileadminFolderName.'/')) { 02203 $dirPrefix = substr($dirPrefix,strlen($this->fileadminFolderName.'/')); 02204 } 02205 return substr($fileProcObj->mounts[$result]['path'].$dirPrefix,strlen(PATH_site)); 02206 } 02207 } else { 02208 return $dirPrefix; 02209 } 02210 } 02211 02212 02213 02214 02215 02216 02217 02218 02219 02220 02221 /************************** 02222 * 02223 * File Input 02224 * 02225 *************************/ 02226 02227 /** 02228 * Loads the header section/all of the $filename into memory 02229 * 02230 * @param string Filename, absolute 02231 * @param boolean If set, all information is loaded (header, records and files). Otherwise the default is to read only the header information 02232 * @return boolean True if the operation went well 02233 */ 02234 function loadFile($filename,$all=0) { 02235 if (@is_file($filename)) { 02236 $fI = pathinfo($filename); 02237 if (strtolower($fI['extension'])=='xml') { 02238 // XML: 02239 $xmlContent = t3lib_div::getUrl($filename); 02240 if (strlen($xmlContent)) { 02241 $this->dat = t3lib_div::xml2array($xmlContent,'',TRUE); 02242 if (is_array($this->dat)) { 02243 if ($this->dat['_DOCUMENT_TAG']==='T3RecordDocument' && is_array($this->dat['header']) && is_array($this->dat['records'])) { 02244 $this->loadInit(); 02245 return TRUE; 02246 } else $this->error('XML file did not contain proper XML for TYPO3 Import'); 02247 } else $this->error('XML could not be parsed: '.$this->dat); 02248 } else $this->error('Error opening file: '.$filename); 02249 } else { 02250 // T3D 02251 if($fd = fopen($filename,'rb')) { 02252 $this->dat['header'] = $this->getNextFilePart($fd,1,'header'); 02253 if ($all) { 02254 $this->dat['records'] = $this->getNextFilePart($fd,1,'records'); 02255 $this->dat['files'] = $this->getNextFilePart($fd,1,'files'); 02256 } 02257 $this->loadInit(); 02258 return TRUE; 02259 } else $this->error('Error opening file: '.$filename); 02260 fclose($fd); 02261 } 02262 } else $this->error('Filename not found: '.$filename); 02263 02264 return FALSE; 02265 } 02266 02267 /** 02268 * Returns the next content part form the fileresource (t3d), $fd 02269 * 02270 * @param pointer File pointer 02271 * @param boolean If set, the returned content is unserialized into an array, otherwise you get the raw string 02272 * @param string For error messages this indicates the section of the problem. 02273 * @return string Data string 02274 * @access private 02275 * @see loadFile() 02276 */ 02277 function getNextFilePart($fd,$unserialize=0,$name='') { 02278 $initStrLen = 32+1+1+1+10+1; 02279 02280 // getting header data 02281 $initStr = fread($fd, $initStrLen); 02282 $initStrDat = explode(':',$initStr); 02283 if (strstr($initStrDat[0],'Warning') == FALSE) { 02284 if (!strcmp($initStrDat[3],'')) { 02285 $datString = fread($fd,intval($initStrDat[2])); 02286 fread($fd,1); 02287 if (!strcmp(md5($datString), $initStrDat[0])) { 02288 if ($initStrDat[1]) { 02289 if ($this->compress) { 02290 $datString = gzuncompress($datString); 02291 } else $this->error('Content read error: This file requires decompression, but this server does not offer gzcompress()/gzuncompress() functions.',1); 02292 } 02293 return $unserialize ? unserialize($datString) : $datString; 02294 } else $this->error('MD5 check failed ('.$name.')'); 02295 } else $this->error('File read error: InitString had a wrong length. ('.$name.')'); 02296 } else $this->error('File read error: Warning message in file. ('.$initStr.fgets($fd).')'); 02297 } 02298 02299 /** 02300 * Loads T3D file content into the $this->dat array 02301 * (This function can be used to test the output strings from ->compileMemoryToFileContent()) 02302 * 02303 * @param string File content 02304 * @return void 02305 */ 02306 function loadContent($filecontent) { 02307 $pointer = 0; 02308 02309 $this->dat['header'] = $this->getNextContentPart($filecontent,$pointer,1,'header'); 02310 $this->dat['records'] = $this->getNextContentPart($filecontent,$pointer,1,'records'); 02311 $this->dat['files'] = $this->getNextContentPart($filecontent,$pointer,1,'files'); 02312 $this->loadInit(); 02313 } 02314 02315 /** 02316 * Returns the next content part from the $filecontent 02317 * 02318 * @param string File content string 02319 * @param integer File pointer (where to read from) 02320 * @param boolean If set, the returned content is unserialized into an array, otherwise you get the raw string 02321 * @param string For error messages this indicates the section of the problem. 02322 * @return string Data string 02323 */ 02324 function getNextContentPart($filecontent,&$pointer,$unserialize=0,$name='') { 02325 $initStrLen = 32+1+1+1+10+1; 02326 // getting header data 02327 $initStr = substr($filecontent,$pointer,$initStrLen); 02328 $pointer+= $initStrLen; 02329 $initStrDat = explode(':',$initStr); 02330 if (!strcmp($initStrDat[3],'')) { 02331 $datString = substr($filecontent,$pointer,intval($initStrDat[2])); 02332 $pointer+= intval($initStrDat[2])+1; 02333 if (!strcmp(md5($datString),$initStrDat[0])) { 02334 if ($initStrDat[1]) { 02335 if ($this->compress) { 02336 $datString = gzuncompress($datString); 02337 } else $this->error('Content read error: This file requires decompression, but this server does not offer gzcompress()/gzuncompress() functions.',1); 02338 } 02339 return $unserialize ? unserialize($datString) : $datString; 02340 } else $this->error('MD5 check failed ('.$name.')'); 02341 } else $this->error('Content read error: InitString had a wrong length. ('.$name.')'); 02342 } 02343 02344 /** 02345 * Setting up the object based on the recently loaded ->dat array 02346 * 02347 * @return void 02348 */ 02349 function loadInit() { 02350 $this->relStaticTables = (array)$this->dat['header']['relStaticTables']; 02351 $this->excludeMap = (array)$this->dat['header']['excludeMap']; 02352 02353 $this->softrefCfg = (array)$this->dat['header']['softrefCfg']; 02354 $this->extensionDependencies = (array)$this->dat['header']['extensionDependencies']; 02355 02356 $this->fixCharsets(); 02357 } 02358 02359 /** 02360 * Fix charset of import memory if different from system charset 02361 * 02362 * @return void 02363 * @see loadInit() 02364 */ 02365 function fixCharsets() { 02366 global $LANG; 02367 02368 $importCharset = $this->dat['header']['charset']; 02369 if ($importCharset) { 02370 if ($importCharset!==$LANG->charSet) { 02371 $this->error('CHARSET: Converting charset of input file ('.$importCharset.') to the system charset ('.$LANG->charSet.')'); 02372 02373 // convert meta data: 02374 if (is_array($this->dat['header']['meta'])) { 02375 $LANG->csConvObj->convArray($this->dat['header']['meta'],$importCharset,$LANG->charSet); 02376 } 02377 // convert record headers: 02378 if (is_array($this->dat['header']['records'])) { 02379 $LANG->csConvObj->convArray($this->dat['header']['records'],$importCharset,$LANG->charSet); 02380 } 02381 // convert records themselves: 02382 if (is_array($this->dat['records'])) { 02383 $LANG->csConvObj->convArray($this->dat['records'],$importCharset,$LANG->charSet); 02384 } 02385 } 02386 } else { 02387 $this->error('CHARSET: No charset found in import file!'); 02388 } 02389 } 02390 02391 02392 02393 02394 02395 02396 02397 02398 02399 02400 02401 02402 02403 02404 02405 02406 02407 02408 02409 /******************************************************** 02410 * 02411 * Visual rendering of import/export memory, $this->dat 02412 * 02413 ********************************************************/ 02414 02415 /** 02416 * Displays an overview of the header-content. 02417 * 02418 * @return string HTML content 02419 */ 02420 function displayContentOverview() { 02421 global $LANG; 02422 02423 // Check extension dependencies: 02424 if (is_array($this->dat['header']['extensionDependencies'])) { 02425 foreach($this->dat['header']['extensionDependencies'] as $extKey) { 02426 if (!t3lib_extMgm::isLoaded($extKey)) { 02427 $this->error('DEPENDENCY: The extension with key "'.$extKey.'" must be installed!'); 02428 } 02429 } 02430 } 02431 02432 // Probably this is done to save memory space? 02433 unset($this->dat['files']); 02434 02435 // Traverse header: 02436 $this->remainHeader = $this->dat['header']; 02437 if (is_array($this->remainHeader)) { 02438 02439 // If there is a page tree set, show that: 02440 if (is_array($this->dat['header']['pagetree'])) { 02441 reset($this->dat['header']['pagetree']); 02442 $lines = array(); 02443 $this->traversePageTree($this->dat['header']['pagetree'],$lines); 02444 02445 $rows = array(); 02446 $rows[] = ' 02447 <tr class="bgColor5 tableheader"> 02448 <td>'.$LANG->getLL('impexpcore_displaycon_controls',1).'</td> 02449 <td>'.$LANG->getLL('impexpcore_displaycon_title',1).'</td> 02450 <td>'.$LANG->getLL('impexpcore_displaycon_size',1).'</td> 02451 <td>'.$LANG->getLL('impexpcore_displaycon_message',1).'</td> 02452 '.($this->update ? '<td>'.$LANG->getLL('impexpcore_displaycon_updateMode',1).'</td>' : '').' 02453 '.($this->update ? '<td>'.$LANG->getLL('impexpcore_displaycon_currentPath',1).'</td>' : '').' 02454 '.($this->showDiff ? '<td>'.$LANG->getLL('impexpcore_displaycon_result',1).'</td>' : '').' 02455 </tr>'; 02456 02457 foreach($lines as $r) { 02458 $rows[] = ' 02459 <tr class="'.$r['class'].'"> 02460 <td>'.$this->renderControls($r).'</td> 02461 <td nowrap="nowrap">'.$r['preCode'].$r['title'].'</td> 02462 <td nowrap="nowrap">'.t3lib_div::formatSize($r['size']).'</td> 02463 <td nowrap="nowrap">'.($r['msg'] && !$this->doesImport ? '<span class="typo3-red">'.htmlspecialchars($r['msg']).'</span>' : '').'</td> 02464 '.($this->update ? '<td nowrap="nowrap">'.$r['updateMode'].'</td>' : '').' 02465 '.($this->update ? '<td nowrap="nowrap">'.$r['updatePath'].'</td>' : '').' 02466 '.($this->showDiff ? '<td>'.$r['showDiffContent'].'</td>' : '').' 02467 </tr>'; 02468 } 02469 02470 $out = ' 02471 <strong>'.$LANG->getLL('impexpcore_displaycon_insidePagetree',1).'</strong> 02472 <br /><br /> 02473 <table border="0" cellpadding="0" cellspacing="1">'.implode('',$rows).'</table> 02474 <br /><br />'; 02475 } 02476 02477 // Print remaining records that were not contained inside the page tree: 02478 $lines = array(); 02479 if (is_array($this->remainHeader['records'])) { 02480 if (is_array($this->remainHeader['records']['pages'])) { 02481 $this->traversePageRecords($this->remainHeader['records']['pages'], $lines); 02482 } 02483 $this->traverseAllRecords($this->remainHeader['records'], $lines); 02484 02485 if (count($lines)) { 02486 $rows = array(); 02487 $rows[] = ' 02488 <tr class="bgColor5 tableheader"> 02489 <td>'.$LANG->getLL('impexpcore_displaycon_controls',1).'</td> 02490 <td>'.$LANG->getLL('impexpcore_displaycon_title',1).'</td> 02491 <td>'.$LANG->getLL('impexpcore_displaycon_size',1).'</td> 02492 <td>'.$LANG->getLL('impexpcore_displaycon_message',1).'</td> 02493 '.($this->update ? '<td>'.$LANG->getLL('impexpcore_displaycon_updateMode',1).'</td>' : '').' 02494 '.($this->update ? '<td>'.$LANG->getLL('impexpcore_displaycon_currentPath',1).'</td>' : '').' 02495 '.($this->showDiff ? '<td>'.$LANG->getLL('impexpcore_displaycon_result',1).'</td>' : '').' 02496 </tr>'; 02497 02498 foreach($lines as $r) { 02499 $rows[] = '<tr class="'.$r['class'].'"> 02500 <td>'.$this->renderControls($r).'</td> 02501 <td nowrap="nowrap">'.$r['preCode'].$r['title'].'</td> 02502 <td nowrap="nowrap">'.t3lib_div::formatSize($r['size']).'</td> 02503 <td nowrap="nowrap">'.($r['msg'] && !$this->doesImport ? '<span class="typo3-red">'.htmlspecialchars($r['msg']).'</span>' : '').'</td> 02504 '.($this->update ? '<td nowrap="nowrap">'.$r['updateMode'].'</td>' : '').' 02505 '.($this->update ? '<td nowrap="nowrap">'.$r['updatePath'].'</td>' : '').' 02506 '.($this->showDiff ? '<td>'.$r['showDiffContent'].'</td>' : '').' 02507 </tr>'; 02508 } 02509 02510 $out.= ' 02511 <strong>'.$LANG->getLL('impexpcore_singlereco_outsidePagetree',1).'</strong> 02512 <br /><br /> 02513 <table border="0" cellpadding="0" cellspacing="1">'.implode('',$rows).'</table>'; 02514 } 02515 } 02516 } 02517 return $out; 02518 } 02519 02520 /** 02521 * Go through page tree for display 02522 * 02523 * @param array Page tree array with uid/subrow (from ->dat[header][pagetree] 02524 * @param array Output lines array (is passed by reference and modified) 02525 * @param string Pre-HTML code 02526 * @return void 02527 */ 02528 function traversePageTree($pT,&$lines,$preCode='') { 02529 foreach ($pT as $k => $v) { 02530 02531 // Add this page: 02532 $this->singleRecordLines('pages',$k,$lines,$preCode); 02533 02534 // Subrecords: 02535 if (is_array($this->dat['header']['pid_lookup'][$k])) { 02536 foreach ($this->dat['header']['pid_lookup'][$k] as $t => $recUidArr) { 02537 if ($t!='pages') { 02538 foreach ($recUidArr as $ruid => $value) { 02539 $this->singleRecordLines($t,$ruid,$lines,$preCode.' '); 02540 } 02541 } 02542 } 02543 unset($this->remainHeader['pid_lookup'][$k]); 02544 } 02545 02546 // Subpages, called recursively: 02547 if (is_array($v['subrow'])) { 02548 $this->traversePageTree($v['subrow'],$lines,$preCode.' '); 02549 } 02550 } 02551 } 02552 02553 /** 02554 * Go through remaining pages (not in tree) 02555 * 02556 * @param array Page tree array with uid/subrow (from ->dat[header][pagetree] 02557 * @param array Output lines array (is passed by reference and modified) 02558 * @return void 02559 */ 02560 function traversePageRecords($pT,&$lines) { 02561 foreach ($pT as $k => $rHeader) { 02562 $this->singleRecordLines('pages',$k,$lines,'',1); 02563 // Subrecords: 02564 if (is_array($this->dat['header']['pid_lookup'][$k])) { 02565 foreach ($this->dat['header']['pid_lookup'][$k] as $t => $recUidArr) { 02566 if ($t!='pages') { 02567 foreach ($recUidArr as $ruid => $value) { 02568 $this->singleRecordLines($t,$ruid,$lines,' '); 02569 } 02570 } 02571 } 02572 unset($this->remainHeader['pid_lookup'][$k]); 02573 } 02574 } 02575 } 02576 02577 /** 02578 * Go through ALL records (if the pages are displayed first, those will not be amoung these!) 02579 * 02580 * @param array Page tree array with uid/subrow (from ->dat[header][pagetree] 02581 * @param array Output lines array (is passed by reference and modified) 02582 * @return void 02583 */ 02584 function traverseAllRecords($pT,&$lines) { 02585 foreach ($pT as $t => $recUidArr) { 02586 if ($t!='pages') { 02587 foreach ($recUidArr as $ruid => $value) { 02588 $this->singleRecordLines($t,$ruid,$lines,$preCode,1); 02589 } 02590 } 02591 } 02592 } 02593 02594 /** 02595 * Add entries for a single record 02596 * 02597 * @param string Table name 02598 * @param integer Record uid 02599 * @param array Output lines array (is passed by reference and modified) 02600 * @param string Pre-HTML code 02601 * @param boolean If you want import validation, you can set this so it checks if the import can take place on the specified page. 02602 * @return void 02603 */ 02604 function singleRecordLines($table,$uid,&$lines,$preCode,$checkImportInPidRecord=0) { 02605 global $TCA,$BE_USER,$LANG; 02606 02607 // Get record: 02608 $record = $this->dat['header']['records'][$table][$uid]; 02609 unset($this->remainHeader['records'][$table][$uid]); 02610 if (!is_array($record) && !($table==='pages' && !$uid)) $this->error('MISSING RECORD: '.$table.':'.$uid,1); 02611 02612 // Begin to create the line arrays information record, pInfo: 02613 $pInfo = array(); 02614 $pInfo['ref'] = $table.':'.$uid; 02615 if ($table==='_SOFTREF_') { // Unknown table name: 02616 $pInfo['preCode'] = $preCode; 02617 $pInfo['title'] = '<em>'.$LANG->getLL('impexpcore_singlereco_softReferencesFiles',1).'</em>'; 02618 } elseif (!isset($TCA[$table])) { // Unknown table name: 02619 $pInfo['preCode'] = $preCode; 02620 $pInfo['msg'] = "UNKNOWN TABLE '".$pInfo['ref']."'"; 02621 $pInfo['title'] = '<em>'.htmlspecialchars($record['title']).'</em>'; 02622 } else { // Otherwise, set table icon and title. 02623 02624 // Import Validation (triggered by $this->display_import_pid_record) will show messages if import is not possible of various items. 02625 if (is_array($this->display_import_pid_record)) { 02626 if ($checkImportInPidRecord) { 02627 if (!$BE_USER->doesUserHaveAccess($this->display_import_pid_record, $table=='pages'?8:16)) { 02628 $pInfo['msg'].="'".$pInfo['ref']."' cannot be INSERTED on this page! "; 02629 } 02630 if (!$this->checkDokType($table, $this->display_import_pid_record['doktype']) && !$TCA[$table]['ctrl']['rootLevel']) { 02631 $pInfo['msg'].="'".$table."' cannot be INSERTED on this page type (change page type to 'Folder'.) "; 02632 } 02633 } 02634 if (!$BE_USER->check('tables_modify',$table)) {$pInfo['msg'].="You are not allowed to CREATE '".$table."' tables! ";} 02635 02636 if ($TCA[$table]['ctrl']['readOnly']) {$pInfo['msg'].="TABLE '".$table."' is READ ONLY! ";} 02637 if ($TCA[$table]['ctrl']['adminOnly'] && !$BE_USER->isAdmin()) {$pInfo['msg'].="TABLE '".$table."' is ADMIN ONLY! ";} 02638 if ($TCA[$table]['ctrl']['is_static']) {$pInfo['msg'].="TABLE '".$table."' is a STATIC TABLE! ";} 02639 if ($TCA[$table]['ctrl']['rootLevel']) {$pInfo['msg'].="TABLE '".$table."' will be inserted on ROOT LEVEL! ";} 02640 02641 $diffInverse = FALSE; 02642 if ($this->update) { 02643 $diffInverse = TRUE; // In case of update-PREVIEW we swap the diff-sources. 02644 $recInf = $this->doesRecordExist($table, $uid, $this->showDiff ? '*' : ''); 02645 $pInfo['updatePath']= $recInf ? htmlspecialchars($this->getRecordPath($recInf['pid'])) : '<strong>NEW!</strong>'; 02646 02647 // Mode selector: 02648 $optValues = array(); 02649 $optValues[] = $recInf ? $LANG->getLL('impexpcore_singlereco_update') : $LANG->getLL('impexpcore_singlereco_insert'); 02650 if ($recInf) $optValues['as_new'] = $LANG->getLL('impexpcore_singlereco_importAsNew'); 02651 if ($recInf) { 02652 if (!$this->global_ignore_pid) { 02653 $optValues['ignore_pid'] = $LANG->getLL('impexpcore_singlereco_ignorePid'); 02654 } else { 02655 $optValues['respect_pid'] = $LANG->getLL('impexpcore_singlereco_respectPid'); 02656 } 02657 } 02658 if (!$recInf && $GLOBALS['BE_USER']->isAdmin()) $optValues['force_uid'] = sprintf($LANG->getLL('impexpcore_singlereco_forceUidSAdmin'),$uid); 02659 $optValues['exclude'] = $LANG->getLL('impexpcore_singlereco_exclude'); 02660 02661 $pInfo['updateMode'] = $this->renderSelectBox('tx_impexp[import_mode]['.$table.':'.$uid.']',$this->import_mode[$table.':'.$uid],$optValues); 02662 } 02663 02664 // Diff vieiw: 02665 if ($this->showDiff) { 02666 // For IMPORTS, get new id: 02667 if ($newUid = $this->import_mapId[$table][$uid]) { 02668 $diffInverse = FALSE; 02669 $recInf = $this->doesRecordExist($table, $newUid, '*'); 02670 t3lib_BEfunc::workspaceOL($table,$recInf); 02671 } 02672 if (is_array($recInf)) { 02673 $pInfo['showDiffContent'] = $this->compareRecords($recInf, $this->dat['records'][$table.':'.$uid]['data'], $table, $diffInverse); 02674 } 02675 } 02676 } 02677 02678 $pInfo['preCode'] = $preCode.t3lib_iconworks::getSpriteIconForRecord($table, (array)$this->dat['records'][$table . ':' . $uid]['data'], array('title' => htmlspecialchars($table . ':' . $uid))); 02679 $pInfo['title'] = htmlspecialchars($record['title']); 02680 02681 // View page: 02682 if ($table==='pages') { 02683 $viewID = $this->mode === 'export' ? $uid : ($this->doesImport ? $this->import_mapId['pages'][$uid] : 0); 02684 if ($viewID) { 02685 $pInfo['title'] = '<a href="#" onclick="'.htmlspecialchars(t3lib_BEfunc::viewOnClick($viewID, $GLOBALS['BACK_PATH'])).'return false;">'.$pInfo['title'].'</a>'; 02686 } 02687 } 02688 } 02689 $pInfo['class'] = $table=='pages' ? 'bgColor4-20' : 'bgColor4'; 02690 $pInfo['type'] = 'record'; 02691 $pInfo['size'] = $record['size']; 02692 $lines[] = $pInfo; 02693 02694 // File relations: 02695 if (is_array($record['filerefs'])) { 02696 $this->addFiles($record['filerefs'],$lines,$preCode); 02697 } 02698 02699 // DB relations 02700 if (is_array($record['rels'])) { 02701 $this->addRelations($record['rels'],$lines,$preCode); 02702 } 02703 02704 // Soft ref 02705 if (count($record['softrefs'])) { 02706 $preCode_A = $preCode.' '; 02707 $preCode_B = $preCode.' '; 02708 foreach($record['softrefs'] as $info) { 02709 $pInfo = array(); 02710 $pInfo['preCode'] = $preCode_A. t3lib_iconWorks::getSpriteIcon('status-status-reference-soft'); 02711 $pInfo['title'] = '<em>'.$info['field'].', "'.$info['spKey'].'" </em>: <span title="'.htmlspecialchars($info['matchString']).'">'.htmlspecialchars(t3lib_div::fixed_lgd_cs($info['matchString'],60)).'</span>'; 02712 if ($info['subst']['type']) { 02713 if (strlen($info['subst']['title'])) { 02714 $pInfo['title'].= '<br/>'.$preCode_B.'<strong>'.$LANG->getLL('impexpcore_singlereco_title',1).'</strong> '.htmlspecialchars(t3lib_div::fixed_lgd_cs($info['subst']['title'],60)); 02715 } 02716 if (strlen($info['subst']['description'])) { 02717 $pInfo['title'].= '<br/>'.$preCode_B.'<strong>'.$LANG->getLL('impexpcore_singlereco_descr',1).'</strong> '.htmlspecialchars(t3lib_div::fixed_lgd_cs($info['subst']['description'],60)); 02718 } 02719 $pInfo['title'].= '<br/>'.$preCode_B. 02720 ($info['subst']['type'] == 'file' ? $LANG->getLL('impexpcore_singlereco_filename',1).' <strong>'.$info['subst']['relFileName'].'</strong>' : ''). 02721 ($info['subst']['type'] == 'string' ? $LANG->getLL('impexpcore_singlereco_value',1).' <strong>'.$info['subst']['tokenValue'].'</strong>' : ''). 02722 ($info['subst']['type'] == 'db' ? $LANG->getLL('impexpcore_softrefsel_record',1).' <strong>'.$info['subst']['recordRef'].'</strong>' : ''); 02723 } 02724 $pInfo['ref'] = 'SOFTREF'; 02725 $pInfo['size'] = ''; 02726 $pInfo['class'] = 'bgColor3'; 02727 $pInfo['type'] = 'softref'; 02728 $pInfo['_softRefInfo'] = $info; 02729 $pInfo['type'] = 'softref'; 02730 if ($info['error'] && !t3lib_div::inList('editable,exclude',$this->softrefCfg[$info['subst']['tokenID']]['mode'])) { 02731 $pInfo['msg'].= $info['error']; 02732 } 02733 $lines[] = $pInfo; 02734 02735 // Add relations: 02736 if ($info['subst']['type'] == 'db') { 02737 list($tempTable, $tempUid) = explode(':', $info['subst']['recordRef']); 02738 $this->addRelations(array(array('table' => $tempTable, 'id' => $tempUid, 'tokenID' => $info['subst']['tokenID'])),$lines,$preCode_B,array(), ''); 02739 } 02740 02741 // Add files: 02742 if ($info['subst']['type'] == 'file') { 02743 #debug($info); 02744 $this->addFiles(array($info['file_ID']),$lines,$preCode_B, '', $info['subst']['tokenID']); 02745 } 02746 } 02747 } 02748 } 02749 02750 /** 02751 * Add DB relations entries for a record's rels-array 02752 * 02753 * @param array Array of relations 02754 * @param array Output lines array (is passed by reference and modified) 02755 * @param string Pre-HTML code 02756 * @param array Recursivity check stack 02757 * @param string Alternative HTML color class to use. 02758 * @return void 02759 * @access private 02760 * @see singleRecordLines() 02761 */ 02762 function addRelations($rels,&$lines,$preCode,$recurCheck=array(),$htmlColorClass='') { 02763 02764 foreach($rels as $dat) { 02765 $table = $dat['table']; 02766 $uid = $dat['id']; 02767 $pInfo = array(); 02768 $Iprepend = ''; 02769 $staticFixed = FALSE; 02770 $pInfo['ref'] = $table.':'.$uid; 02771 if (!in_array($pInfo['ref'],$recurCheck)) { 02772 if ($uid > 0) { 02773 $record = $this->dat['header']['records'][$table][$uid]; 02774 if (!is_array($record)) { 02775 if ($this->isTableStatic($table) || $this->isExcluded($table, $uid) || ($dat['tokenID'] && !$this->includeSoftref($dat['tokenID']))) { 02776 $pInfo['title'] = htmlspecialchars('STATIC: '.$pInfo['ref']); 02777 $Iprepend = '_static'; 02778 $staticFixed = TRUE; 02779 } else { 02780 $doesRE = $this->doesRecordExist($table,$uid); 02781 $lostPath = $this->getRecordPath($table==='pages' ? $doesRE['uid'] : $doesRE['pid']); 02782 02783 $pInfo['title'] = htmlspecialchars($pInfo['ref']); 02784 $pInfo['title'] = '<span title="'.htmlspecialchars($lostPath).'">'.$pInfo['title'].'</span>'; 02785 02786 $pInfo['msg'] = 'LOST RELATION'.(!$doesRE ? ' (Record not found!)' : ' (Path: '.$lostPath.')'); 02787 $Iprepend = '_lost'; 02788 # debug('MISSING relation: '.$table.':'.$uid); 02789 } 02790 } else { 02791 $pInfo['title'] = htmlspecialchars($record['title']); 02792 $pInfo['title'] = '<span title="'.htmlspecialchars($this->getRecordPath($table==='pages' ? $record['uid'] : $record['pid'])).'">'.$pInfo['title'].'</span>'; 02793 02794 # $pInfo['size'] = $record['size']; 02795 } 02796 } 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 02797 $pInfo['title'] = htmlspecialchars('FIXED: '.$pInfo['ref']); 02798 $staticFixed = TRUE; 02799 } 02800 02801 $pInfo['preCode'] = $preCode.' <img'.t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'],'gfx/rel_db'.$Iprepend.'.gif','width="13" height="12"').' align="top" title="'.htmlspecialchars($pInfo['ref']).'" alt="" />'; 02802 $pInfo['class'] = $htmlColorClass ? $htmlColorClass : 'bgColor3'; 02803 $pInfo['type'] = 'rel'; 02804 02805 if (!$staticFixed || $this->showStaticRelations) { 02806 $lines[] = $pInfo; 02807 if (is_array($record) && is_array($record['rels'])) { 02808 $this->addRelations($record['rels'], $lines, $preCode.' ', array_merge($recurCheck,array($pInfo['ref'])), $htmlColorClass); 02809 } 02810 } 02811 } else $this->error($pInfo['ref'].' was recursive...'); 02812 } 02813 } 02814 02815 /** 02816 * Add file relation entries for a record's rels-array 02817 * 02818 * @param array Array of file IDs 02819 * @param array Output lines array (is passed by reference and modified) 02820 * @param string Pre-HTML code 02821 * @param string Alternative HTML color class to use. 02822 * @param string Token ID if this is a softreference (in which case it only makes sense with a single element in the $rels array!) 02823 * @return void 02824 * @access private 02825 * @see singleRecordLines() 02826 */ 02827 function addFiles($rels,&$lines,$preCode,$htmlColorClass='',$tokenID='') { 02828 02829 foreach($rels as $ID) { 02830 02831 // Process file: 02832 $pInfo = array(); 02833 $fI = $this->dat['header']['files'][$ID]; 02834 if (!is_array($fI)) { 02835 if (!$tokenID || $this->includeSoftref($tokenID)) { 02836 $pInfo['msg'] = 'MISSING FILE: '.$ID; 02837 $this->error('MISSING FILE: '.$ID,1); 02838 } else { 02839 return; 02840 } 02841 } 02842 $pInfo['preCode'] = $preCode.' '.t3lib_iconWorks::getSpriteIcon('status-status-reference-hard'); 02843 $pInfo['title'] = htmlspecialchars($fI['filename']); 02844 $pInfo['ref'] = 'FILE'; 02845 $pInfo['size'] = $fI['filesize']; 02846 $pInfo['class'] = $htmlColorClass ? $htmlColorClass : 'bgColor3'; 02847 $pInfo['type'] = 'file'; 02848 02849 // If import mode and there is a non-RTE softreference, check the destination directory: 02850 if ($this->mode==='import' && $tokenID && !$fI['RTE_ORIG_ID']) { 02851 if (isset($fI['parentRelFileName'])) { 02852 $pInfo['msg'] = 'Seems like this file is already referenced from within an HTML/CSS file. That takes precedence. '; 02853 } else { 02854 $testDirPrefix = dirname($fI['relFileName']).'/'; 02855 $testDirPrefix2 = $this->verifyFolderAccess($testDirPrefix); 02856 02857 if (!$testDirPrefix2) { 02858 $pInfo['msg'] = 'ERROR: There are no available filemounts to write file in! '; 02859 } elseif (strcmp($testDirPrefix,$testDirPrefix2)) { 02860 $pInfo['msg'] = 'File will be attempted written to "'.$testDirPrefix2.'". '; 02861 } 02862 } 02863 02864 02865 // Check if file exists: 02866 if (file_exists(PATH_site.$fI['relFileName'])) { 02867 if ($this->update) { 02868 $pInfo['updatePath'].= 'File exists.'; 02869 } else { 02870 $pInfo['msg'].= 'File already exists! '; 02871 } 02872 } 02873 02874 // Check extension: 02875 $fileProcObj = $this->getFileProcObj(); 02876 if ($fileProcObj->actionPerms['newFile']) { 02877 $testFI = t3lib_div::split_fileref(PATH_site.$fI['relFileName']); 02878 if (!$this->allowPHPScripts && !$fileProcObj->checkIfAllowed($testFI['fileext'], $testFI['path'], $testFI['file'])) { 02879 $pInfo['msg'].= 'File extension was not allowed!'; 02880 } 02881 } else $pInfo['msg'] = 'You user profile does not allow you to create files on the server!'; 02882 } 02883 02884 $pInfo['showDiffContent'] = substr($this->fileIDMap[$ID],strlen(PATH_site)); 02885 02886 $lines[] = $pInfo; 02887 unset($this->remainHeader['files'][$ID]); 02888 02889 // RTE originals: 02890 if ($fI['RTE_ORIG_ID']) { 02891 $ID = $fI['RTE_ORIG_ID']; 02892 $pInfo = array(); 02893 $fI = $this->dat['header']['files'][$ID]; 02894 if (!is_array($fI)) { 02895 $pInfo['msg'] = 'MISSING RTE original FILE: '.$ID; 02896 $this->error('MISSING RTE original FILE: '.$ID,1); 02897 } 02898 02899 $pInfo['showDiffContent'] = substr($this->fileIDMap[$ID],strlen(PATH_site)); 02900 02901 $pInfo['preCode'] = $preCode.' '.t3lib_iconWorks::getSpriteIcon('actions-reference-file'); 02902 $pInfo['title'] = htmlspecialchars($fI['filename']).' <em>(Original)</em>'; 02903 $pInfo['ref'] = 'FILE'; 02904 $pInfo['size'] = $fI['filesize']; 02905 $pInfo['class'] = $htmlColorClass ? $htmlColorClass : 'bgColor3'; 02906 $pInfo['type'] = 'file'; 02907 $lines[] = $pInfo; 02908 unset($this->remainHeader['files'][$ID]); 02909 } 02910 02911 // External resources: 02912 if (is_array($fI['EXT_RES_ID'])) { 02913 foreach($fI['EXT_RES_ID'] as $ID) { 02914 $pInfo = array(); 02915 $fI = $this->dat['header']['files'][$ID]; 02916 if (!is_array($fI)) { 02917 $pInfo['msg'] = 'MISSING External Resource FILE: '.$ID; 02918 $this->error('MISSING External Resource FILE: '.$ID,1); 02919 } else { 02920 $pInfo['updatePath'] = $fI['parentRelFileName']; 02921 } 02922 02923 $pInfo['showDiffContent'] = substr($this->fileIDMap[$ID],strlen(PATH_site)); 02924 02925 $pInfo['preCode'] = $preCode.' '.t3lib_iconWorks::getSpriteIcon('actions-insert-reference'); 02926 $pInfo['title'] = htmlspecialchars($fI['filename']).' <em>(Resource)</em>'; 02927 $pInfo['ref'] = 'FILE'; 02928 $pInfo['size'] = $fI['filesize']; 02929 $pInfo['class'] = $htmlColorClass ? $htmlColorClass : 'bgColor3'; 02930 $pInfo['type'] = 'file'; 02931 $lines[] = $pInfo; 02932 unset($this->remainHeader['files'][$ID]); 02933 } 02934 } 02935 } 02936 } 02937 02938 /** 02939 * Verifies that a table is allowed on a certain doktype of a page 02940 * 02941 * @param string Table name to check 02942 * @param integer doktype value. 02943 * @return boolean True if OK 02944 */ 02945 function checkDokType($checkTable,$doktype) { 02946 global $PAGES_TYPES; 02947 $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables']; 02948 $allowedArray = t3lib_div::trimExplode(',',$allowedTableList,1); 02949 if (strstr($allowedTableList,'*') || in_array($checkTable,$allowedArray)) { // If all tables or the table is listed as a allowed type, return true 02950 return true; 02951 } 02952 } 02953 02954 /** 02955 * Render input controls for import or export 02956 * 02957 * @param array Configuration for element 02958 * @param boolean Set if export situation 02959 * @return string HTML 02960 */ 02961 function renderControls($r) { 02962 global $LANG; 02963 02964 if ($this->mode==='export') { 02965 return ($r['type']=='record' ? '<input type="checkbox" name="tx_impexp[exclude]['.$r['ref'].']" id="checkExclude'.$r['ref'].'" value="1" /> <label for="checkExclude'.$r['ref'].'">'.$LANG->getLL('impexpcore_singlereco_exclude',1).'</label>' : 02966 ($r['type']=='softref' ? $this->softrefSelector($r['_softRefInfo']) : '')); 02967 } else { // During import 02968 02969 // For softreferences with editable fields: 02970 if ($r['type']=='softref' && is_array($r['_softRefInfo']['subst']) && $r['_softRefInfo']['subst']['tokenID']) { 02971 $tokenID = $r['_softRefInfo']['subst']['tokenID']; 02972 $cfg = $this->softrefCfg[$tokenID]; 02973 if ($cfg['mode'] === 'editable') { 02974 return 02975 (strlen($cfg['title']) ? '<strong>'.htmlspecialchars($cfg['title']).'</strong><br/>' : ''). 02976 htmlspecialchars($cfg['description']).'<br/> 02977 <input type="text" name="tx_impexp[softrefInputValues]['.$tokenID.']" value="'.htmlspecialchars(isset($this->softrefInputValues[$tokenID]) ? $this->softrefInputValues[$tokenID] : $cfg['defValue']).'" />'; 02978 } 02979 } 02980 } 02981 } 02982 02983 /** 02984 * Selectorbox with export options for soft references 02985 * 02986 * @param array softref configuration array. An export box is shown only if a substitution scheme is found for the soft reference. 02987 * @return string Selector box HTML 02988 */ 02989 function softrefSelector($cfg) { 02990 global $LANG; 02991 02992 // Looking for file ID if any: 02993 $fI = $cfg['file_ID'] ? $this->dat['header']['files'][$cfg['file_ID']] : array(); 02994 02995 // Substitution scheme has to be around and RTE images MUST be exported. 02996 if (is_array($cfg['subst']) && $cfg['subst']['tokenID'] && !$fI['RTE_ORIG_ID']) { 02997 02998 // Create options: 02999 $optValues = array(); 03000 $optValues[''] = ''; 03001 $optValues['editable'] = $LANG->getLL('impexpcore_softrefsel_editable'); 03002 $optValues['exclude'] = $LANG->getLL('impexpcore_softrefsel_exclude'); 03003 03004 // Get current value: 03005 $value = $this->softrefCfg[$cfg['subst']['tokenID']]['mode']; 03006 03007 // Render options selector: 03008 $selectorbox = $this->renderSelectBox('tx_impexp[softrefCfg]['.$cfg['subst']['tokenID'].'][mode]',$value,$optValues).'<br/>'; 03009 03010 if ($value === 'editable') { 03011 03012 $descriptionField = ''; 03013 03014 // Title: 03015 if (strlen($cfg['subst']['title'])) { 03016 $descriptionField.= ' 03017 <input type="hidden" name="tx_impexp[softrefCfg]['.$cfg['subst']['tokenID'].'][title]" value="'.htmlspecialchars($cfg['subst']['title']).'" /> 03018 <strong>'.htmlspecialchars($cfg['subst']['title']).'</strong><br/>'; 03019 } 03020 03021 // Description: 03022 if (!strlen($cfg['subst']['description'])) { 03023 $descriptionField.= ' 03024 '.$LANG->getLL('impexpcore_printerror_description',1).'<br/> 03025 <input type="text" name="tx_impexp[softrefCfg]['.$cfg['subst']['tokenID'].'][description]" value="'.htmlspecialchars($this->softrefCfg[$cfg['subst']['tokenID']]['description']).'" />'; 03026 } else { 03027 $descriptionField.= ' 03028 03029 <input type="hidden" name="tx_impexp[softrefCfg]['.$cfg['subst']['tokenID'].'][description]" value="'.htmlspecialchars($cfg['subst']['description']).'" />'. 03030 htmlspecialchars($cfg['subst']['description']); 03031 } 03032 03033 // Default Value: 03034 $descriptionField.= '<input type="hidden" name="tx_impexp[softrefCfg]['.$cfg['subst']['tokenID'].'][defValue]" value="'.htmlspecialchars($cfg['subst']['tokenValue']).'" />'; 03035 03036 } else $descriptionField = ''; 03037 03038 return $selectorbox.$descriptionField; 03039 } 03040 } 03041 03042 03043 03044 03045 03046 03047 03048 03049 03050 03051 03052 03053 /***************************** 03054 * 03055 * Helper functions of kinds 03056 * 03057 *****************************/ 03058 03059 /** 03060 * Returns true if the input table name is to be regarded as a static relation (that is, not exported etc). 03061 * 03062 * @param string Table name 03063 * @return boolean True, if table is marked static 03064 */ 03065 function isTableStatic($table) { 03066 global $TCA; 03067 03068 if (is_array($TCA[$table])) { 03069 return $TCA[$table]['ctrl']['is_static'] || in_array($table, $this->relStaticTables) || in_array('_ALL', $this->relStaticTables); 03070 } 03071 } 03072 03073 /** 03074 * Returns true if the input table name is to be included as relation 03075 * 03076 * @param string Table name 03077 * @return boolean True, if table is marked static 03078 */ 03079 function inclRelation($table) { 03080 global $TCA; 03081 03082 if (is_array($TCA[$table])) { 03083 return (in_array($table, $this->relOnlyTables) || in_array('_ALL', $this->relOnlyTables)) && $GLOBALS['BE_USER']->check('tables_select',$table); 03084 } 03085 } 03086 03087 /** 03088 * Returns true if the element should be excluded as static record. 03089 * 03090 * @param string Table name 03091 * @param integer UID value 03092 * @return boolean True, if table is marked static 03093 */ 03094 function isExcluded($table,$uid) { 03095 global $TCA; 03096 03097 return $this->excludeMap[$table.':'.$uid] ? TRUE : FALSE; 03098 } 03099 03100 /** 03101 * Returns true if soft reference should be included in exported file. 03102 * 03103 * @param string Token ID for soft reference 03104 * @return boolean True if softreference media should be included 03105 */ 03106 function includeSoftref($tokenID) { 03107 return $tokenID && !t3lib_div::inList('exclude,editable', $this->softrefCfg[$tokenID]['mode']); 03108 } 03109 03110 /** 03111 * Checking if a PID is in the webmounts of the user 03112 * 03113 * @param integer Page ID to check 03114 * @return boolean True if OK 03115 */ 03116 function checkPID($pid) { 03117 global $BE_USER; 03118 03119 if (!isset($this->checkPID_cache[$pid])) { 03120 $this->checkPID_cache[$pid] = (boolean)$BE_USER->isInWebMount($pid); 03121 } 03122 03123 return $this->checkPID_cache[$pid]; 03124 } 03125 03126 /** 03127 * Checks if the position of an updated record is configured to be corrected. This can be disabled globally and changed for elements individually. 03128 * 03129 * @param string Table name 03130 * @param integer Uid or record 03131 * @return boolean True if the position of the record should be updated to match the one in the import structure 03132 */ 03133 function dontIgnorePid($table, $uid) { 03134 return $this->import_mode[$table.':'.$uid]!=='ignore_pid' && 03135 (!$this->global_ignore_pid || $this->import_mode[$table.':'.$uid]==='respect_pid'); 03136 } 03137 03138 /** 03139 * Checks if the record exists 03140 * 03141 * @param string Table name 03142 * @param integer UID of record 03143 * @param string Field list to select. Default is "uid,pid" 03144 * @return array Result of t3lib_BEfunc::getRecord() which means the record if found, otherwise false 03145 */ 03146 function doesRecordExist($table,$uid,$fields='') { 03147 return t3lib_BEfunc::getRecord($table, $uid, $fields ? $fields : 'uid,pid'); 03148 } 03149 03150 /** 03151 * Returns the page title path of a PID value. Results are cached internally 03152 * 03153 * @param integer Record PID to check 03154 * @return string The path for the input PID 03155 */ 03156 function getRecordPath($pid) { 03157 if (!isset($this->cache_getRecordPath[$pid])) { 03158 $clause = $GLOBALS['BE_USER']->getPagePermsClause(1); 03159 $this->cache_getRecordPath[$pid] = (string)t3lib_BEfunc::getRecordPath($pid, $clause, 20); 03160 } 03161 03162 return $this->cache_getRecordPath[$pid]; 03163 } 03164 03165 /** 03166 * Makes a selector-box from optValues 03167 * 03168 * @param string Form element name 03169 * @param string Current value 03170 * @param array Options to display (key/value pairs) 03171 * @return string HTML select element 03172 */ 03173 function renderSelectBox($prefix,$value,$optValues) { 03174 $opt = array(); 03175 $isSelFlag = 0; 03176 foreach ($optValues as $k => $v) { 03177 $sel = (!strcmp($k,$value) ? ' selected="selected"' : ''); 03178 if ($sel) $isSelFlag++; 03179 $opt[] = '<option value="'.htmlspecialchars($k).'"'.$sel.'>'.htmlspecialchars($v).'</option>'; 03180 } 03181 if (!$isSelFlag && strcmp('',$value)) { 03182 $opt[] = '<option value="'.htmlspecialchars($value).'" selected="selected">'.htmlspecialchars("['".$value."']").'</option>'; 03183 } 03184 return '<select name="'.$prefix.'">'.implode('',$opt).'</select>'; 03185 } 03186 03187 /** 03188 * Compares two records, the current database record and the one from the import memory. Will return HTML code to show any differences between them! 03189 * 03190 * @param array Database record, all fields (new values) 03191 * @param array Import memorys record for the same table/uid, all fields (old values) 03192 * @param string The table name of the record 03193 * @param boolean Inverse the diff view (switch red/green, needed for pre-update difference view) 03194 * @return string HTML 03195 */ 03196 function compareRecords($databaseRecord, $importRecord, $table, $inverseDiff=FALSE) { 03197 global $TCA, $LANG; 03198 03199 // Initialize: 03200 $output = array(); 03201 $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff'); 03202 03203 // Check if both inputs are records: 03204 if (is_array($databaseRecord) && is_array($importRecord)) { 03205 03206 // Traverse based on database record 03207 foreach($databaseRecord as $fN => $value) { 03208 if (is_array($TCA[$table]['columns'][$fN]) && $TCA[$table]['columns'][$fN]['config']['type']!='passthrough') { 03209 if (isset($importRecord[$fN])) { 03210 if (strcmp(trim($databaseRecord[$fN]), trim($importRecord[$fN]))) { 03211 03212 // Create diff-result: 03213 $output[$fN] = $t3lib_diff_Obj->makeDiffDisplay( 03214 t3lib_BEfunc::getProcessedValue($table,$fN,!$inverseDiff ? $importRecord[$fN] : $databaseRecord[$fN] ,0,1,1), 03215 t3lib_BEfunc::getProcessedValue($table,$fN,!$inverseDiff ? $databaseRecord[$fN] : $importRecord[$fN] ,0,1,1) 03216 ); 03217 } 03218 unset($importRecord[$fN]); 03219 } else { 03220 // This will tell us if the field is not in the import file, but who cares? It is totally ok that the database contains fields that are not in the import, isn't it (extensions could be installed that added these fields!)? 03221 #$output[$fN] = '<strong>Field missing</strong> in import file'; 03222 } 03223 } 03224 } 03225 03226 // Traverse remaining in import record: 03227 foreach($importRecord as $fN => $value) { 03228 if (is_array($TCA[$table]['columns'][$fN]) && $TCA[$table]['columns'][$fN]['config']['type']!='passthrough') { 03229 $output[$fN] = '<strong>Field missing</strong> in database'; 03230 } 03231 } 03232 03233 // Create output: 03234 if (count($output)) { 03235 $tRows = array(); 03236 foreach($output as $fN => $state) { 03237 $tRows[] = ' 03238 <tr> 03239 <td class="bgColor5">'.$LANG->sL($TCA[$table]['columns'][$fN]['label'],1).' ('.htmlspecialchars($fN).')</td> 03240 <td class="bgColor4">'.$state.'</td> 03241 </tr> 03242 '; 03243 } 03244 03245 $output = '<table border="0" cellpadding="0" cellspacing="1">'.implode('',$tRows).'</table>'; 03246 } else { 03247 $output = 'Match'; 03248 } 03249 03250 return '<strong class="nobr">['.htmlspecialchars($table.':'.$importRecord['uid'].' => '.$databaseRecord['uid']).']:</strong> '.$output; 03251 } 03252 03253 03254 return 'ERROR: One of the inputs were not an array!'; 03255 } 03256 03257 /** 03258 * Creates the original file name for a copy-RTE image (magic type) 03259 * 03260 * @param string RTE copy filename, eg. "RTEmagicC_user_pm_icon_01.gif.gif" 03261 * @return string RTE original filename, eg. "RTEmagicP_user_pm_icon_01.gif". IF the input filename was NOT prefixed RTEmagicC_ as RTE images would be, nothing is returned! 03262 */ 03263 function getRTEoriginalFilename($string) { 03264 // If "magic image": 03265 if (t3lib_div::isFirstPartOfStr($string,'RTEmagicC_')) { 03266 // Find original file: 03267 $pI = pathinfo(substr($string,strlen('RTEmagicC_'))); 03268 $filename = substr($pI['basename'],0,-strlen('.'.$pI['extension'])); 03269 $origFilePath = 'RTEmagicP_'.$filename; 03270 03271 return $origFilePath; 03272 } 03273 } 03274 03275 /** 03276 * Returns file processing object, initialized only once. 03277 * 03278 * @return object File processor object 03279 */ 03280 function getFileProcObj() { 03281 if (!is_object($this->fileProcObj)) { 03282 $this->fileProcObj = t3lib_div::makeInstance('t3lib_extFileFunctions'); 03283 $this->fileProcObj->init($GLOBALS['FILEMOUNTS'], $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']); 03284 $this->fileProcObj->init_actionPerms($GLOBALS['BE_USER']->getFileoperationPermissions()); 03285 } 03286 return $this->fileProcObj; 03287 } 03288 03289 /** 03290 * Call Hook 03291 * 03292 * @param string $name name of the hook 03293 * @param array $params array with params 03294 * @return void 03295 */ 03296 public function callHook($name, $params) { 03297 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name])) { 03298 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/impexp/class.tx_impexp.php'][$name] as $hook) { 03299 t3lib_div::callUserFunction($hook, $params, $this); 03300 } 03301 } 03302 } 03303 03304 03305 03306 03307 03308 03309 03310 /***************************** 03311 * 03312 * Error handling 03313 * 03314 *****************************/ 03315 03316 /** 03317 * Sets error message in the internal error log 03318 * 03319 * @param string Error message 03320 * @return void 03321 */ 03322 function error($msg) { 03323 $this->errorLog[]=$msg; 03324 } 03325 03326 /** 03327 * Returns a table with the error-messages. 03328 * 03329 * @return string HTML print of error log 03330 */ 03331 function printErrorLog() { 03332 return count($this->errorLog) ? t3lib_utility_Debug::viewArray($this->errorLog) : ''; 03333 } 03334 } 03335 03336 03337 03338 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/impexp/class.tx_impexp.php'])) { 03339 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/impexp/class.tx_impexp.php']); 03340 } 03341 ?>
1.8.0