2 namespace TYPO3\CMS\Impexp\Controller
;
5 * This file is part of the TYPO3 CMS project.
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.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use Psr\Http\Message\ResponseInterface
;
18 use Psr\Http\Message\ServerRequestInterface
;
19 use TYPO3\CMS\Backend\Template\ModuleTemplate
;
20 use TYPO3\CMS\Backend\Tree\View\PageTreeView
;
21 use TYPO3\CMS\Backend\Utility\BackendUtility
;
22 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication
;
23 use TYPO3\CMS\Core\Database\ConnectionPool
;
24 use TYPO3\CMS\Core\Database\Query\QueryHelper
;
25 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction
;
26 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
;
27 use TYPO3\CMS\Core\Http\HtmlResponse
;
28 use TYPO3\CMS\Core\Imaging\Icon
;
29 use TYPO3\CMS\Core\Imaging\IconFactory
;
30 use TYPO3\CMS\Core\Localization\LanguageService
;
31 use TYPO3\CMS\Core\Messaging\FlashMessage
;
32 use TYPO3\CMS\Core\Messaging\FlashMessageService
;
33 use TYPO3\CMS\Core\
Resource\DuplicationBehavior
;
34 use TYPO3\CMS\Core\
Resource\Exception
;
35 use TYPO3\CMS\Core\
Resource\Filter\FileExtensionFilter
;
36 use TYPO3\CMS\Core\
Resource\ResourceFactory
;
37 use TYPO3\CMS\Core\Type\Bitmask\Permission
;
38 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility
;
39 use TYPO3\CMS\Core\Utility\File\ExtendedFileUtility
;
40 use TYPO3\CMS\Core\Utility\GeneralUtility
;
41 use TYPO3\CMS\Core\Utility\PathUtility
;
42 use TYPO3\CMS\Fluid\View\StandaloneView
;
43 use TYPO3\CMS\Impexp\Domain\Repository\PresetRepository
;
44 use TYPO3\CMS\Impexp\Export
;
45 use TYPO3\CMS\Impexp\Import
;
46 use TYPO3\CMS\Impexp\View\ExportPageTreeView
;
49 * Main script class for the Import / Export facility
50 * @internal this is a TYPO3 Backend controller implementation and not part of TYPO3's Core API.
52 class ImportExportController
55 * @var array|\TYPO3\CMS\Core\Resource\File[]
57 protected $uploadedFiles = [];
60 * The integer value of the GET/POST var, 'id'. Used for submodules to the 'Web' module (page id)
67 * Array containing the current page.
74 * A WHERE clause for selection records from the pages table based on read-permissions of the current backend user.
78 protected $perms_clause;
91 * @var ExtendedFileUtility
93 protected $fileProcessor;
96 * @var LanguageService
103 protected $treeHTML = '';
108 protected $iconFactory;
111 * The name of the module
115 protected $moduleName = 'xMOD_tximpexp';
118 * ModuleTemplate Container
120 * @var ModuleTemplate
122 protected $moduleTemplate;
125 * The name of the shortcut for this page
129 protected $shortcutName;
134 * @var PresetRepository
136 protected $presetRepository;
139 * @var StandaloneView
141 protected $standaloneView;
146 protected $excludeDisabledRecords = false;
153 protected $returnUrl;
158 public function __construct()
160 $this->iconFactory
= GeneralUtility
::makeInstance(IconFactory
::class);
161 $this->moduleTemplate
= GeneralUtility
::makeInstance(ModuleTemplate
::class);
162 $this->presetRepository
= GeneralUtility
::makeInstance(PresetRepository
::class);
164 $templatePath = ExtensionManagementUtility
::extPath('impexp') . 'Resources/Private/';
166 $this->standaloneView
= GeneralUtility
::makeInstance(StandaloneView
::class);
167 $this->standaloneView
->setTemplateRootPaths([$templatePath . 'Templates/ImportExport/']);
168 $this->standaloneView
->setLayoutRootPaths([$templatePath . 'Layouts/']);
169 $this->standaloneView
->setPartialRootPaths([$templatePath . 'Partials/']);
170 $this->standaloneView
->getRequest()->setControllerExtensionName('impexp');
174 * Initializes the module and defining necessary variables for this module to run.
176 protected function init()
178 $this->id
= (int)GeneralUtility
::_GP('id');
179 $this->perms_clause
= $this->getBackendUser()->getPagePermsClause(Permission
::PAGE_SHOW
);
180 $this->returnUrl
= GeneralUtility
::sanitizeLocalUrl(GeneralUtility
::_GP('returnUrl'));
181 $this->lang
= $this->getLanguageService();
185 * Main module function
187 * @throws \BadFunctionCallException
188 * @throws \InvalidArgumentException
189 * @throws \RuntimeException
191 protected function main()
193 $this->lang
->includeLLFile('EXT:impexp/Resources/Private/Language/locallang.xlf');
195 $this->pageinfo
= BackendUtility
::readPageAccess($this->id
, $this->perms_clause
);
196 if (is_array($this->pageinfo
)) {
197 $this->moduleTemplate
->getDocHeaderComponent()->setMetaInformation($this->pageinfo
);
199 // Setting up the context sensitive menu:
200 $this->moduleTemplate
->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
201 $this->moduleTemplate
->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Impexp/ImportExport');
202 $this->moduleTemplate
->addJavaScriptCode(
204 'if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id
. ';'
207 // Input data grabbed:
208 $inData = GeneralUtility
::_GP('tx_impexp');
209 if ($inData === null) {
210 // This happens if the post request was larger than allowed on the server
211 // We set the import action as default and output a user information
213 'action' => 'import',
215 $flashMessage = GeneralUtility
::makeInstance(
217 $this->lang
->getLL('importdata_upload_nodata'),
218 $this->lang
->getLL('importdata_upload_error'),
221 $flashMessageService = GeneralUtility
::makeInstance(FlashMessageService
::class);
222 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
223 $defaultFlashMessageQueue->enqueue($flashMessage);
225 if (!array_key_exists('excludeDisabled', $inData)) {
226 // flag doesn't exist initially; state is on by default
227 $inData['excludeDisabled'] = 1;
229 $uriBuilder = GeneralUtility
::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder
::class);
230 $this->standaloneView
->assign('moduleUrl', (string)$uriBuilder->buildUriFromRoute('xMOD_tximpexp'));
231 $this->standaloneView
->assign('id', $this->id
);
232 $this->standaloneView
->assign('inData', $inData);
234 switch ((string)$inData['action']) {
236 $this->shortcutName
= $this->lang
->getLL('title_export');
237 // Call export interface
238 $this->exportData($inData);
239 $this->standaloneView
->setTemplate('Export.html');
242 $backendUser = $this->getBackendUser();
243 $isEnabledForNonAdmin = (bool)($backendUser->getTSConfig()['options.']['impexp.']['enableImportForNonAdminUser'] ??
false);
244 if (!$backendUser->isAdmin() && !$isEnabledForNonAdmin) {
245 throw new \
RuntimeException(
246 'Import module is disabled for non admin users and '
247 . 'userTsConfig options.impexp.enableImportForNonAdminUser is not enabled.',
251 $this->shortcutName
= $this->lang
->getLL('title_import');
252 if (GeneralUtility
::_POST('_upload')) {
253 $this->checkUpload();
255 // Finally: If upload went well, set the new file as the import file:
256 if (!empty($this->uploadedFiles
[0])) {
257 // Only allowed extensions....
258 $extension = $this->uploadedFiles
[0]->getExtension();
259 if ($extension === 't3d' ||
$extension === 'xml') {
260 $inData['file'] = $this->uploadedFiles
[0]->getCombinedIdentifier();
263 // Call import interface:
264 $this->importData($inData);
265 $this->standaloneView
->setTemplate('Import.html');
269 // Setting up the buttons and markers for docheader
274 * Injects the request object for the current request and gathers all data
278 * Incoming array has syntax:
279 * GETvar 'id' = import page id (must be readable)
281 * file = pointing to filename relative to public web path
283 * [all relation fields are clear, but not files]
284 * - page-tree is written first
285 * - then remaining pages (to the root of import)
286 * - then all other records are written either to related included pages or if not found to import-root (should be a sysFolder in most cases)
287 * - then all internal relations are set and non-existing relations removed, relations to static tables preserved.
291 * Incoming array has syntax:
296 * record[] = table:uid
298 * pagetree[id] = (single id)
299 * pagetree[levels]=1,2,3, -1 = currently unpacked tree, -2 = only tables on page
300 * pagetree[tables][]=table/_ALL
302 * external_ref[tables][]=table/_ALL
304 * @param ServerRequestInterface $request the current request
305 * @return ResponseInterface the response with the content
307 public function mainAction(ServerRequestInterface
$request): ResponseInterface
311 $this->moduleTemplate
->setContent($this->standaloneView
->render());
312 return new HtmlResponse($this->moduleTemplate
->renderContent());
316 * Create the panel of buttons for submitting the form or otherwise perform operations.
318 protected function getButtons()
320 $buttonBar = $this->moduleTemplate
->getDocHeaderComponent()->getButtonBar();
321 if ($this->getBackendUser()->mayMakeShortcut()) {
322 $shortcutButton = $buttonBar->makeShortcutButton()
323 ->setGetVariables(['tx_impexp'])
324 ->setDisplayName($this->shortcutName
)
325 ->setModuleName($this->moduleName
);
326 $buttonBar->addButton($shortcutButton);
329 if ($this->returnUrl
) {
330 $backButton = $buttonBar->makeLinkButton()
331 ->setHref($this->returnUrl
)
332 ->setTitle($this->lang
->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
333 ->setIcon($this->moduleTemplate
->getIconFactory()->getIcon('actions-view-go-back', Icon
::SIZE_SMALL
));
334 $buttonBar->addButton($backButton);
336 // Input data grabbed:
337 $inData = GeneralUtility
::_GP('tx_impexp');
338 if ((string)$inData['action'] === 'import') {
339 if ($this->id
&& is_array($this->pageinfo
) ||
$this->getBackendUser()->isAdmin() && !$this->id
) {
340 if (is_array($this->pageinfo
) && $this->pageinfo
['uid']) {
342 $onClick = BackendUtility
::viewOnClick(
343 $this->pageinfo
['uid'],
345 BackendUtility
::BEgetRootLine($this->pageinfo
['uid'])
347 $viewButton = $buttonBar->makeLinkButton()
348 ->setTitle($this->lang
->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
350 ->setIcon($this->iconFactory
->getIcon('actions-view-page', Icon
::SIZE_SMALL
))
351 ->setOnClick($onClick);
352 $buttonBar->addButton($viewButton);
358 /**************************
360 **************************/
363 * Export part of module
365 * @param array $inData Content of POST VAR tx_impexp[]..
366 * @throws \InvalidArgumentException
367 * @throws \RuntimeException
368 * @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException
370 protected function exportData($inData)
372 // BUILDING EXPORT DATA:
373 // Processing of InData array values:
374 $inData['filename'] = trim(preg_replace('/[^[:alnum:]._-]*/', '', preg_replace('/\\.(t3d|xml)$/', '', $inData['filename'])));
375 if (strlen($inData['filename'])) {
376 $inData['filename'] .= $inData['filetype'] === 'xml' ?
'.xml' : '.t3d';
378 // Set exclude fields in export object:
379 if (!is_array($inData['exclude'])) {
380 $inData['exclude'] = [];
382 // Saving/Loading/Deleting presets:
383 $this->presetRepository
->processPresets($inData);
384 // Create export object and configure it:
385 $this->export
= GeneralUtility
::makeInstance(Export
::class);
386 $this->export
->init(0);
387 $this->export
->excludeMap
= (array)$inData['exclude'];
388 $this->export
->softrefCfg
= (array)$inData['softrefCfg'];
389 $this->export
->extensionDependencies
= ($inData['extension_dep'] === '') ?
[] : (array)$inData['extension_dep'];
390 $this->export
->showStaticRelations
= $inData['showStaticRelations'];
391 $this->export
->includeExtFileResources
= !$inData['excludeHTMLfileResources'];
392 $this->excludeDisabledRecords
= (bool)$inData['excludeDisabled'];
393 $this->export
->setExcludeDisabledRecords($this->excludeDisabledRecords
);
396 if (is_array($inData['external_static']['tables'])) {
397 $this->export
->relStaticTables
= $inData['external_static']['tables'];
399 // Configure which tables external relations are included for:
400 if (is_array($inData['external_ref']['tables'])) {
401 $this->export
->relOnlyTables
= $inData['external_ref']['tables'];
403 $saveFilesOutsideExportFile = false;
404 if (isset($inData['save_export']) && isset($inData['saveFilesOutsideExportFile']) && $inData['saveFilesOutsideExportFile'] === '1') {
405 $this->export
->setSaveFilesOutsideExportFile(true);
406 $saveFilesOutsideExportFile = true;
408 $this->export
->setHeaderBasics();
409 // Meta data setting:
411 $beUser = $this->getBackendUser();
412 $this->export
->setMetaData(
413 $inData['meta']['title'],
414 $inData['meta']['description'],
415 $inData['meta']['notes'],
416 $beUser->user
['username'],
417 $beUser->user
['realName'],
418 $beUser->user
['email']
420 // Configure which records to export
421 if (is_array($inData['record'])) {
422 foreach ($inData['record'] as $ref) {
423 $rParts = explode(':', $ref);
424 $this->export
->export_addRecord($rParts[0], BackendUtility
::getRecord($rParts[0], $rParts[1]));
427 // Configure which tables to export
428 if (is_array($inData['list'])) {
429 foreach ($inData['list'] as $ref) {
430 $rParts = explode(':', $ref);
431 if ($beUser->check('tables_select', $rParts[0])) {
432 $statement = $this->exec_listQueryPid($rParts[0], $rParts[1]);
433 while ($subTrow = $statement->fetch()) {
434 $this->export
->export_addRecord($rParts[0], $subTrow);
440 if (isset($inData['pagetree']['id'])) {
441 // Based on click-expandable tree
443 if ($inData['pagetree']['levels'] == -1) {
444 $pagetree = GeneralUtility
::makeInstance(ExportPageTreeView
::class);
445 if ($this->excludeDisabledRecords
) {
446 $pagetree->init(BackendUtility
::BEenableFields('pages'));
448 $tree = $pagetree->ext_tree($inData['pagetree']['id'], $this->filterPageIds($this->export
->excludeMap
));
449 $this->treeHTML
= $pagetree->printTree($tree);
450 $idH = $pagetree->buffer_idH
;
451 } elseif ($inData['pagetree']['levels'] == -2) {
452 $this->addRecordsForPid($inData['pagetree']['id'], $inData['pagetree']['tables']);
456 // If the ID is zero, export root
457 if (!$inData['pagetree']['id'] && $beUser->isAdmin()) {
463 $sPage = BackendUtility
::getRecordWSOL('pages', $inData['pagetree']['id'], '*', ' AND ' . $this->perms_clause
);
465 if (is_array($sPage)) {
466 $pid = $inData['pagetree']['id'];
467 $tree = GeneralUtility
::makeInstance(PageTreeView
::class);
468 $initClause = 'AND ' . $this->perms_clause
. $this->filterPageIds($this->export
->excludeMap
);
469 if ($this->excludeDisabledRecords
) {
470 $initClause .= BackendUtility
::BEenableFields('pages');
472 $tree->init($initClause);
473 $HTML = $this->iconFactory
->getIconForRecord('pages', $sPage, Icon
::SIZE_SMALL
)->render();
474 $tree->tree
[] = ['row' => $sPage, 'HTML' => $HTML];
475 $tree->buffer_idH
= [];
476 if ($inData['pagetree']['levels'] > 0) {
477 $tree->getTree($pid, $inData['pagetree']['levels'], '');
480 $idH[$pid]['uid'] = $pid;
481 if (!empty($tree->buffer_idH
)) {
482 $idH[$pid]['subrow'] = $tree->buffer_idH
;
484 $pagetree = GeneralUtility
::makeInstance(ExportPageTreeView
::class);
485 $this->treeHTML
= $pagetree->printTree($tree->tree
);
486 $this->shortcutName
.= ' (' . $sPage['title'] . ')';
489 // In any case we should have a multi-level array, $idH, with the page structure
490 // here (and the HTML-code loaded into memory for nice display...)
491 if (is_array($idH)) {
492 // Sets the pagetree and gets a 1-dim array in return with the pages (in correct submission order BTW...)
493 $flatList = $this->export
->setPageTree($idH);
494 foreach ($flatList as $k => $value) {
495 $this->export
->export_addRecord('pages', BackendUtility
::getRecord('pages', $k));
496 $this->addRecordsForPid($k, $inData['pagetree']['tables']);
500 // After adding ALL records we set relations:
501 for ($a = 0; $a < 10; $a++
) {
502 $addR = $this->export
->export_addDBRelations($a);
507 // Finally files are added:
508 // MUST be after the DBrelations are set so that files from ALL added records are included!
509 $this->export
->export_addFilesFromRelations();
511 $this->export
->export_addFilesFromSysFilesRecords();
513 // If the download button is clicked, return file
514 if ($inData['download_export'] ||
$inData['save_export']) {
515 switch ((string)$inData['filetype']) {
517 $out = $this->export
->compileMemoryToFileContent('xml');
521 $this->export
->dontCompress
= 1;
522 // intentional fall-through
525 $out = $this->export
->compileMemoryToFileContent();
526 $fExt = ($this->export
->doOutputCompress() ?
'-z' : '') . '.t3d';
529 $dlFile = $inData['filename'];
531 $exportName = substr(preg_replace('/[^[:alnum:]_]/', '-', $inData['download_export_name']), 0, 20);
532 $dlFile = 'T3D_' . $exportName . '_' . date('Y-m-d_H-i') . $fExt;
535 // Export for download:
536 if ($inData['download_export']) {
537 $mimeType = 'application/octet-stream';
538 header('Content-Type: ' . $mimeType);
539 header('Content-Length: ' . strlen($out));
540 header('Content-Disposition: attachment; filename=' . PathUtility
::basename($dlFile));
545 if ($inData['save_export']) {
546 $saveFolder = $this->getDefaultImportExportFolder();
547 $lang = $this->getLanguageService();
548 if ($saveFolder !== false && $saveFolder->checkActionPermission('write')) {
549 $temporaryFileName = GeneralUtility
::tempnam('export');
550 file_put_contents($temporaryFileName, $out);
551 $file = $saveFolder->addFile($temporaryFileName, $dlFile, 'replace');
552 if ($saveFilesOutsideExportFile) {
553 $filesFolderName = $dlFile . '.files';
554 $filesFolder = $saveFolder->createFolder($filesFolderName);
555 $temporaryFolderForExport = ResourceFactory
::getInstance()->retrieveFileOrFolderObject($this->export
->getTemporaryFilesPathForExport());
556 $temporaryFilesForExport = $temporaryFolderForExport->getFiles();
557 foreach ($temporaryFilesForExport as $temporaryFileForExport) {
558 $filesFolder->getStorage()->moveFile($temporaryFileForExport, $filesFolder);
560 $temporaryFolderForExport->delete();
563 /** @var FlashMessage $flashMessage */
564 $flashMessage = GeneralUtility
::makeInstance(
566 sprintf($lang->getLL('exportdata_savedInSBytes'), $file->getPublicUrl(), GeneralUtility
::formatSize(strlen($out))),
567 $lang->getLL('exportdata_savedFile'),
571 /** @var FlashMessage $flashMessage */
572 $flashMessage = GeneralUtility
::makeInstance(
574 sprintf($lang->getLL('exportdata_badPathS'), $saveFolder->getPublicUrl()),
575 $lang->getLL('exportdata_problemsSavingFile'),
579 /** @var \TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService */
580 $flashMessageService = GeneralUtility
::makeInstance(FlashMessageService
::class);
581 /** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
582 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
583 $defaultFlashMessageQueue->enqueue($flashMessage);
587 $this->makeConfigurationForm($inData);
589 $this->makeSaveForm($inData);
591 $this->makeAdvancedOptionsForm($inData);
593 $this->standaloneView
->assign('errors', $this->export
->errorLog
);
595 // Generate overview:
596 $this->standaloneView
->assign(
598 $this->export
->displayContentOverview()
603 * Adds records to the export object for a specific page id.
605 * @param int $k Page id for which to select records to add
606 * @param array $tables Array of table names to select from
608 protected function addRecordsForPid($k, $tables)
610 if (!is_array($tables)) {
613 foreach ($GLOBALS['TCA'] as $table => $value) {
614 if ($table !== 'pages' && (in_array($table, $tables) ||
in_array('_ALL', $tables))) {
615 if ($this->getBackendUser()->check('tables_select', $table) && !$GLOBALS['TCA'][$table]['ctrl']['is_static']) {
616 $statement = $this->exec_listQueryPid($table, $k);
617 while ($subTrow = $statement->fetch()) {
618 $this->export
->export_addRecord($table, $subTrow);
626 * Selects records from table / pid
628 * @param string $table Table to select from
629 * @param int $pid Page ID to select from
630 * @return \Doctrine\DBAL\Driver\Statement Query statement
632 protected function exec_listQueryPid($table, $pid)
634 $queryBuilder = GeneralUtility
::makeInstance(ConnectionPool
::class)->getQueryBuilderForTable($table);
636 $orderBy = $GLOBALS['TCA'][$table]['ctrl']['sortby'] ?
: $GLOBALS['TCA'][$table]['ctrl']['default_sortby'];
637 $queryBuilder->getRestrictions()->add(GeneralUtility
::makeInstance(BackendWorkspaceRestriction
::class));
639 if ($this->excludeDisabledRecords
=== false) {
640 $queryBuilder->getRestrictions()
642 ->add(GeneralUtility
::makeInstance(DeletedRestriction
::class))
643 ->add(GeneralUtility
::makeInstance(BackendWorkspaceRestriction
::class));
646 $queryBuilder->select('*')
649 $queryBuilder->expr()->eq(
651 $queryBuilder->createNamedParameter($pid, \PDO
::PARAM_INT
)
655 foreach (QueryHelper
::parseOrderBy((string)$orderBy) as $orderPair) {
656 list($fieldName, $order) = $orderPair;
657 $queryBuilder->addOrderBy($fieldName, $order);
660 return $queryBuilder->execute();
664 * Create configuration form
666 * @param array $inData Form configuration data
668 protected function makeConfigurationForm($inData)
670 $nameSuggestion = '';
671 // Page tree export options:
672 if (isset($inData['pagetree']['id'])) {
673 $this->standaloneView
->assign('treeHTML', $this->treeHTML
);
676 '-2' => $this->lang
->getLL('makeconfig_tablesOnThisPage'),
677 '-1' => $this->lang
->getLL('makeconfig_expandedTree'),
678 '0' => $this->lang
->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
679 '1' => $this->lang
->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
680 '2' => $this->lang
->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
681 '3' => $this->lang
->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
682 '4' => $this->lang
->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
683 '999' => $this->lang
->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi'),
685 $this->standaloneView
->assign('levelSelectOptions', $opt);
686 $this->standaloneView
->assign('tableSelectOptions', $this->getTableSelectOptions('pages'));
687 $nameSuggestion .= 'tree_PID' . $inData['pagetree']['id'] . '_L' . $inData['pagetree']['levels'];
689 // Single record export:
690 if (is_array($inData['record'])) {
692 foreach ($inData['record'] as $ref) {
693 $rParts = explode(':', $ref);
696 $nameSuggestion .= $tName . '_' . $rUid;
697 $rec = BackendUtility
::getRecordWSOL($tName, $rUid);
700 'icon' => $this->iconFactory
->getIconForRecord($tName, $rec, Icon
::SIZE_SMALL
)->render(),
701 'title' => BackendUtility
::getRecordTitle($tName, $rec, true),
702 'tableName' => $tName,
707 $this->standaloneView
->assign('records', $records);
709 // Single tables/pids:
710 if (is_array($inData['list'])) {
712 // Display information about pages from which the export takes place
714 foreach ($inData['list'] as $reference) {
715 $referenceParts = explode(':', $reference);
716 $tableName = $referenceParts[0];
717 if ($this->getBackendUser()->check('tables_select', $tableName)) {
718 // If the page is actually the root, handle it differently
719 // NOTE: we don't compare integers, because the number actually comes from the split string above
720 if ($referenceParts[1] === '0') {
721 $iconAndTitle = $this->iconFactory
->getIcon('apps-pagetree-root', Icon
::SIZE_SMALL
)->render() . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
723 $record = BackendUtility
::getRecordWSOL('pages', $referenceParts[1]);
724 $iconAndTitle = $this->iconFactory
->getIconForRecord('pages', $record, Icon
::SIZE_SMALL
)->render()
725 . BackendUtility
::getRecordTitle('pages', $record, true);
729 'iconAndTitle' => sprintf($this->lang
->getLL('makeconfig_tableListEntry'), $tableName, $iconAndTitle),
730 'reference' => $reference
734 $this->standaloneView
->assign('tableList', $tableList);
737 $this->standaloneView
->assign('externalReferenceTableSelectOptions', $this->getTableSelectOptions());
738 $this->standaloneView
->assign('externalStaticTableSelectOptions', $this->getTableSelectOptions());
739 $this->standaloneView
->assign('nameSuggestion', $nameSuggestion);
743 * Create advanced options form
745 * @param array $inData Form configuration data
747 protected function makeAdvancedOptionsForm($inData)
749 $loadedExtensions = ExtensionManagementUtility
::getLoadedExtensionListArray();
750 $loadedExtensions = array_combine($loadedExtensions, $loadedExtensions);
751 $this->standaloneView
->assign('extensions', $loadedExtensions);
752 $this->standaloneView
->assign('inData', $inData);
756 * Create configuration form
758 * @param array $inData Form configuration data
760 protected function makeSaveForm($inData)
762 $opt = $this->presetRepository
->getPresets((int)$inData['pagetree']['id']);
764 $this->standaloneView
->assign('presetSelectOptions', $opt);
766 $saveFolder = $this->getDefaultImportExportFolder();
768 $this->standaloneView
->assign('saveFolder', $saveFolder->getCombinedIdentifier());
773 $opt['xml'] = $this->lang
->getLL('makesavefo_xml');
774 if ($this->export
->compress
) {
775 $opt['t3d_compressed'] = $this->lang
->getLL('makesavefo_t3dFileCompressed');
777 $opt['t3d'] = $this->lang
->getLL('makesavefo_t3dFile');
779 $this->standaloneView
->assign('filetypeSelectOptions', $opt);
783 $this->standaloneView
->assign('saveFolder', $saveFolder->getPublicUrl());
784 $this->standaloneView
->assign('hasSaveFolder', true);
786 $this->standaloneView
->assign('fileName', $fileName);
789 /**************************
791 **************************/
794 * Import part of module
796 * @param array $inData Content of POST VAR tx_impexp[]..
797 * @throws \BadFunctionCallException
798 * @throws \InvalidArgumentException
799 * @throws \RuntimeException
801 protected function importData($inData)
803 $access = is_array($this->pageinfo
);
804 $beUser = $this->getBackendUser();
805 if ($this->id
&& $access ||
$beUser->isAdmin() && !$this->id
) {
806 if ($beUser->isAdmin() && !$this->id
) {
807 $this->pageinfo
= ['title' => '[root-level]', 'uid' => 0, 'pid' => 0];
809 if ($inData['new_import']) {
810 unset($inData['import_mode']);
812 /** @var Import $import */
813 $import = GeneralUtility
::makeInstance(Import
::class);
815 $import->update
= $inData['do_update'];
816 $import->import_mode
= $inData['import_mode'];
817 $import->enableLogging
= $inData['enableLogging'];
818 $import->global_ignore_pid
= $inData['global_ignore_pid'];
819 $import->force_all_UIDS
= $inData['force_all_UIDS'];
820 $import->showDiff
= !$inData['notShowDiff'];
821 $import->allowPHPScripts
= $inData['allowPHPScripts'];
822 $import->softrefInputValues
= $inData['softrefInputValues'];
826 // Make input selector:
827 // must have trailing slash.
828 $path = $this->getDefaultImportExportFolder();
829 $exportFiles = $this->getExportFiles();
831 $this->shortcutName
.= ' (' . $this->pageinfo
['title'] . ')';
834 $selectOptions = [''];
835 foreach ($exportFiles as $file) {
836 $selectOptions[$file->getCombinedIdentifier()] = $file->getPublicUrl();
839 $this->standaloneView
->assign('import', $import);
840 $this->standaloneView
->assign('inData', $inData);
841 $this->standaloneView
->assign('fileSelectOptions', $selectOptions);
844 $this->standaloneView
->assign('importPath', sprintf($this->lang
->getLL('importdata_fromPathS'), $path->getCombinedIdentifier()));
846 $this->standaloneView
->assign('importPath', $this->lang
->getLL('importdata_no_default_upload_folder'));
848 $this->standaloneView
->assign('isAdmin', $beUser->isAdmin());
851 $tempFolder = $this->getDefaultImportExportFolder();
853 $this->standaloneView
->assign('tempFolder', $tempFolder->getCombinedIdentifier());
854 $this->standaloneView
->assign('hasTempUploadFolder', true);
855 if (GeneralUtility
::_POST('_upload')) {
856 $this->standaloneView
->assign('submitted', GeneralUtility
::_POST('_upload'));
857 $this->standaloneView
->assign('noFileUploaded', $this->fileProcessor
->internalUploadMap
[1]);
858 if ($this->uploadedFiles
[0]) {
859 $this->standaloneView
->assign('uploadedFile', $this->uploadedFiles
[0]->getName());
864 // Perform import or preview depending:
865 $inFile = $this->getFile($inData['file']);
866 if ($inFile !== null && $inFile->exists()) {
867 $this->standaloneView
->assign('metaDataInFileExists', true);
868 $importInhibitedMessages = [];
869 if ($import->loadFile($inFile->getForLocalProcessing(false), 1)) {
870 $importInhibitedMessages = $import->checkImportPrerequisites();
871 if ($inData['import_file']) {
872 if (empty($importInhibitedMessages)) {
873 $import->importData($this->id
);
874 BackendUtility
::setUpdateSignal('updatePageTree');
877 $import->display_import_pid_record
= $this->pageinfo
;
878 $this->standaloneView
->assign('contentOverview', $import->displayContentOverview());
880 // Compile messages which are inhibiting a proper import and add them to output.
881 if (!empty($importInhibitedMessages)) {
882 $flashMessageQueue = GeneralUtility
::makeInstance(FlashMessageService
::class)->getMessageQueueByIdentifier('impexp.errors');
883 foreach ($importInhibitedMessages as $message) {
884 $flashMessageQueue->addMessage(GeneralUtility
::makeInstance(
894 $this->standaloneView
->assign('errors', $import->errorLog
);
898 /****************************
900 ****************************/
903 * Returns a \TYPO3\CMS\Core\Resource\Folder object for saving export files
904 * to the server and is also used for uploading import files.
906 * @throws \InvalidArgumentException
907 * @return \TYPO3\CMS\Core\Resource\Folder|null
909 protected function getDefaultImportExportFolder()
911 $defaultImportExportFolder = null;
913 $defaultTemporaryFolder = $this->getBackendUser()->getDefaultUploadTemporaryFolder();
914 if ($defaultTemporaryFolder !== null) {
915 $importExportFolderName = 'importexport';
916 $createFolder = !$defaultTemporaryFolder->hasFolder($importExportFolderName);
917 if ($createFolder === true) {
919 $defaultImportExportFolder = $defaultTemporaryFolder->createFolder($importExportFolderName);
920 } catch (Exception
$folderAccessException) {
923 $defaultImportExportFolder = $defaultTemporaryFolder->getSubfolder($importExportFolderName);
927 return $defaultImportExportFolder;
931 * Check if a file has been uploaded
933 * @throws \InvalidArgumentException
934 * @throws \UnexpectedValueException
936 protected function checkUpload()
938 $file = GeneralUtility
::_GP('file');
940 $this->fileProcessor
= GeneralUtility
::makeInstance(ExtendedFileUtility
::class);
941 $this->fileProcessor
->setActionPermissions();
942 $conflictMode = empty(GeneralUtility
::_GP('overwriteExistingFiles')) ? DuplicationBehavior
::__default
: DuplicationBehavior
::REPLACE
;
943 $this->fileProcessor
->setExistingFilesConflictMode(DuplicationBehavior
::cast($conflictMode));
944 $this->fileProcessor
->start($file);
945 $result = $this->fileProcessor
->processData();
946 if (!empty($result['upload'])) {
947 foreach ($result['upload'] as $uploadedFiles) {
948 $this->uploadedFiles +
= $uploadedFiles;
954 * Returns option array to be used in Fluid
956 * @param string $excludeList Table names (and the string "_ALL") to exclude. Comma list
959 protected function getTableSelectOptions($excludeList = '')
962 if (!GeneralUtility
::inList($excludeList, '_ALL')) {
963 $optValues['_ALL'] = '[' . $this->lang
->getLL('ALL_tables') . ']';
965 foreach ($GLOBALS['TCA'] as $table => $_) {
966 if ($this->getBackendUser()->check('tables_select', $table) && !GeneralUtility
::inList($excludeList, $table)) {
967 $optValues[$table] = $table;
974 * Filter page IDs by traversing exclude array, finding all
975 * excluded pages (if any) and making an AND NOT IN statement for the select clause.
977 * @param array $exclude Exclude array from import/export object.
978 * @return string AND where clause part to filter out page uids.
980 protected function filterPageIds($exclude)
983 $exclude = array_keys($exclude);
986 foreach ($exclude as $element) {
987 list($table, $uid) = explode(':', $element);
988 if ($table === 'pages') {
989 $pageIds[] = (int)$uid;
993 if (!empty($pageIds)) {
994 return ' AND uid NOT IN (' . implode(',', $pageIds) . ')';
1000 * @return BackendUserAuthentication
1002 protected function getBackendUser(): BackendUserAuthentication
1004 return $GLOBALS['BE_USER'];
1008 * @return LanguageService
1010 protected function getLanguageService(): LanguageService
1012 return $GLOBALS['LANG'];
1016 * Gets all export files.
1018 * @throws \InvalidArgumentException
1019 * @return array|\TYPO3\CMS\Core\Resource\File[]
1021 protected function getExportFiles()
1025 $folder = $this->getDefaultImportExportFolder();
1026 if ($folder !== null) {
1028 /** @var FileExtensionFilter $filter */
1029 $filter = GeneralUtility
::makeInstance(FileExtensionFilter
::class);
1030 $filter->setAllowedFileExtensions(['t3d', 'xml']);
1031 $folder->getStorage()->addFileAndFolderNameFilter([$filter, 'filterFileList']);
1033 $exportFiles = $folder->getFiles();
1036 return $exportFiles;
1040 * Gets a file by combined identifier.
1042 * @param string $combinedIdentifier
1043 * @return \TYPO3\CMS\Core\Resource\File|null
1045 protected function getFile($combinedIdentifier)
1048 $file = ResourceFactory
::getInstance()->getFileObjectFromCombinedIdentifier($combinedIdentifier);
1049 } catch (\Exception
$exception) {