4216096e79d67c8ac9e7edae22ffa7f7e4ae6a86
[Packages/TYPO3.CMS.git] / typo3 / sysext / impexp / Classes / Controller / ImportExportController.php
1 <?php
2 namespace TYPO3\CMS\Impexp\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Utility\IconUtility;
18 use TYPO3\CMS\Core\Database\DatabaseConnection;
19 use TYPO3\CMS\Core\Resource\ResourceFactory;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Core\Utility\MathUtility;
23 use TYPO3\CMS\Core\Utility\PathUtility;
24 use TYPO3\CMS\Lang\LanguageService;
25
26 /**
27 * Main script class for the Import / Export facility
28 *
29 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
30 */
31 class ImportExportController extends \TYPO3\CMS\Backend\Module\BaseScriptClass {
32
33 /**
34 * @var array|\TYPO3\CMS\Core\Resource\File[]
35 */
36 protected $uploadedFiles = array();
37
38 /**
39 * Array containing the current page.
40 *
41 * @var array
42 */
43 public $pageinfo;
44
45 /**
46 * @var \TYPO3\CMS\Impexp\ImportExport
47 */
48 protected $export;
49
50 /**
51 * @var \TYPO3\CMS\Impexp\ImportExport
52 */
53 protected $import;
54
55 /**
56 * @var \TYPO3\CMS\Core\Utility\File\ExtendedFileUtility
57 */
58 protected $fileProcessor;
59
60 /**
61 * @var string
62 */
63 protected $vC = '';
64
65 /**
66 * @var LanguageService
67 */
68 protected $lang = NULL;
69
70 /**
71 * @var string
72 */
73 protected $treeHTML = '';
74
75 /**
76 * @return void
77 */
78 public function init() {
79 parent::init();
80 $this->vC = GeneralUtility::_GP('vC');
81 $this->lang = $this->getLanguageService();
82 }
83
84 /**
85 * Main module function
86 *
87 * @throws \BadFunctionCallException
88 * @throws \InvalidArgumentException
89 * @return void
90 */
91 public function main() {
92 $this->lang->includeLLFile('EXT:impexp/app/locallang.xlf');
93 // Start document template object:
94 $this->doc = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class);
95 $this->doc->backPath = $GLOBALS['BACK_PATH'];
96 $this->doc->bodyTagId = 'imp-exp-mod';
97 $this->doc->setModuleTemplate(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath('impexp') . '/app/template.html');
98 $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
99 // Setting up the context sensitive menu:
100 $this->doc->getContextMenuCode();
101 $this->doc->postCode = $this->doc->wrapScriptTags('if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';');
102 $this->doc->form = '<form action="' . htmlspecialchars(BackendUtility::getModuleUrl('xMOD_tximpexp')) . '" method="post" enctype="'
103 . $GLOBALS['TYPO3_CONF_VARS']['SYS']['form_enctype'] . '"><input type="hidden" name="id" value="' . $this->id . '" />';
104 $this->content .= $this->doc->header($this->lang->getLL('title'));
105 $this->content .= $this->doc->spacer(5);
106 // Input data grabbed:
107 $inData = GeneralUtility::_GP('tx_impexp');
108 $this->checkUpload();
109 switch ((string)$inData['action']) {
110 case 'export':
111 // Finally: If upload went well, set the new file as the thumbnail in the $inData array:
112 if (!empty($this->uploadedFiles[0])) {
113 $inData['meta']['thumbnail'] = $this->uploadedFiles[0]->getCombinedIdentifier();
114 }
115 // Call export interface
116 $this->exportData($inData);
117 break;
118 case 'import':
119 // Finally: If upload went well, set the new file as the import file:
120 if (!empty($this->uploadedFiles[0])) {
121 // Only allowed extensions....
122 if (GeneralUtility::inList('t3d,xml', $this->uploadedFiles[0]->getExtension())) {
123 $inData['file'] = $this->uploadedFiles[0]->getCombinedIdentifier();
124 }
125 }
126 // Call import interface:
127 $this->importData($inData);
128 break;
129 }
130 // Setting up the buttons and markers for docheader
131 $docHeaderButtons = $this->getButtons();
132 $markers['CONTENT'] = $this->content;
133 // Build the <body> for the module
134 $this->content = $this->doc->startPage($this->lang->getLL('title'));
135 $this->content .= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
136 $this->content .= $this->doc->endPage();
137 $this->content = $this->doc->insertStylesAndJS($this->content);
138 }
139
140 /**
141 * Print the content
142 *
143 * @return void
144 */
145 public function printContent() {
146 echo $this->content;
147 }
148
149 /**
150 * Create the panel of buttons for submitting the form or otherwise perform operations.
151 *
152 * @return array all available buttons as an associated array
153 */
154 protected function getButtons() {
155 $buttons = array(
156 'view' => '',
157 'shortcut' => ''
158 );
159 if ($this->getBackendUser()->mayMakeShortcut()) {
160 $buttons['shortcut'] = $this->doc->makeShortcutIcon('tx_impexp', '', $this->MCONF['name']);
161 }
162 // Input data grabbed:
163 $inData = GeneralUtility::_GP('tx_impexp');
164 if ((string)$inData['action'] == 'import') {
165 if ($this->id && is_array($this->pageinfo) || $this->getBackendUser()->user['admin'] && !$this->id) {
166 if (is_array($this->pageinfo) && $this->pageinfo['uid']) {
167 // View
168 $onClick = BackendUtility::viewOnClick(
169 $this->pageinfo['uid'],
170 $this->doc->backPath,
171 BackendUtility::BEgetRootLine($this->pageinfo['uid'])
172 );
173 $title = $this->lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', TRUE);
174 $buttons['view'] = '<a href="#" onclick="' . htmlspecialchars($onClick) . '" title="' . $title . '">'
175 . IconUtility::getSpriteIcon('actions-document-view') . '</a>';
176 }
177 }
178 }
179 return $buttons;
180 }
181
182 /**************************
183 * EXPORT FUNCTIONS
184 **************************/
185
186 /**
187 * Export part of module
188 * Setting content in $this->content
189 *
190 * @param array $inData Content of POST VAR tx_impexp[]..
191 * @throws \InvalidArgumentException
192 * @throws \RuntimeException
193 * @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException
194 * @return void
195 */
196 public function exportData($inData) {
197 // BUILDING EXPORT DATA:
198 // Processing of InData array values:
199 $inData['pagetree']['maxNumber'] = MathUtility::forceIntegerInRange($inData['pagetree']['maxNumber'], 1, 1000000, 100);
200 $inData['listCfg']['maxNumber'] = MathUtility::forceIntegerInRange($inData['listCfg']['maxNumber'], 1, 1000000, 100);
201 $inData['maxFileSize'] = MathUtility::forceIntegerInRange($inData['maxFileSize'], 1, 1000000, 1000);
202 $inData['filename'] = trim(preg_replace('/[^[:alnum:]._-]*/', '', preg_replace('/\\.(t3d|xml)$/', '', $inData['filename'])));
203 if (strlen($inData['filename'])) {
204 $inData['filename'] .= $inData['filetype'] == 'xml' ? '.xml' : '.t3d';
205 }
206 // Set exclude fields in export object:
207 if (!is_array($inData['exclude'])) {
208 $inData['exclude'] = array();
209 }
210 // Saving/Loading/Deleting presets:
211 $this->processPresets($inData);
212 // Create export object and configure it:
213 $this->export = GeneralUtility::makeInstance(\TYPO3\CMS\Impexp\ImportExport::class);
214 $this->export->init(0, 'export');
215 $this->export->setCharset($this->lang->charSet);
216 $this->export->maxFileSize = $inData['maxFileSize'] * 1024;
217 $this->export->excludeMap = (array)$inData['exclude'];
218 $this->export->softrefCfg = (array)$inData['softrefCfg'];
219 $this->export->extensionDependencies = (array)$inData['extension_dep'];
220 $this->export->showStaticRelations = $inData['showStaticRelations'];
221 $this->export->includeExtFileResources = !$inData['excludeHTMLfileResources'];
222 // Static tables:
223 if (is_array($inData['external_static']['tables'])) {
224 $this->export->relStaticTables = $inData['external_static']['tables'];
225 }
226 // Configure which tables external relations are included for:
227 if (is_array($inData['external_ref']['tables'])) {
228 $this->export->relOnlyTables = $inData['external_ref']['tables'];
229 }
230 $saveFilesOutsideExportFile = FALSE;
231 if (isset($inData['save_export']) && isset($inData['saveFilesOutsideExportFile']) && $inData['saveFilesOutsideExportFile'] === '1') {
232 $this->export->setSaveFilesOutsideExportFile(TRUE);
233 $saveFilesOutsideExportFile = TRUE;
234 }
235 $this->export->setHeaderBasics();
236 // Meta data setting:
237
238 $beUser = $this->getBackendUser();
239 $this->export->setMetaData(
240 $inData['meta']['title'],
241 $inData['meta']['description'],
242 $inData['meta']['notes'],
243 $beUser->user['username'],
244 $beUser->user['realName'],
245 $beUser->user['email']
246 );
247 if ($inData['meta']['thumbnail']) {
248 $theThumb = $this->getFile($inData['meta']['thumbnail']);
249 if ($theThumb !== NULL && $theThumb->exists()) {
250 $this->export->addThumbnail($theThumb->getForLocalProcessing(FALSE));
251 }
252 }
253 // Configure which records to export
254 if (is_array($inData['record'])) {
255 foreach ($inData['record'] as $ref) {
256 $rParts = explode(':', $ref);
257 $this->export->export_addRecord($rParts[0], BackendUtility::getRecord($rParts[0], $rParts[1]));
258 }
259 }
260 // Configure which tables to export
261 if (is_array($inData['list'])) {
262 $db = $this->getDatabaseConnection();
263 foreach ($inData['list'] as $ref) {
264 $rParts = explode(':', $ref);
265 if ($beUser->check('tables_select', $rParts[0])) {
266 $res = $this->exec_listQueryPid($rParts[0], $rParts[1], MathUtility::forceIntegerInRange($inData['listCfg']['maxNumber'], 1));
267 while ($subTrow = $db->sql_fetch_assoc($res)) {
268 $this->export->export_addRecord($rParts[0], $subTrow);
269 }
270 $db->sql_free_result($res);
271 }
272 }
273 }
274 // Pagetree
275 if (isset($inData['pagetree']['id'])) {
276 // Based on click-expandable tree
277 $idH = NULL;
278 if ($inData['pagetree']['levels'] == -1) {
279 $pagetree = GeneralUtility::makeInstance(\TYPO3\CMS\Impexp\LocalPageTree::class);
280 $tree = $pagetree->ext_tree($inData['pagetree']['id'], $this->filterPageIds($this->export->excludeMap));
281 $this->treeHTML = $pagetree->printTree($tree);
282 $idH = $pagetree->buffer_idH;
283 } elseif ($inData['pagetree']['levels'] == -2) {
284 $this->addRecordsForPid($inData['pagetree']['id'], $inData['pagetree']['tables'], $inData['pagetree']['maxNumber']);
285 } else {
286 // Based on depth
287 // Drawing tree:
288 // If the ID is zero, export root
289 if (!$inData['pagetree']['id'] && $beUser->isAdmin()) {
290 $sPage = array(
291 'uid' => 0,
292 'title' => 'ROOT'
293 );
294 } else {
295 $sPage = BackendUtility::getRecordWSOL('pages', $inData['pagetree']['id'], '*', ' AND ' . $this->perms_clause);
296 }
297 if (is_array($sPage)) {
298 $pid = $inData['pagetree']['id'];
299 $tree = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\View\PageTreeView::class);
300 $tree->init('AND ' . $this->perms_clause . $this->filterPageIds($this->export->excludeMap));
301 $HTML = IconUtility::getSpriteIconForRecord('pages', $sPage);
302 $tree->tree[] = array('row' => $sPage, 'HTML' => $HTML);
303 $tree->buffer_idH = array();
304 if ($inData['pagetree']['levels'] > 0) {
305 $tree->getTree($pid, $inData['pagetree']['levels'], '');
306 }
307 $idH = array();
308 $idH[$pid]['uid'] = $pid;
309 if (count($tree->buffer_idH)) {
310 $idH[$pid]['subrow'] = $tree->buffer_idH;
311 }
312 $pagetree = GeneralUtility::makeInstance(\TYPO3\CMS\Impexp\LocalPageTree::class);
313 $this->treeHTML = $pagetree->printTree($tree->tree);
314 }
315 }
316 // In any case we should have a multi-level array, $idH, with the page structure
317 // here (and the HTML-code loaded into memory for nice display...)
318 if (is_array($idH)) {
319 // Sets the pagetree and gets a 1-dim array in return with the pages (in correct submission order BTW...)
320 $flatList = $this->export->setPageTree($idH);
321 foreach ($flatList as $k => $value) {
322 $this->export->export_addRecord('pages', BackendUtility::getRecord('pages', $k));
323 $this->addRecordsForPid($k, $inData['pagetree']['tables'], $inData['pagetree']['maxNumber']);
324 }
325 }
326 }
327 // After adding ALL records we set relations:
328 for ($a = 0; $a < 10; $a++) {
329 $addR = $this->export->export_addDBRelations($a);
330 if (!count($addR)) {
331 break;
332 }
333 }
334 // Finally files are added:
335 // MUST be after the DBrelations are set so that files from ALL added records are included!
336 $this->export->export_addFilesFromRelations();
337
338 $this->export->export_addFilesFromSysFilesRecords();
339
340 // If the download button is clicked, return file
341 if ($inData['download_export'] || $inData['save_export']) {
342 switch ((string)$inData['filetype']) {
343 case 'xml':
344 $out = $this->export->compileMemoryToFileContent('xml');
345 $fExt = '.xml';
346 break;
347 case 't3d':
348 $this->export->dontCompress = 1;
349 // intentional fall-through
350 default:
351 $out = $this->export->compileMemoryToFileContent();
352 $fExt = ($this->export->doOutputCompress() ? '-z' : '') . '.t3d';
353 }
354 // Filename:
355 $dlFile = $inData['filename'];
356 if (!$dlFile) {
357 $exportName = substr(preg_replace('/[^[:alnum:]_]/', '-', $inData['download_export_name']), 0, 20);
358 $dlFile = 'T3D_' . $exportName . '_' . date('Y-m-d_H-i') . $fExt;
359 }
360
361 // Export for download:
362 if ($inData['download_export']) {
363 $mimeType = 'application/octet-stream';
364 Header('Content-Type: ' . $mimeType);
365 Header('Content-Length: ' . strlen($out));
366 Header('Content-Disposition: attachment; filename=' . basename($dlFile));
367 echo $out;
368 die;
369 }
370 // Export by saving:
371 if ($inData['save_export']) {
372 $saveFolder = $this->getDefaultImportExportFolder();
373 if ($saveFolder !== FALSE && $saveFolder->checkActionPermission('write')) {
374 $temporaryFileName = GeneralUtility::tempnam('export');
375 file_put_contents($temporaryFileName, $out);
376 $file = $saveFolder->addFile($temporaryFileName, $dlFile, 'replace');
377 if ($saveFilesOutsideExportFile) {
378 $filesFolderName = $dlFile . '.files';
379 $filesFolder = $saveFolder->createFolder($filesFolderName);
380 $temporaryFolderForExport = ResourceFactory::getInstance()->retrieveFileOrFolderObject($this->export->getTemporaryFilesPathForExport());
381 $temporaryFilesForExport = $temporaryFolderForExport->getFiles();
382 foreach ($temporaryFilesForExport as $temporaryFileForExport) {
383 $filesFolder->getStorage()->moveFile($temporaryFileForExport, $filesFolder);
384 }
385 $temporaryFolderForExport->delete();
386 }
387
388 $this->content .= $this->doc->section($GLOBALS['LANG']->getLL('exportdata_savedFile'), sprintf($GLOBALS['LANG']->getLL('exportdata_savedInSBytes', TRUE), $file->getPublicUrl(), GeneralUtility::formatSize(strlen($out))), 0, 1);
389 } else {
390 $this->content .= $this->doc->section($GLOBALS['LANG']->getLL('exportdata_problemsSavingFile'), sprintf($GLOBALS['LANG']->getLL('exportdata_badPathS', TRUE), $this->getTemporaryFolderPath()), 0, 1, 2);
391 }
392 }
393 }
394 // OUTPUT to BROWSER:
395 // Now, if we didn't make download file, show configuration form based on export:
396 $menuItems = array();
397 // Export configuration
398 $row = array();
399 $this->makeConfigurationForm($inData, $row);
400 $menuItems[] = array(
401 'label' => $this->lang->getLL('tableselec_configuration'),
402 'content' => '
403 <table border="0" cellpadding="1" cellspacing="1">
404 ' . implode('
405 ', $row) . '
406 </table>
407 '
408 );
409 // File options
410 $row = array();
411 $this->makeSaveForm($inData, $row);
412 $menuItems[] = array(
413 'label' => $this->lang->getLL('exportdata_filePreset'),
414 'content' => '
415 <table border="0" cellpadding="1" cellspacing="1">
416 ' . implode('
417 ', $row) . '
418 </table>
419 '
420 );
421 // File options
422 $row = array();
423 $this->makeAdvancedOptionsForm($inData, $row);
424 $menuItems[] = array(
425 'label' => $this->lang->getLL('exportdata_advancedOptions'),
426 'content' => '
427 <table border="0" cellpadding="1" cellspacing="1">
428 ' . implode('
429 ', $row) . '
430 </table>
431 '
432 );
433 // Generate overview:
434 $overViewContent = $this->export->displayContentOverview();
435 // Print errors that might be:
436 $errors = $this->export->printErrorLog();
437 $menuItems[] = array(
438 'label' => $this->lang->getLL('exportdata_messages'),
439 'content' => $errors,
440 'stateIcon' => $errors ? 2 : 0
441 );
442 // Add hidden fields and create tabs:
443
444 $content = $this->doc->getDynamicTabMenu($menuItems, 'tx_impexp_export', 1, FALSE, TRUE, FALSE);
445 $content .= '<input type="hidden" name="tx_impexp[action]" value="export" />';
446 $this->content .= $this->doc->section('', $content, 0, 1);
447 // Output Overview:
448 $this->content .= $this->doc->section($this->lang->getLL('execlistqu_structureToBeExported'), $overViewContent, 0, 1);
449 }
450
451 /**
452 * Adds records to the export object for a specific page id.
453 *
454 * @param int $k Page id for which to select records to add
455 * @param array $tables Array of table names to select from
456 * @param int $maxNumber Max amount of records to select
457 * @return void
458 */
459 public function addRecordsForPid($k, $tables, $maxNumber) {
460 if (!is_array($tables)) {
461 return;
462 }
463 $db = $this->getDatabaseConnection();
464 foreach ($GLOBALS['TCA'] as $table => $value) {
465 if ($table != 'pages' && (in_array($table, $tables) || in_array('_ALL', $tables))) {
466 if ($this->getBackendUser()->check('tables_select', $table) && !$GLOBALS['TCA'][$table]['ctrl']['is_static']) {
467 $res = $this->exec_listQueryPid($table, $k, MathUtility::forceIntegerInRange($maxNumber, 1));
468 while ($subTrow = $db->sql_fetch_assoc($res)) {
469 $this->export->export_addRecord($table, $subTrow);
470 }
471 $db->sql_free_result($res);
472 }
473 }
474 }
475 }
476
477 /**
478 * Selects records from table / pid
479 *
480 * @param string $table Table to select from
481 * @param int $pid Page ID to select from
482 * @param int $limit Max number of records to select
483 * @return \mysqli_result|object Database resource
484 */
485 public function exec_listQueryPid($table, $pid, $limit) {
486 $db = $this->getDatabaseConnection();
487 $orderBy = $GLOBALS['TCA'][$table]['ctrl']['sortby']
488 ? 'ORDER BY ' . $GLOBALS['TCA'][$table]['ctrl']['sortby']
489 : $GLOBALS['TCA'][$table]['ctrl']['default_sortby'];
490 $res = $db->exec_SELECTquery(
491 '*',
492 $table,
493 'pid=' . (int)$pid . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table),
494 '',
495 $db->stripOrderBy($orderBy),
496 $limit
497 );
498 // Warning about hitting limit:
499 if ($db->sql_num_rows($res) == $limit) {
500 $limitWarning = sprintf($this->lang->getLL('makeconfig_anSqlQueryReturned', TRUE), $limit);
501 $this->content .= $this->doc->section($this->lang->getLL('execlistqu_maxNumberLimit'), $limitWarning, 0, 1, 2);
502 }
503 return $res;
504 }
505
506 /**
507 * Create configuration form
508 *
509 * @param array $inData Form configurat data
510 * @param array $row Table row accumulation variable. This is filled with table rows.
511 * @return void Sets content in $this->content
512 */
513 public function makeConfigurationForm($inData, &$row) {
514 $nameSuggestion = '';
515 // Page tree export options:
516 if (isset($inData['pagetree']['id'])) {
517 $nameSuggestion .= 'tree_PID' . $inData['pagetree']['id'] . '_L' . $inData['pagetree']['levels'];
518 $row[] = '
519 <tr class="tableheader bgColor5">
520 <td colspan="2">' . $this->lang->getLL('makeconfig_exportPagetreeConfiguration', TRUE)
521 . BackendUtility::cshItem('xMOD_tx_impexp', 'pageTreeCfg') . '</td>
522 </tr>';
523 $row[] = '
524 <tr class="bgColor4">
525 <td><strong>' . $this->lang->getLL('makeconfig_pageId', TRUE) . '</strong></td>
526 <td>' . htmlspecialchars($inData['pagetree']['id']) . '<input type="hidden" value="'
527 . htmlspecialchars($inData['pagetree']['id']) . '" name="tx_impexp[pagetree][id]" /></td>
528 </tr>';
529 $row[] = '
530 <tr class="bgColor4">
531 <td><strong>' . $this->lang->getLL('makeconfig_tree', TRUE) . '</strong>'
532 . BackendUtility::cshItem('xMOD_tx_impexp', 'pageTreeDisplay') . '</td>
533 <td>' . ($this->treeHTML ?: $this->lang->getLL('makeconfig_noTreeExportedOnly', TRUE)) . '</td>
534 </tr>';
535 $opt = array(
536 '-2' => $this->lang->getLL('makeconfig_tablesOnThisPage'),
537 '-1' => $this->lang->getLL('makeconfig_expandedTree'),
538 '0' => $this->lang->getLL('makeconfig_onlyThisPage'),
539 '1' => $this->lang->getLL('makeconfig_1Level'),
540 '2' => $this->lang->getLL('makeconfig_2Levels'),
541 '3' => $this->lang->getLL('makeconfig_3Levels'),
542 '4' => $this->lang->getLL('makeconfig_4Levels'),
543 '999' => $this->lang->getLL('makeconfig_infinite')
544 );
545 $row[] = '
546 <tr class="bgColor4">
547 <td><strong>' . $this->lang->getLL('makeconfig_levels', TRUE) . '</strong>'
548 . BackendUtility::cshItem('xMOD_tx_impexp', 'pageTreeMode') . '</td>
549 <td>' . $this->renderSelectBox('tx_impexp[pagetree][levels]', $inData['pagetree']['levels'], $opt) . '</td>
550 </tr>';
551 $row[] = '
552 <tr class="bgColor4">
553 <td><strong>' . $this->lang->getLL('makeconfig_includeTables', TRUE) . '</strong>'
554 . BackendUtility::cshItem('xMOD_tx_impexp', 'pageTreeRecordLimit') . '</td>
555 <td>' . $this->tableSelector('tx_impexp[pagetree][tables]', $inData['pagetree']['tables'], 'pages') . '<br/>
556 ' . $this->lang->getLL('makeconfig_maxNumberOfRecords', TRUE) . '<br/>
557 <input type="text" name="tx_impexp[pagetree][maxNumber]" value="'
558 . htmlspecialchars($inData['pagetree']['maxNumber']) . '"' . $this->doc->formWidth(10) . ' /><br/>
559 </td>
560 </tr>';
561 }
562 // Single record export:
563 if (is_array($inData['record'])) {
564 $row[] = '
565 <tr class="tableheader bgColor5">
566 <td colspan="2">' . $this->lang->getLL('makeconfig_exportSingleRecord', TRUE)
567 . BackendUtility::cshItem('xMOD_tx_impexp', 'singleRecord') . '</td>
568 </tr>';
569 foreach ($inData['record'] as $ref) {
570 $rParts = explode(':', $ref);
571 $tName = $rParts[0];
572 $rUid = $rParts[1];
573 $nameSuggestion .= $tName . '_' . $rUid;
574 $rec = BackendUtility::getRecordWSOL($tName, $rUid);
575 $row[] = '
576 <tr class="bgColor4">
577 <td><strong>' . $this->lang->getLL('makeconfig_record', TRUE) . '</strong></td>
578 <td>' . IconUtility::getSpriteIconForRecord($tName, $rec) . BackendUtility::getRecordTitle($tName, $rec, TRUE)
579 . '<input type="hidden" name="tx_impexp[record][]" value="' . htmlspecialchars(($tName . ':' . $rUid)) . '" /></td>
580 </tr>';
581 }
582 }
583 // Single tables/pids:
584 if (is_array($inData['list'])) {
585 $row[] = '
586 <tr class="tableheader bgColor5">
587 <td colspan="2">' . $this->lang->getLL('makeconfig_exportTablesFromPages', TRUE) . '</td>
588 </tr>';
589 // Display information about pages from which the export takes place
590 $tblList = '';
591 foreach ($inData['list'] as $reference) {
592 $referenceParts = explode(':', $reference);
593 $tableName = $referenceParts[0];
594 if ($this->getBackendUser()->check('tables_select', $tableName)) {
595 // If the page is actually the root, handle it differently
596 // NOTE: we don't compare integers, because the number actually comes from the split string above
597 if ($referenceParts[1] === '0') {
598 $iconAndTitle = IconUtility::getSpriteIcon('apps-pagetree-root') . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
599 } else {
600 $record = BackendUtility::getRecordWSOL('pages', $referenceParts[1]);
601 $iconAndTitle = IconUtility::getSpriteIconForRecord('pages', $record)
602 . BackendUtility::getRecordTitle('pages', $record, TRUE);
603 }
604 $tblList .= 'Table "' . $tableName . '" from ' . $iconAndTitle
605 . '<input type="hidden" name="tx_impexp[list][]" value="' . htmlspecialchars($reference) . '" /><br/>';
606 }
607 }
608 $row[] = '
609 <tr class="bgColor4">
610 <td><strong>' . $this->lang->getLL('makeconfig_tablePids', TRUE) . '</strong>'
611 . BackendUtility::cshItem('xMOD_tx_impexp', 'tableList') . '</td>
612 <td>' . $tblList . '</td>
613 </tr>';
614 $row[] = '
615 <tr class="bgColor4">
616 <td><strong>' . $this->lang->getLL('makeconfig_maxNumberOfRecords', TRUE)
617 . BackendUtility::cshItem('xMOD_tx_impexp', 'tableListMaxNumber') . '</strong></td>
618 <td>
619 <input type="text" name="tx_impexp[listCfg][maxNumber]" value="'
620 . htmlspecialchars($inData['listCfg']['maxNumber']) . '"' . $this->doc->formWidth(10) . ' /><br/>
621 </td>
622 </tr>';
623 }
624 $row[] = '
625 <tr class="tableheader bgColor5">
626 <td colspan="2">' . $this->lang->getLL('makeconfig_relationsAndExclusions', TRUE) . '</td>
627 </tr>';
628 // Add relation selector:
629 $row[] = '
630 <tr class="bgColor4">
631 <td><strong>' . $this->lang->getLL('makeconfig_includeRelationsToTables', TRUE) . '</strong>'
632 . BackendUtility::cshItem('xMOD_tx_impexp', 'inclRelations') . '</td>
633 <td>' . $this->tableSelector('tx_impexp[external_ref][tables]', $inData['external_ref']['tables']) . '</td>
634 </tr>';
635 // Add static relation selector:
636 $row[] = '
637 <tr class="bgColor4">
638 <td><strong>' . $this->lang->getLL('makeconfig_useStaticRelationsFor', TRUE) . '</strong>'
639 . BackendUtility::cshItem('xMOD_tx_impexp', 'staticRelations') . '</td>
640 <td>' . $this->tableSelector('tx_impexp[external_static][tables]', $inData['external_static']['tables']) . '<br/>
641 <label for="checkShowStaticRelations">' . $this->lang->getLL('makeconfig_showStaticRelations', TRUE)
642 . '</label> <input type="checkbox" name="tx_impexp[showStaticRelations]" id="checkShowStaticRelations" value="1"'
643 . ($inData['showStaticRelations'] ? ' checked="checked"' : '') . ' />
644 </td>
645 </tr>';
646 // Exclude:
647 $excludeHiddenFields = '';
648 if (is_array($inData['exclude'])) {
649 foreach ($inData['exclude'] as $key => $value) {
650 $excludeHiddenFields .= '<input type="hidden" name="tx_impexp[exclude][' . $key . ']" value="1" />';
651 }
652 }
653 if (count($inData['exclude'])) {
654 $excludedElements = '<em>' . implode(', ', array_keys($inData['exclude'])) . '</em><hr/><label for="checkExclude">'
655 . $this->lang->getLL('makeconfig_clearAllExclusions', TRUE)
656 . '</label> <input type="checkbox" name="tx_impexp[exclude]" id="checkExclude" value="1" />';
657 } else {
658 $excludedElements = $this->lang->getLL('makeconfig_noExcludedElementsYet', TRUE);
659 }
660 $row[] = '
661 <tr class="bgColor4">
662 <td><strong>' . $this->lang->getLL('makeconfig_excludeElements', TRUE) . '</strong>'
663 . BackendUtility::cshItem('xMOD_tx_impexp', 'excludedElements') . '</td>
664 <td>' . $excludeHiddenFields . '
665 ' . $excludedElements . '
666 </td>
667 </tr>';
668 // Add buttons:
669 $row[] = '
670 <tr class="bgColor4">
671 <td>&nbsp;</td>
672 <td>
673 <input class="btn btn-default" type="submit" value="' . $this->lang->getLL('makeadvanc_update', TRUE) . '" />
674 <input type="hidden" name="tx_impexp[download_export_name]" value="' . substr($nameSuggestion, 0, 30) . '" />
675 </td>
676 </tr>';
677 }
678
679 /**
680 * Create advanced options form
681 * Sets content in $this->content
682 *
683 * @param array $inData Form configurat data
684 * @param array $row Table row accumulation variable. This is filled with table rows.
685 * @return void
686 */
687 public function makeAdvancedOptionsForm($inData, &$row) {
688 // Soft references
689 $row[] = '
690 <tr class="tableheader bgColor5">
691 <td colspan="2">' . $this->lang->getLL('makeadvanc_softReferences', TRUE) . '</td>
692 </tr>';
693 $row[] = '
694 <tr class="bgColor4">
695 <td><label for="checkExcludeHTMLfileResources"><strong>'
696 . $this->lang->getLL('makeadvanc_excludeHtmlCssFile', TRUE) . '</strong></label>'
697 . BackendUtility::cshItem('xMOD_tx_impexp', 'htmlCssResources') . '</td>
698 <td><input type="checkbox" name="tx_impexp[excludeHTMLfileResources]" id="checkExcludeHTMLfileResources" value="1"'
699 . ($inData['excludeHTMLfileResources'] ? ' checked="checked"' : '') . ' /></td>
700 </tr>';
701
702 // Files options
703 $row[] = '
704 <tr class="tableheader bgColor5">
705 <td colspan="2">' . $this->lang->getLL('makeadvanc_files', TRUE) . '</td>
706 </tr>';
707 $row[] = '
708 <tr class="bgColor4">
709 <td><label for="saveFilesOutsideExportFile"><strong>'
710 . $this->lang->getLL('makeadvanc_saveFilesOutsideExportFile', TRUE) . '</strong><br />'
711 . $this->lang->getLL('makeadvanc_saveFilesOutsideExportFile_limit', TRUE) . '</label></td>
712 <td><input type="checkbox" name="tx_impexp[saveFilesOutsideExportFile]" id="saveFilesOutsideExportFile" value="1"'
713 . ($inData['saveFilesOutsideExportFile'] ? ' checked="checked"' : '') . ' /></td>
714 </tr>';
715 // Extensions
716 $row[] = '
717 <tr class="tableheader bgColor5">
718 <td colspan="2">' . $this->lang->getLL('makeadvanc_extensionDependencies', TRUE) . '</td>
719 </tr>';
720 $row[] = '
721 <tr class="bgColor4">
722 <td><strong>' . $this->lang->getLL('makeadvanc_selectExtensionsThatThe', TRUE) . '</strong>'
723 . BackendUtility::cshItem('xMOD_tx_impexp', 'extensionDependencies') . '</td>
724 <td>' . $this->extensionSelector('tx_impexp[extension_dep]', $inData['extension_dep']) . '</td>
725 </tr>';
726 // Add buttons:
727 $row[] = '
728 <tr class="bgColor4">
729 <td>&nbsp;</td>
730 <td>
731 <input class="btn btn-default" type="submit" value="' . $this->lang->getLL('makesavefo_update', TRUE) . '" />
732 <input type="hidden" name="tx_impexp[download_export_name]" value="" />
733 </td>
734 </tr>';
735 }
736
737 /**
738 * Create configuration form
739 *
740 * @param array $inData Form configurat data
741 * @param array $row Table row accumulation variable. This is filled with table rows.
742 * @return void Sets content in $this->content
743 */
744 public function makeSaveForm($inData, &$row) {
745 // Presets:
746 $row[] = '
747 <tr class="tableheader bgColor5">
748 <td colspan="2">' . $this->lang->getLL('makesavefo_presets', TRUE) . '</td>
749 </tr>';
750 $opt = array('');
751 $where = '(public>0 OR user_uid=' . (int)$this->getBackendUser()->user['uid'] . ')'
752 . ($inData['pagetree']['id'] ? ' AND (item_uid=' . (int)$inData['pagetree']['id'] . ' OR item_uid=0)' : '');
753 $presets = $this->getDatabaseConnection()->exec_SELECTgetRows('*', 'tx_impexp_presets', $where);
754 if (is_array($presets)) {
755 foreach ($presets as $presetCfg) {
756 $opt[$presetCfg['uid']] = $presetCfg['title'] . ' [' . $presetCfg['uid'] . ']'
757 . ($presetCfg['public'] ? ' [Public]' : '')
758 . ($presetCfg['user_uid'] === $this->getBackendUser()->user['uid'] ? ' [Own]' : '');
759 }
760 }
761 $row[] = '
762 <tr class="bgColor4">
763 <td><strong>' . $this->lang->getLL('makesavefo_presets', TRUE) . '</strong>'
764 . BackendUtility::cshItem('xMOD_tx_impexp', 'presets') . '</td>
765 <td>
766 ' . $this->lang->getLL('makesavefo_selectPreset', TRUE) . '<br/>
767 ' . $this->renderSelectBox('preset[select]', '', $opt) . '
768 <br/>
769 <input class="btn btn-default" type="submit" value="' . $this->lang->getLL('makesavefo_load', TRUE) . '" name="preset[load]" />
770 <input class="btn btn-default" type="submit" value="' . $this->lang->getLL('makesavefo_save', TRUE) . '" name="preset[save]" onclick="return confirm(\'' . $this->lang->getLL('makesavefo_areYouSure', TRUE) . '\');" />
771 <input class="btn btn-default" type="submit" value="' . $this->lang->getLL('makesavefo_delete', TRUE) . '" name="preset[delete]" onclick="return confirm(\'' . $this->lang->getLL('makesavefo_areYouSure', TRUE) . '\');" />
772 <input class="btn btn-default" type="submit" value="' . $this->lang->getLL('makesavefo_merge', TRUE) . '" name="preset[merge]" onclick="return confirm(\'' . $this->lang->getLL('makesavefo_areYouSure', TRUE) . '\');" />
773 <br/>
774 ' . $this->lang->getLL('makesavefo_titleOfNewPreset', TRUE) . '
775 <input type="text" name="tx_impexp[preset][title]" value="'
776 . htmlspecialchars($inData['preset']['title']) . '"' . $this->doc->formWidth(30) . ' /><br/>
777 <label for="checkPresetPublic">' . $this->lang->getLL('makesavefo_public', TRUE) . '</label>
778 <input type="checkbox" name="tx_impexp[preset][public]" id="checkPresetPublic" value="1"'
779 . ($inData['preset']['public'] ? ' checked="checked"' : '') . ' /><br/>
780 </td>
781 </tr>';
782 // Output options:
783 $row[] = '
784 <tr class="tableheader bgColor5">
785 <td colspan="2">' . $this->lang->getLL('makesavefo_outputOptions', TRUE) . '</td>
786 </tr>';
787 // Meta data:
788 $thumbnailFiles = array();
789 foreach ($this->getThumbnailFiles() as $thumbnailFile) {
790 $thumbnailFiles[$thumbnailFile->getCombinedIdentifier()] = $thumbnailFile->getName();
791 }
792 if (!empty($thumbnailFiles)) {
793 array_unshift($thumbnailFiles, '');
794 }
795 $thumbnail = NULL;
796 if (!empty($inData['meta']['thumbnail'])) {
797 $thumbnail = $this->getFile($inData['meta']['thumbnail']);
798 }
799 $saveFolder = $this->getDefaultImportExportFolder();
800
801 $row[] = '
802 <tr class="bgColor4">
803 <td><strong>' . $this->lang->getLL('makesavefo_metaData', TRUE) . '</strong>'
804 . BackendUtility::cshItem('xMOD_tx_impexp', 'metadata') . '</td>
805 <td>
806 ' . $this->lang->getLL('makesavefo_title', TRUE) . ' <br/>
807 <input type="text" name="tx_impexp[meta][title]" value="' . htmlspecialchars($inData['meta']['title']) . '"' . $this->doc->formWidth(30) . ' /><br/>
808 ' . $this->lang->getLL('makesavefo_description', TRUE) . ' <br/>
809 <input type="text" name="tx_impexp[meta][description]" value="' . htmlspecialchars($inData['meta']['description']) . '"' . $this->doc->formWidth(30) . ' /><br/>
810 ' . $this->lang->getLL('makesavefo_notes', TRUE) . ' <br/>
811 <textarea name="tx_impexp[meta][notes]"' . $this->doc->formWidth(30, 1) . '>' . GeneralUtility::formatForTextarea($inData['meta']['notes']) . '</textarea><br/>
812 ' . (!empty($thumbnailFiles) ? '
813 ' . $this->lang->getLL('makesavefo_thumbnail', TRUE) . '<br/>
814 ' . $this->renderSelectBox('tx_impexp[meta][thumbnail]', $inData['meta']['thumbnail'], $thumbnailFiles) : '') . '<br/>
815 ' . ($thumbnail ? '<img src="' . htmlspecialchars($thumbnail->getPublicUrl(TRUE)) . '" vspace="5" style="border: solid black 1px;" alt="" /><br/>' : '') . '
816 ' . $this->lang->getLL('makesavefo_uploadThumbnail', TRUE) . '<br/>
817 ' . ($saveFolder ? '<input type="file" name="upload_1" ' . $this->doc->formWidth(30) . ' size="30" /><br/>
818 <input type="hidden" name="file[upload][1][target]" value="' . htmlspecialchars($saveFolder->getCombinedIdentifier()) . '" />
819 <input type="hidden" name="file[upload][1][data]" value="1" /><br />' : '') . '
820 </td>
821 </tr>';
822 // Add file options:
823 $opt = array();
824 if ($this->export->compress) {
825 $opt['t3d_compressed'] = $this->lang->getLL('makesavefo_t3dFileCompressed');
826 }
827 $opt['t3d'] = $this->lang->getLL('makesavefo_t3dFile');
828 $opt['xml'] = $this->lang->getLL('makesavefo_xml');
829 $fileName = '';
830 if ($saveFolder) {
831 $fileName = sprintf($this->lang->getLL('makesavefo_filenameSavedInS', TRUE), $saveFolder->getCombinedIdentifier())
832 . '<br/>
833 <input type="text" name="tx_impexp[filename]" value="'
834 . htmlspecialchars($inData['filename']) . '"' . $this->doc->formWidth(30) . ' /><br/>';
835 }
836 $row[] = '
837 <tr>
838 <td>
839 <strong>' . $this->lang->getLL('makesavefo_fileFormat', TRUE) . '</strong>'
840 . BackendUtility::cshItem('xMOD_tx_impexp', 'fileFormat') . '
841 </td>
842 <td>
843 ' . $this->renderSelectBox('tx_impexp[filetype]', $inData['filetype'], $opt) . '<br/>
844 ' . $this->lang->getLL('makesavefo_maxSizeOfFiles', TRUE) . '<br/>
845 <input type="text" name="tx_impexp[maxFileSize]" value="' . htmlspecialchars($inData['maxFileSize']) . '"' . $this->doc->formWidth(10) . ' />
846 <br/>
847 ' . $fileName . '
848 </td>
849 </tr>';
850 // Add buttons:
851 $row[] = '
852 <tr>
853 <td>&nbsp;</td>
854 <td>
855 <input class="btn btn-default" type="submit" value="' . $this->lang->getLL('makesavefo_update', TRUE) . '" /> -
856 <input class="btn btn-default" type="submit" value="' . $this->lang->getLL('makesavefo_downloadExport', TRUE) . '" name="tx_impexp[download_export]" />
857 ' . ($saveFolder ? ' - <input class="btn btn-default" type="submit" value="' . $this->lang->getLL('importdata_saveToFilename', TRUE) . '" name="tx_impexp[save_export]" />' : '') . '
858 </td>
859 </tr>';
860 }
861
862 /**************************
863 * IMPORT FUNCTIONS
864 **************************/
865
866 /**
867 * Import part of module
868 *
869 * @param array $inData Content of POST VAR tx_impexp[]..
870 * @throws \BadFunctionCallException
871 * @throws \InvalidArgumentException
872 * @throws \RuntimeException
873 * @return void Setting content in $this->content
874 */
875 public function importData($inData) {
876 $access = is_array($this->pageinfo) ? 1 : 0;
877 $beUser = $this->getBackendUser();
878 if ($this->id && $access || $beUser->user['admin'] && !$this->id) {
879 if ($beUser->user['admin'] && !$this->id) {
880 $this->pageinfo = array('title' => '[root-level]', 'uid' => 0, 'pid' => 0);
881 }
882 if ($inData['new_import']) {
883 unset($inData['import_mode']);
884 }
885 /** @var $import \TYPO3\CMS\Impexp\ImportExport */
886 $import = GeneralUtility::makeInstance(\TYPO3\CMS\Impexp\ImportExport::class);
887 $import->init(0, 'import');
888 $import->update = $inData['do_update'];
889 $import->import_mode = $inData['import_mode'];
890 $import->enableLogging = $inData['enableLogging'];
891 $import->global_ignore_pid = $inData['global_ignore_pid'];
892 $import->force_all_UIDS = $inData['force_all_UIDS'];
893 $import->showDiff = !$inData['notShowDiff'];
894 $import->allowPHPScripts = $inData['allowPHPScripts'];
895 $import->softrefInputValues = $inData['softrefInputValues'];
896 // OUTPUT creation:
897 $menuItems = array();
898 // Make input selector:
899 // must have trailing slash.
900 $path = $this->getDefaultImportExportFolder();
901 $exportFiles = $this->getExportFiles();
902 // Configuration
903 $row = array();
904 $selectOptions = array('');
905 foreach ($exportFiles as $file) {
906 $selectOptions[$file->getCombinedIdentifier()] = $file->getPublicUrl();
907 }
908 $row[] = '
909 <tr>
910 <th colspan="2">' . $this->lang->getLL('importdata_selectFileToImport', TRUE) . '</th>
911 </tr>';
912 $noCompressorAvailable = !$import->compress
913 ? '<br /><span class="typo3-red">' . $this->lang->getLL('importdata_noteNoDecompressorAvailable', TRUE) . '</span>'
914 : '';
915 $row[] = '
916 <tr>
917 <td valign="top">
918 ' . $this->lang->getLL('importdata_file', TRUE) . ''
919 . BackendUtility::cshItem('xMOD_tx_impexp', 'importFile') . '
920 </td>
921 <td>
922 ' . $this->renderSelectBox('tx_impexp[file]', $inData['file'], $selectOptions) . '<br />'
923 . sprintf($this->lang->getLL('importdata_fromPathS', TRUE), $path ? $path->getCombinedIdentifier() : $this->lang->getLL('importdata_no_accessible_file_mount', TRUE)) .
924 $noCompressorAvailable . '
925 </td>
926 </tr>';
927 $row[] = '
928 <tr>
929 <th colspan="2">
930 ' . $this->lang->getLL('importdata_importOptions', TRUE) . '
931 </th>
932 </tr>';
933 $row[] = '
934 <tr>
935 <td valign="top">
936 ' . $this->lang->getLL('importdata_update', TRUE)
937 . BackendUtility::cshItem('xMOD_tx_impexp', 'update') . '
938 </td>
939 <td>
940 <input type="checkbox" name="tx_impexp[do_update]" id="checkDo_update" value="1"'
941 . ($inData['do_update'] ? ' checked="checked"' : '') . ' />
942 <label for="checkDo_update">' . $this->lang->getLL('importdata_updateRecords', TRUE) . '</label>
943 <br/>
944 <em>(' . $this->lang->getLL('importdata_thisOptionRequiresThat', TRUE) . ')</em>' . ($inData['do_update'] ? ' <hr/>
945 <input type="checkbox" name="tx_impexp[global_ignore_pid]" id="checkGlobal_ignore_pid" value="1"'
946 . ($inData['global_ignore_pid'] ? ' checked="checked"' : '') . ' />
947 <label for="checkGlobal_ignore_pid">' . $this->lang->getLL('importdata_ignorePidDifferencesGlobally', TRUE) . '</label><br/>
948 <em>(' . $this->lang->getLL('importdata_ifYouSetThis', TRUE) . ')</em>
949 ' : '') . '
950 </td>
951 </tr>';
952 $allowPhpScripts = $beUser->isAdmin()
953 ? '
954 <input type="checkbox" name="tx_impexp[allowPHPScripts]" id="checkAllowPHPScripts" value="1"'
955 . ($inData['allowPHPScripts'] ? ' checked="checked"' : '') . ' />
956 <label for="checkAllowPHPScripts">' . $this->lang->getLL('importdata_allowToWriteBanned', TRUE) . '</label><br/>'
957 : '';
958 $doUpdate = !$inData['do_update'] && $beUser->isAdmin()
959 ? '
960 <br/>
961 <input type="checkbox" name="tx_impexp[force_all_UIDS]" id="checkForce_all_UIDS" value="1"'
962 . ($inData['force_all_UIDS'] ? ' checked="checked"' : '') . ' />
963 <label for="checkForce_all_UIDS"><span class="typo3-red">'
964 . $this->lang->getLL('importdata_force_all_UIDS', TRUE) . '</span></label><br/>
965 <em>(' . $this->lang->getLL('importdata_force_all_UIDS_descr', TRUE) . ')</em>'
966 : '';
967 $row[] = '<tr>
968 <td valign="top">
969 ' . $this->lang->getLL('importdata_options', TRUE) . BackendUtility::cshItem('xMOD_tx_impexp', 'options') . '
970 </td>
971 <td>
972 <input type="checkbox" name="tx_impexp[notShowDiff]" id="checkNotShowDiff" value="1"'
973 . ($inData['notShowDiff'] ? ' checked="checked"' : '') . ' />
974 <label for="checkNotShowDiff">' . $this->lang->getLL('importdata_doNotShowDifferences', TRUE) . '</label><br/>
975 <em>(' . $this->lang->getLL('importdata_greenValuesAreFrom', TRUE) . ')</em>
976 <br/><br/>
977
978 ' . $allowPhpScripts . $doUpdate . '
979 </td>
980 </tr>';
981 $newImport = !$inData['import_file']
982 ? '<input class="btn btn-default" type="submit" value="' . $this->lang->getLL('importdata_preview', TRUE) . '" />' . ($inData['file']
983 ? ' - <input class="btn btn-default" type="submit" value="' . ($inData['do_update']
984 ? $this->lang->getLL('importdata_update_299e', TRUE)
985 : $this->lang->getLL('importdata_import', TRUE)) . '" name="tx_impexp[import_file]" onclick="return confirm(\''
986 . $this->lang->getLL('importdata_areYouSure', TRUE) . '\');" />'
987 : '')
988 : '<input class="btn btn-default" type="submit" name="tx_impexp[new_import]" value="' . $this->lang->getLL('importdata_newImport', TRUE) . '" />';
989 $row[] = '<tr>
990 <td valign="top">
991 ' . $this->lang->getLL('importdata_action', TRUE) . BackendUtility::cshItem('xMOD_tx_impexp', 'action') . '
992 </td>
993 <td>
994 ' . $newImport . '
995 <input type="hidden" name="tx_impexp[action]" value="import" />
996 </td>
997 </tr>';
998 $row[] = '<tr>
999 <td valign="top">
1000 ' . $this->lang->getLL('importdata_enableLogging', TRUE)
1001 . BackendUtility::cshItem('xMOD_tx_impexp', 'enableLogging') . '
1002 </td>
1003 <td>
1004 <input type="checkbox" name="tx_impexp[enableLogging]" id="checkEnableLogging" value="1"'
1005 . ($inData['enableLogging'] ? ' checked="checked"' : '') . ' />
1006 <label for="checkEnableLogging">' . $this->lang->getLL('importdata_writeIndividualDbActions', TRUE) . '</label><br/>
1007 <em>(' . $this->lang->getLL('importdata_thisIsDisabledBy', TRUE) . ')</em>
1008 </td>
1009 </tr>';
1010 $menuItems[] = array(
1011 'label' => $this->lang->getLL('importdata_import', TRUE),
1012 'content' => '
1013 <table border="0" cellpadding="1" cellspacing="1">
1014 ' . implode('
1015 ', $row) . '
1016 </table>
1017 '
1018 );
1019 // Upload file:
1020 $tempFolder = $this->getDefaultImportExportFolder();
1021 if ($tempFolder) {
1022 $row = array();
1023 $row[] = '
1024 <tr>
1025 <th colspan="2">' . $this->lang->getLL('importdata_uploadFileFromLocal', TRUE) . '</th>
1026 </tr>';
1027 $row[] = '
1028 <tr>
1029 <td valign="top">
1030 ' . $this->lang->getLL('importdata_browse', TRUE) . BackendUtility::cshItem('xMOD_tx_impexp', 'upload') . '
1031 </td>
1032 <td>
1033 <input type="file" name="upload_1"' . $this->doc->formWidth(35) . ' size="40" />
1034 <input type="hidden" name="file[upload][1][target]" value="' . htmlspecialchars($tempFolder->getCombinedIdentifier()) . '" />
1035 <input type="hidden" name="file[upload][1][data]" value="1" />
1036 <br />
1037 <input class="btn btn-default" type="submit" name="_upload" value="' . $this->lang->sL('LLL:EXT:lang/locallang_core.xlf:file_upload.php.submit', TRUE) . '" />
1038 <input type="checkbox" name="overwriteExistingFiles" id="checkOverwriteExistingFiles" value="1" checked="checked" />
1039 <label for="checkOverwriteExistingFiles">' . $this->lang->sL('LLL:EXT:lang/locallang_misc.xlf:overwriteExistingFiles', TRUE) . '</label>
1040 </td>
1041 </tr>';
1042 if (GeneralUtility::_POST('_upload')) {
1043 $noFileUploaded = $this->fileProcessor->internalUploadMap[1]
1044 ? $this->lang->getLL('importdata_success', TRUE) . ' ' . $this->uploadedFiles[0]->getName()
1045 : '<span class="typo3-red">' . $this->lang->getLL('importdata_failureNoFileUploaded', TRUE) . '</span>';
1046 $row[] = '<tr class="bgColor4">
1047 <td>' . $this->lang->getLL('importdata_uploadStatus', TRUE) . '</td>
1048 <td>' . $noFileUploaded . '</td>
1049 </tr>';
1050 }
1051 $menuItems[] = array(
1052 'label' => $this->lang->getLL('importdata_upload'),
1053 'content' => '
1054 <table border="0" cellpadding="1" cellspacing="1">
1055 ' . implode('
1056 ', $row) . '
1057 </table>
1058 '
1059 );
1060 }
1061 // Perform import or preview depending:
1062 $overviewContent = '';
1063 $extensionInstallationMessage = '';
1064 $inFile = $this->getFile($inData['file']);
1065 if ($inFile !== NULL && $inFile->exists()) {
1066 $trow = array();
1067 if ($import->loadFile($inFile->getForLocalProcessing(FALSE), 1)) {
1068 // Check extension dependencies:
1069 $extKeysToInstall = array();
1070 if (is_array($import->dat['header']['extensionDependencies'])) {
1071 foreach ($import->dat['header']['extensionDependencies'] as $extKey) {
1072 if (!\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded($extKey)) {
1073 $extKeysToInstall[] = $extKey;
1074 }
1075 }
1076 }
1077 if (count($extKeysToInstall)) {
1078 $extensionInstallationMessage = 'Before you can install this T3D file you need to install the extensions "'
1079 . implode('", "', $extKeysToInstall) . '".';
1080 }
1081 if ($inData['import_file']) {
1082 if (!count($extKeysToInstall)) {
1083 $import->importData($this->id);
1084 BackendUtility::setUpdateSignal('updatePageTree');
1085 }
1086 }
1087 $import->display_import_pid_record = $this->pageinfo;
1088 $overviewContent = $import->displayContentOverview();
1089 }
1090 // Meta data output:
1091 $trow[] = '<tr class="bgColor5">
1092 <td colspan="2"><strong>' . $this->lang->getLL('importdata_metaData', TRUE) . '</strong></td>
1093 </tr>';
1094 $trow[] = '<tr class="bgColor4">
1095 <td><strong>' . $this->lang->getLL('importdata_title', TRUE) . '</strong></td>
1096 <td width="95%">' . nl2br(htmlspecialchars($import->dat['header']['meta']['title'])) . '</td>
1097 </tr>';
1098 $trow[] = '<tr class="bgColor4">
1099 <td><strong>' . $this->lang->getLL('importdata_description', TRUE) . '</strong></td>
1100 <td width="95%">' . nl2br(htmlspecialchars($import->dat['header']['meta']['description'])) . '</td>
1101 </tr>';
1102 $trow[] = '<tr class="bgColor4">
1103 <td><strong>' . $this->lang->getLL('importdata_notes', TRUE) . '</strong></td>
1104 <td width="95%">' . nl2br(htmlspecialchars($import->dat['header']['meta']['notes'])) . '</td>
1105 </tr>';
1106 $trow[] = '<tr class="bgColor4">
1107 <td><strong>' . $this->lang->getLL('importdata_packager', TRUE) . '</strong></td>
1108 <td width="95%">' . nl2br(htmlspecialchars(($import->dat['header']['meta']['packager_name']
1109 . ' (' . $import->dat['header']['meta']['packager_username'] . ')'))) . '<br/>
1110 ' . $this->lang->getLL('importdata_email', TRUE) . ' '
1111 . $import->dat['header']['meta']['packager_email'] . '</td>
1112 </tr>';
1113 // Thumbnail icon:
1114 if (is_array($import->dat['header']['thumbnail'])) {
1115 $pI = pathinfo($import->dat['header']['thumbnail']['filename']);
1116 if (GeneralUtility::inList('gif,jpg,png,jpeg', strtolower($pI['extension']))) {
1117 // Construct filename and write it:
1118 $fileName = PATH_site . 'typo3temp/importthumb.' . $pI['extension'];
1119 GeneralUtility::writeFile($fileName, $import->dat['header']['thumbnail']['content']);
1120 // Check that the image really is an image and not a malicious PHP script...
1121 if (getimagesize($fileName)) {
1122 // Create icon tag:
1123 $iconTag = '<img src="' . $this->doc->backPath . '../' . PathUtility::stripPathSitePrefix($fileName)
1124 . '" ' . $import->dat['header']['thumbnail']['imgInfo'][3]
1125 . ' vspace="5" style="border: solid black 1px;" alt="" />';
1126 $trow[] = '<tr class="bgColor4">
1127 <td><strong>' . $this->lang->getLL('importdata_icon', TRUE) . '</strong></td>
1128 <td>' . $iconTag . '</td>
1129 </tr>';
1130 } else {
1131 GeneralUtility::unlink_tempfile($fileName);
1132 }
1133 }
1134 }
1135 $menuItems[] = array(
1136 'label' => $this->lang->getLL('importdata_metaData_1387'),
1137 'content' => '
1138 <table border="0" cellpadding="1" cellspacing="1">
1139 ' . implode('
1140 ', $trow) . '
1141 </table>
1142 '
1143 );
1144 }
1145 // Print errors that might be:
1146 $errors = $import->printErrorLog();
1147 $menuItems[] = array(
1148 'label' => $this->lang->getLL('importdata_messages'),
1149 'content' => $errors,
1150 'stateIcon' => $errors ? 2 : 0
1151 );
1152 // Output tabs:
1153 $content = $this->doc->getDynamicTabMenu($menuItems, 'tx_impexp_import', 1, FALSE, TRUE, FALSE);
1154 if ($extensionInstallationMessage) {
1155 $content = '<div style="border: 1px black solid; margin: 10px 10px 10px 10px; padding: 10px 10px 10px 10px;">'
1156 . $this->doc->icons(1) . htmlspecialchars($extensionInstallationMessage) . '</div>' . $content;
1157 }
1158 $this->content .= $this->doc->section('', $content, 0, 1);
1159 // Print overview:
1160 if ($overviewContent) {
1161 $this->content .= $this->doc->section($inData['import_file']
1162 ? $this->lang->getLL('importdata_structureHasBeenImported', TRUE)
1163 : $this->lang->getLL('filterpage_structureToBeImported', TRUE), $overviewContent, 0, 1);
1164 }
1165 }
1166 }
1167
1168 /****************************
1169 * Preset functions
1170 ****************************/
1171
1172 /**
1173 * Manipulate presets
1174 *
1175 * @param array $inData In data array, passed by reference!
1176 * @return void
1177 */
1178 public function processPresets(&$inData) {
1179 $presetData = GeneralUtility::_GP('preset');
1180 $err = FALSE;
1181 $msg = '';
1182 // Save preset
1183 $beUser = $this->getBackendUser();
1184 // cast public checkbox to int, since this is a int field and NULL is not allowed
1185 $inData['preset']['public'] = (int)$inData['preset']['public'];
1186 if (isset($presetData['save'])) {
1187 $preset = $this->getPreset($presetData['select']);
1188 // Update existing
1189 if (is_array($preset)) {
1190 if ($beUser->isAdmin() || $preset['user_uid'] === $beUser->user['uid']) {
1191 $fields_values = array(
1192 'public' => $inData['preset']['public'],
1193 'title' => $inData['preset']['title'],
1194 'item_uid' => $inData['pagetree']['id'],
1195 'preset_data' => serialize($inData)
1196 );
1197 $this->getDatabaseConnection()->exec_UPDATEquery('tx_impexp_presets', 'uid=' . (int)$preset['uid'], $fields_values);
1198 $msg = 'Preset #' . $preset['uid'] . ' saved!';
1199 } else {
1200 $msg = 'ERROR: The preset was not saved because you were not the owner of it!';
1201 $err = TRUE;
1202 }
1203 } else {
1204 // Insert new:
1205 $fields_values = array(
1206 'user_uid' => $beUser->user['uid'],
1207 'public' => $inData['preset']['public'],
1208 'title' => $inData['preset']['title'],
1209 'item_uid' => $inData['pagetree']['id'],
1210 'preset_data' => serialize($inData)
1211 );
1212 $this->getDatabaseConnection()->exec_INSERTquery('tx_impexp_presets', $fields_values);
1213 $msg = 'New preset "' . htmlspecialchars($inData['preset']['title']) . '" is created';
1214 }
1215 }
1216 // Delete preset:
1217 if (isset($presetData['delete'])) {
1218 $preset = $this->getPreset($presetData['select']);
1219 if (is_array($preset)) {
1220 // Update existing
1221 if ($beUser->isAdmin() || $preset['user_uid'] === $beUser->user['uid']) {
1222 $this->getDatabaseConnection()->exec_DELETEquery('tx_impexp_presets', 'uid=' . (int)$preset['uid']);
1223 $msg = 'Preset #' . $preset['uid'] . ' deleted!';
1224 } else {
1225 $msg = 'ERROR: You were not the owner of the preset so you could not delete it.';
1226 $err = TRUE;
1227 }
1228 } else {
1229 $msg = 'ERROR: No preset selected for deletion.';
1230 $err = TRUE;
1231 }
1232 }
1233 // Load preset
1234 if (isset($presetData['load']) || isset($presetData['merge'])) {
1235 $preset = $this->getPreset($presetData['select']);
1236 if (is_array($preset)) {
1237 // Update existing
1238 $inData_temp = unserialize($preset['preset_data']);
1239 if (is_array($inData_temp)) {
1240 if (isset($presetData['merge'])) {
1241 // Merge records in:
1242 if (is_array($inData_temp['record'])) {
1243 $inData['record'] = array_merge((array)$inData['record'], $inData_temp['record']);
1244 }
1245 // Merge lists in:
1246 if (is_array($inData_temp['list'])) {
1247 $inData['list'] = array_merge((array)$inData['list'], $inData_temp['list']);
1248 }
1249 } else {
1250 $msg = 'Preset #' . $preset['uid'] . ' loaded!';
1251 $inData = $inData_temp;
1252 }
1253 } else {
1254 $msg = 'ERROR: No configuratio data found in preset record!';
1255 $err = TRUE;
1256 }
1257 } else {
1258 $msg = 'ERROR: No preset selected for loading.';
1259 $err = TRUE;
1260 }
1261 }
1262 // Show message:
1263 if ($msg !== '') {
1264 $this->content .= $this->doc->section('Presets', $msg, 0, 1, $err ? 3 : 1);
1265 }
1266 }
1267
1268 /**
1269 * Get single preset record
1270 *
1271 * @param int $uid Preset record
1272 * @return array Preset record, if any (otherwise FALSE)
1273 */
1274 public function getPreset($uid) {
1275 return $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'tx_impexp_presets', 'uid=' . (int)$uid);
1276 }
1277
1278 /****************************
1279 * Helper functions
1280 ****************************/
1281
1282 /**
1283 * Returns a \TYPO3\CMS\Core\Resource\Folder object for saving export files
1284 * to the server and is also used for uploading import files.
1285 *
1286 * @throws \InvalidArgumentException
1287 * @return NULL|\TYPO3\CMS\Core\Resource\Folder
1288 */
1289 protected function getDefaultImportExportFolder() {
1290 $defaultImportExportFolder = NULL;
1291
1292 $defaultTemporaryFolder = $this->getBackendUser()->getDefaultUploadTemporaryFolder();
1293 if ($defaultTemporaryFolder !== NULL) {
1294
1295 $importExportFolderName = 'importexport';
1296 $createFolder = !$defaultTemporaryFolder->hasFolder($importExportFolderName);
1297 if ($createFolder === TRUE) {
1298 try {
1299 $defaultImportExportFolder = $defaultTemporaryFolder->createFolder($importExportFolderName);
1300 } catch (\TYPO3\CMS\Core\Resource\Exception $folderAccessException) {}
1301 } else {
1302 $defaultImportExportFolder = $defaultTemporaryFolder->getSubfolder($importExportFolderName);
1303 }
1304 }
1305
1306 return $defaultImportExportFolder;
1307 }
1308
1309
1310 /**
1311 * Check if a file has been uploaded
1312 *
1313 * @throws \InvalidArgumentException
1314 * @throws \UnexpectedValueException
1315 * @return void
1316 */
1317 public function checkUpload() {
1318 $file = GeneralUtility::_GP('file');
1319 // Initializing:
1320 $this->fileProcessor = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Utility\File\ExtendedFileUtility::class);
1321 $this->fileProcessor->init(array(), $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']);
1322 $this->fileProcessor->setActionPermissions();
1323 $this->fileProcessor->dontCheckForUnique = GeneralUtility::_GP('overwriteExistingFiles') ? 1 : 0;
1324 // Checking referer / executing:
1325 $refInfo = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
1326 $httpHost = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY');
1327 if (
1328 $httpHost != $refInfo['host']
1329 && !$GLOBALS['$TYPO3_CONF_VARS']['SYS']['doNotCheckReferer']
1330 && $this->vC != $this->getBackendUser()->veriCode()
1331 ) {
1332 $this->fileProcessor->writeLog(0, 2, 1, 'Referer host "%s" and server host "%s" did not match!', array($refInfo['host'], $httpHost));
1333 } else {
1334 $this->fileProcessor->start($file);
1335 $result = $this->fileProcessor->processData();
1336 if (!empty($result['upload'])) {
1337 foreach ($result['upload'] as $uploadedFiles) {
1338 $this->uploadedFiles += $uploadedFiles;
1339 }
1340 }
1341 }
1342 }
1343
1344 /**
1345 * Makes a selector-box from optValues
1346 *
1347 * @param string $prefix Form element name
1348 * @param string $value Current value
1349 * @param array $optValues Options to display (key/value pairs)
1350 * @return string HTML select element
1351 */
1352 public function renderSelectBox($prefix, $value, $optValues) {
1353 $opt = array();
1354 $isSelFlag = 0;
1355 foreach ($optValues as $k => $v) {
1356 $sel = (string)$k === (string)$value ? ' selected="selected"' : '';
1357 if ($sel) {
1358 $isSelFlag++;
1359 }
1360 $opt[] = '<option value="' . htmlspecialchars($k) . '"' . $sel . '>' . htmlspecialchars($v) . '</option>';
1361 }
1362 if (!$isSelFlag && (string)$value !== '') {
1363 $opt[] = '<option value="' . htmlspecialchars($value) . '" selected="selected">'
1364 . htmlspecialchars(('[\'' . $value . '\']')) . '</option>';
1365 }
1366 return '<select name="' . $prefix . '">' . implode('', $opt) . '</select>';
1367 }
1368
1369 /**
1370 * Returns a selector-box with TCA tables
1371 *
1372 * @param string $prefix Form element name prefix
1373 * @param array $value The current values selected
1374 * @param string $excludeList Table names (and the string "_ALL") to exclude. Comma list
1375 * @return string HTML select element
1376 */
1377 public function tableSelector($prefix, $value, $excludeList = '') {
1378 $optValues = array();
1379 if (!GeneralUtility::inList($excludeList, '_ALL')) {
1380 $optValues['_ALL'] = '[' . $this->lang->getLL('ALL_tables') . ']';
1381 }
1382 foreach ($GLOBALS['TCA'] as $table => $_) {
1383 if ($this->getBackendUser()->check('tables_select', $table) && !GeneralUtility::inList($excludeList, $table)) {
1384 $optValues[$table] = $table;
1385 }
1386 }
1387 // make box:
1388 $opt = array();
1389 $opt[] = '<option value=""></option>';
1390 $sel = '';
1391 foreach ($optValues as $k => $v) {
1392 if (is_array($value)) {
1393 $sel = in_array($k, $value) ? ' selected="selected"' : '';
1394 }
1395 $opt[] = '<option value="' . htmlspecialchars($k) . '"' . $sel . '>' . htmlspecialchars($v) . '</option>';
1396 }
1397 return '<select name="' . $prefix . '[]" multiple="multiple" size="'
1398 . MathUtility::forceIntegerInRange(count($opt), 5, 10) . '">' . implode('', $opt) . '</select>';
1399 }
1400
1401 /**
1402 * Returns a selector-box with loaded extension keys
1403 *
1404 * @param string $prefix Form element name prefix
1405 * @param array $value The current values selected
1406 * @return string HTML select element
1407 */
1408 public function extensionSelector($prefix, $value) {
1409 $loadedExtensions = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getLoadedExtensionListArray();
1410
1411 // make box:
1412 $opt = array();
1413 $opt[] = '<option value=""></option>';
1414 foreach ($loadedExtensions as $extensionKey) {
1415 $sel = '';
1416 if (is_array($value)) {
1417 $sel = in_array($extensionKey, $value) ? ' selected="selected"' : '';
1418 }
1419 $opt[] = '<option value="' . htmlspecialchars($extensionKey) . '"' . $sel . '>'
1420 . htmlspecialchars($extensionKey) . '</option>';
1421 }
1422 return '<select name="' . $prefix . '[]" multiple="multiple" size="'
1423 . MathUtility::forceIntegerInRange(count($opt), 5, 10) . '">' . implode('', $opt) . '</select>';
1424 }
1425
1426 /**
1427 * Filter page IDs by traversing exclude array, finding all
1428 * excluded pages (if any) and making an AND NOT IN statement for the select clause.
1429 *
1430 * @param array $exclude Exclude array from import/export object.
1431 * @return string AND where clause part to filter out page uids.
1432 */
1433 public function filterPageIds($exclude) {
1434 // Get keys:
1435 $exclude = array_keys($exclude);
1436 // Traverse
1437 $pageIds = array();
1438 foreach ($exclude as $element) {
1439 list($table, $uid) = explode(':', $element);
1440 if ($table === 'pages') {
1441 $pageIds[] = (int)$uid;
1442 }
1443 }
1444 // Add to clause:
1445 if (count($pageIds)) {
1446 return ' AND uid NOT IN (' . implode(',', $pageIds) . ')';
1447 }
1448 return '';
1449 }
1450
1451 /**
1452 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1453 */
1454 protected function getBackendUser() {
1455 return $GLOBALS['BE_USER'];
1456 }
1457
1458 /**
1459 * @return DatabaseConnection
1460 */
1461 protected function getDatabaseConnection() {
1462 return $GLOBALS['TYPO3_DB'];
1463 }
1464
1465 /**
1466 * @return LanguageService
1467 */
1468 protected function getLanguageService() {
1469 return $GLOBALS['LANG'];
1470 }
1471
1472 /**
1473 * Gets thumbnail files.
1474 *
1475 * @throws \InvalidArgumentException
1476 * @return array|\TYPO3\CMS\Core\Resource\File[]
1477 */
1478 protected function getThumbnailFiles() {
1479 $thumbnailFiles = array();
1480 $defaultTemporaryFolder = $this->getDefaultImportExportFolder();
1481
1482 if ($defaultTemporaryFolder === NULL) {
1483 return $thumbnailFiles;
1484 }
1485
1486 /** @var $filter \TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter */
1487 $filter = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter::class);
1488 $filter->setAllowedFileExtensions(array('png', 'gif', 'jpg'));
1489 $defaultTemporaryFolder->getStorage()->addFileAndFolderNameFilter(array($filter, 'filterFileList'));
1490 $thumbnailFiles = $defaultTemporaryFolder->getFiles();
1491
1492 return $thumbnailFiles;
1493 }
1494
1495 /**
1496 * Gets all export files.
1497 *
1498 * @throws \InvalidArgumentException
1499 * @return array|\TYPO3\CMS\Core\Resource\File[]
1500 */
1501 protected function getExportFiles() {
1502 $exportFiles = array();
1503
1504 $folder = $this->getDefaultImportExportFolder();
1505 if ($folder !== NULL) {
1506
1507 /** @var $filter \TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter */
1508 $filter = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter::class);
1509 $filter->setAllowedFileExtensions(array('t3d', 'xml'));
1510 $folder->getStorage()->addFileAndFolderNameFilter(array($filter, 'filterFileList'));
1511
1512 $exportFiles = $folder->getFiles();
1513 }
1514
1515 return $exportFiles;
1516 }
1517
1518 /**
1519 * Gets a file by combined identifier.
1520 *
1521 * @param string $combinedIdentifier
1522 * @return NULL|\TYPO3\CMS\Core\Resource\File
1523 */
1524 protected function getFile($combinedIdentifier) {
1525 try {
1526 $file = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFileObjectFromCombinedIdentifier($combinedIdentifier);
1527 } catch (\Exception $exception) {
1528 $file = NULL;
1529 }
1530
1531 return $file;
1532 }
1533
1534 }