‪TYPO3CMS  11.5
ExportController.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Http\Message\ResponseFactoryInterface;
21 use Psr\Http\Message\ResponseInterface;
22 use Psr\Http\Message\ServerRequestInterface;
25 use TYPO3\CMS\Backend\Utility\BackendUtility;
32 use TYPO3\CMS\Core\Page\PageRenderer;
41 
48 {
52  protected ‪$routeName = 'tx_impexp_export';
53 
57  protected ‪$export;
58 
62  protected ‪$presetRepository;
63 
64  protected ResponseFactoryInterface ‪$responseFactory;
65 
66  public function ‪__construct(
68  PageRenderer ‪$pageRenderer,
71  ResponseFactoryInterface ‪$responseFactory
72  ) {
74 
75  $this->presetRepository = GeneralUtility::makeInstance(PresetRepository::class);
76  $this->responseFactory = ‪$responseFactory;
77  }
78 
97  public function ‪mainAction(ServerRequestInterface $request): ResponseInterface
98  {
99  if ($this->‪getBackendUser()->isExportEnabled() === false) {
100  throw new \RuntimeException(
101  'Export module is disabled for non admin users and '
102  . 'userTsConfig options.impexp.enableExportForNonAdminUser is not enabled.',
103  1636901978
104  );
105  }
106 
107  $this->‪main($request);
108 
109  // Input data
110  $presetAction = $request->getParsedBody()['preset'] ?? [];
111  $inData = $request->getParsedBody()['tx_impexp'] ?? $request->getQueryParams()['tx_impexp'] ?? [];
112  $inData = $this->‪preprocessInputData($inData);
113 
114  // Perform export
115  $inData = $this->‪processPresets($presetAction, $inData);
116  $inData = $this->‪exportData($inData);
117 
118  // Prepare view
120  $this->‪makeConfigurationForm($inData);
121  $this->‪makeSaveForm($inData);
123  $this->standaloneView->assign('inData', $inData);
124  $this->standaloneView->setTemplate('Export.html');
125  $this->moduleTemplate->setContent($this->standaloneView->render());
126 
127  return new HtmlResponse($this->moduleTemplate->renderContent());
128  }
129 
134  public function ‪preprocessInputData(array $inData): array
135  {
136  // Flag doesn't exist initially; state is on by default
137  if (!array_key_exists('excludeDisabled', $inData)) {
138  $inData['excludeDisabled'] = 1;
139  }
140  if ($inData['resetExclude'] ?? false) {
141  $inData['exclude'] = [];
142  }
143  $inData['preset']['public'] = (int)($inData['preset']['public'] ?? 0);
144  return $inData;
145  }
146 
154  public function ‪processPresets(array $presetAction, array $inData): array
155  {
156  if (empty($presetAction)) {
157  return $inData;
158  }
159 
160  $presetUid = (int)$presetAction['select'];
161 
162  try {
163  $info = null;
164 
165  // Save preset
166  if (isset($presetAction['save'])) {
167  // Update existing
168  if ($presetUid > 0) {
169  $this->presetRepository->updatePreset($presetUid, $inData);
170  $info = 'Preset #' . $presetUid . ' saved!';
171  }
172  // Insert new
173  else {
174  $this->presetRepository->createPreset($inData);
175  $info = 'New preset "' . htmlspecialchars($inData['preset']['title']) . '" is created';
176  }
177  }
178 
179  // Delete preset
180  if (isset($presetAction['delete'])) {
181  if ($presetUid > 0) {
182  $this->presetRepository->deletePreset($presetUid);
183  $info = 'Preset #' . $presetUid . ' deleted!';
184  } else {
185  $error = 'ERROR: No preset selected for deletion.';
186  $this->moduleTemplate->addFlashMessage($error, 'Presets', ‪FlashMessage::ERROR);
187  }
188  }
189 
190  // Load preset data
191  if (isset($presetAction['load']) || isset($presetAction['merge'])) {
192  if ($presetUid > 0) {
193  $presetData = $this->presetRepository->loadPreset($presetUid);
194  if (isset($presetAction['merge'])) {
195  // Merge records in:
196  if (is_array($presetData['record'] ?? null)) {
197  $inData['record'] = array_merge((array)$inData['record'], $presetData['record']);
198  }
199  // Merge lists in:
200  if (is_array($presetData['list'] ?? null)) {
201  $inData['list'] = array_merge((array)$inData['list'], $presetData['list']);
202  }
203  $info = 'Preset #' . $presetUid . ' merged!';
204  } else {
205  $inData = $presetData;
206  $info = 'Preset #' . $presetUid . ' loaded!';
207  }
208  } else {
209  $error = 'ERROR: No preset selected for loading.';
210  $this->moduleTemplate->addFlashMessage($error, 'Presets', ‪FlashMessage::ERROR);
211  }
212  }
213 
214  if ($info !== null) {
215  $this->moduleTemplate->addFlashMessage($info, 'Presets', ‪FlashMessage::INFO);
216  }
217  } catch (Exception $e) {
218  $this->moduleTemplate->addFlashMessage($e->getMessage(), 'Presets', ‪FlashMessage::ERROR);
219  }
220  return $inData;
221  }
222 
230  protected function ‪exportData(array $inData): array
231  {
232  // Create export object and configure it:
233  $this->export = GeneralUtility::makeInstance(Export::class);
234  $this->export->setExcludeMap((array)($inData['exclude'] ?? []));
235  $this->export->setSoftrefCfg((array)($inData['softrefCfg'] ?? []));
236  $this->export->setExtensionDependencies((($inData['extension_dep'] ?? '') === '') ? [] : (array)$inData['extension_dep']);
237  $this->export->setShowStaticRelations((bool)($inData['showStaticRelations'] ?? false));
238  $this->export->setIncludeExtFileResources(!($inData['excludeHTMLfileResources'] ?? false));
239  $this->export->setExcludeDisabledRecords((bool)($inData['excludeDisabled'] ?? false));
240  if (!empty($inData['filetype'])) {
241  $this->export->setExportFileType((string)$inData['filetype']);
242  }
243  $this->export->setExportFileName((string)($inData['filename'] ?? ''));
244 
245  // Static tables:
246  if (is_array($inData['external_static']['tables'] ?? null)) {
247  $this->export->setRelStaticTables($inData['external_static']['tables']);
248  }
249  // Configure which tables external relations are included for:
250  if (is_array($inData['external_ref']['tables'] ?? null)) {
251  $this->export->setRelOnlyTables($inData['external_ref']['tables']);
252  }
253  if (isset($inData['save_export'], $inData['saveFilesOutsideExportFile']) && $inData['saveFilesOutsideExportFile'] === '1') {
254  $this->export->setSaveFilesOutsideExportFile(true);
255  }
256  if (is_array($inData['meta'] ?? null)) {
257  if (isset($inData['meta']['title'])) {
258  $this->export->setTitle($inData['meta']['title']);
259  }
260  if (isset($inData['meta']['description'])) {
261  $this->export->setDescription($inData['meta']['description']);
262  }
263  if (isset($inData['meta']['notes'])) {
264  $this->export->setNotes($inData['meta']['notes']);
265  }
266  }
267  if (is_array($inData['record'] ?? null)) {
268  $this->export->setRecord($inData['record']);
269  }
270  if (is_array($inData['list'] ?? null)) {
271  $this->export->setList($inData['list']);
272  }
273  if (‪MathUtility::canBeInterpretedAsInteger($inData['pagetree']['id'] ?? null)) {
274  $this->export->setPid((int)$inData['pagetree']['id']);
275  }
276  if (‪MathUtility::canBeInterpretedAsInteger($inData['pagetree']['levels'] ?? null)) {
277  $this->export->setLevels((int)$inData['pagetree']['levels']);
278  }
279  if (is_array($inData['pagetree']['tables'] ?? null)) {
280  $this->export->setTables($inData['pagetree']['tables']);
281  }
282 
283  $this->export->process();
284 
285  $inData['filename'] = $this->export->getExportFileName();
286 
287  // Perform export:
288  if (($inData['download_export'] ?? null) || ($inData['save_export'] ?? null)) {
289  // Export by download:
290  if ($inData['download_export'] ?? null) {
291  $fileName = $this->export->getOrGenerateExportFileNameWithFileExtension();
292  $fileContent = $this->export->render();
293  $response = $this->responseFactory->createResponse()
294  ->withHeader('Content-Type', 'application/octet-stream')
295  ->withHeader('Content-Length', (string)strlen($fileContent))
296  ->withHeader('Content-Disposition', 'attachment; filename=' . ‪PathUtility::basename($fileName));
297  $response->getBody()->write($fileContent);
298  // @todo: Refactor to *return* the response instead of throwing PropagateResponseException
299  throw new PropagateResponseException($response, 1629196918);
300  }
301 
302  // Export by saving on server:
303  if ($inData['save_export'] ?? null) {
304  try {
305  $saveFile = $this->export->saveToFile();
306  $saveFileSize = $saveFile->getProperty('size');
307  $this->moduleTemplate->addFlashMessage(
308  sprintf($this->lang->getLL('exportdata_savedInSBytes'), $saveFile->getPublicUrl(), GeneralUtility::formatSize($saveFileSize)),
309  $this->lang->getLL('exportdata_savedFile')
310  );
311  } catch (‪CoreException $e) {
312  $saveFolder = $this->export->getOrCreateDefaultImportExportFolder();
313  $this->moduleTemplate->addFlashMessage(
314  sprintf($this->lang->getLL('exportdata_badPathS'), $saveFolder->getPublicUrl()),
315  $this->lang->getLL('exportdata_problemsSavingFile'),
317  );
318  }
319  }
320  }
321 
322  $this->standaloneView->assign('errors', $this->export->getErrorLog());
323  $this->standaloneView->assign('preview', $this->export->renderPreview());
324  return $inData;
325  }
326 
332  protected function ‪makeConfigurationForm(array $inData): void
333  {
334  // Page tree export:
335  if (‪MathUtility::canBeInterpretedAsInteger($inData['pagetree']['id'] ?? '')) {
336  $options = [
337  ‪Export::LEVELS_RECORDS_ON_THIS_PAGE => $this->lang->getLL('makeconfig_tablesOnThisPage'),
338  ‪Export::LEVELS_EXPANDED_TREE => $this->lang->getLL('makeconfig_expandedTree'),
339  0 => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
340  1 => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
341  2 => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
342  3 => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
343  4 => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
344  ‪Export::LEVELS_INFINITE => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi'),
345  ];
346  $this->standaloneView->assign('levelSelectOptions', $options);
347  $this->standaloneView->assign('tableSelectOptions', $this->‪getTableSelectOptions('pages'));
348  $this->standaloneView->assign('treeHTML', $this->export->getTreeHTML());
349  }
350 
351  // Single records export:
352  if (is_array($inData['record'] ?? null)) {
353  $records = [];
354  foreach ($inData['record'] as $ref) {
355  $rParts = explode(':', $ref);
356  [$tName, $rUid] = $rParts;
357  $rec = BackendUtility::getRecordWSOL((string)$tName, (int)$rUid);
358  if (!empty($rec)) {
359  $records[] = [
360  'icon' => $this->iconFactory->getIconForRecord($tName, $rec, ‪Icon::SIZE_SMALL)->render(),
361  'title' => BackendUtility::getRecordTitle($tName, $rec, true),
362  'tableName' => $tName,
363  'recordUid' => $rUid,
364  ];
365  }
366  }
367  $this->standaloneView->assign('records', $records);
368  }
369 
370  // Single tables export:
371  if (is_array($inData['list'] ?? null)) {
372  // Display information about pages from which the export takes place
373  $tableList = [];
374  foreach ($inData['list'] as $reference) {
375  $referenceParts = explode(':', $reference);
376  $tableName = $referenceParts[0];
377  if ($this->‪getBackendUser()->check('tables_select', $tableName)) {
378  // If the page is actually the root, handle it differently
379  // NOTE: we don't compare integers, because the number actually comes from the split string above
380  if ($referenceParts[1] === '0') {
381  $iconAndTitle = $this->iconFactory->getIcon('apps-pagetree-root', ‪Icon::SIZE_SMALL)->render() . ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
382  } else {
383  $record = BackendUtility::getRecordWSOL('pages', (int)$referenceParts[1]);
384  $iconAndTitle = $this->iconFactory->getIconForRecord('pages', $record, ‪Icon::SIZE_SMALL)->render()
385  . BackendUtility::getRecordTitle('pages', $record, true);
386  }
387 
388  $tableList[] = [
389  'iconAndTitle' => sprintf($this->lang->getLL('makeconfig_tableListEntry'), $tableName, $iconAndTitle),
390  'reference' => $reference,
391  ];
392  }
393  }
394  $this->standaloneView->assign('tableList', $tableList);
395  }
396 
397  $this->standaloneView->assign('externalReferenceTableSelectOptions', $this->‪getTableSelectOptions());
398  $this->standaloneView->assign('externalStaticTableSelectOptions', $this->‪getTableSelectOptions());
399  }
400 
404  protected function ‪makeAdvancedOptionsForm(): void
405  {
407  $loadedExtensions = array_combine($loadedExtensions, $loadedExtensions);
408  $this->standaloneView->assign('extensions', $loadedExtensions);
409  }
410 
416  protected function ‪makeSaveForm(array $inData): void
417  {
418  $presetOptions = $this->presetRepository->getPresets((int)($inData['pagetree']['id'] ?? 0));
419 
420  $fileTypeOptions = [];
421  foreach ($this->export->getSupportedFileTypes() as $supportedFileType) {
422  $fileTypeOptions[$supportedFileType] = $this->lang->getLL('makesavefo_' . $supportedFileType);
423  }
424 
425  $saveFolder = $this->export->getOrCreateDefaultImportExportFolder();
426  if ($saveFolder) {
427  $this->standaloneView->assign('saveFolder', $saveFolder->getPublicUrl());
428  $this->standaloneView->assign('hasSaveFolder', true);
429  }
430 
431  $this->standaloneView->assign('fileName', '');
432  $this->standaloneView->assign('presetSelectOptions', $presetOptions);
433  $this->standaloneView->assign('filetypeSelectOptions', $fileTypeOptions);
434  }
435 
442  protected function ‪getTableSelectOptions(string $excludeList = ''): array
443  {
444  $options = [];
445  if (!GeneralUtility::inList($excludeList, '_ALL')) {
446  $options['_ALL'] = '[' . $this->lang->getLL('ALL_tables') . ']';
447  }
448  foreach (‪$GLOBALS['TCA'] as $table => $_) {
449  if (!GeneralUtility::inList($excludeList, $table) && $this->‪getBackendUser()->check('tables_select', $table)) {
450  $options[$table] = $table;
451  }
452  }
453  natsort($options);
454  return $options;
455  }
456 }
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Impexp\Controller\ExportController\$responseFactory
‪ResponseFactoryInterface $responseFactory
Definition: ExportController.php:61
‪TYPO3\CMS\Impexp\Controller\ImportExportController\main
‪main(ServerRequestInterface $request)
Definition: ImportExportController.php:127
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:25
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Impexp\Controller\ImportExportController\$uriBuilder
‪UriBuilder $uriBuilder
Definition: ImportExportController.php:86
‪TYPO3\CMS\Impexp\Controller
Definition: ExportController.php:18
‪TYPO3\CMS\Impexp\Controller\ExportController\exportData
‪array exportData(array $inData)
Definition: ExportController.php:227
‪TYPO3\CMS\Impexp\Controller\ExportController\makeConfigurationForm
‪makeConfigurationForm(array $inData)
Definition: ExportController.php:329
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Impexp\Exception
Definition: ImportFailedException.php:18
‪TYPO3\CMS\Impexp\Controller\ImportExportController\$moduleTemplateFactory
‪ModuleTemplateFactory $moduleTemplateFactory
Definition: ImportExportController.php:87
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:29
‪TYPO3\CMS\Impexp\Controller\ExportController\__construct
‪__construct(IconFactory $iconFactory, PageRenderer $pageRenderer, UriBuilder $uriBuilder, ModuleTemplateFactory $moduleTemplateFactory, ResponseFactoryInterface $responseFactory)
Definition: ExportController.php:63
‪TYPO3\CMS\Impexp\Domain\Repository\PresetRepository
Definition: PresetRepository.php:36
‪TYPO3\CMS\Core\Exception
‪TYPO3\CMS\Impexp\Controller\ExportController\preprocessInputData
‪array preprocessInputData(array $inData)
Definition: ExportController.php:131
‪TYPO3\CMS\Impexp\Controller\ExportController\mainAction
‪ResponseInterface mainAction(ServerRequestInterface $request)
Definition: ExportController.php:94
‪TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException
Definition: ExistingTargetFileNameException.php:23
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Impexp\Controller\ExportController\$presetRepository
‪PresetRepository $presetRepository
Definition: ExportController.php:59
‪TYPO3\CMS\Impexp\Export\LEVELS_INFINITE
‪const LEVELS_INFINITE
Definition: Export.php:51
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static string basename($path)
Definition: PathUtility.php:226
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:43
‪TYPO3\CMS\Impexp\Controller\ImportExportController
Definition: ImportExportController.php:43
‪TYPO3\CMS\Impexp\Controller\ExportController
Definition: ExportController.php:48
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Impexp\Export\LEVELS_EXPANDED_TREE
‪const LEVELS_EXPANDED_TREE
Definition: Export.php:50
‪TYPO3\CMS\Impexp\Controller\ImportExportController\registerDocHeaderButtons
‪registerDocHeaderButtons()
Definition: ImportExportController.php:161
‪TYPO3\CMS\Impexp\Export\LEVELS_RECORDS_ON_THIS_PAGE
‪const LEVELS_RECORDS_ON_THIS_PAGE
Definition: Export.php:49
‪TYPO3\CMS\Core\Http\PropagateResponseException
Definition: PropagateResponseException.php:47
‪TYPO3\CMS\Impexp\Controller\ExportController\$routeName
‪string $routeName
Definition: ExportController.php:51
‪TYPO3\CMS\Core\Messaging\AbstractMessage\INFO
‪const INFO
Definition: AbstractMessage.php:28
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:26
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Impexp\Export
Definition: Export.php:48
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\getLoadedExtensionListArray
‪static array getLoadedExtensionListArray()
Definition: ExtensionManagementUtility.php:1830
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Impexp\Controller\ImportExportController\getBackendUser
‪BackendUserAuthentication getBackendUser()
Definition: ImportExportController.php:177
‪TYPO3\CMS\Impexp\Controller\ExportController\makeAdvancedOptionsForm
‪makeAdvancedOptionsForm()
Definition: ExportController.php:401
‪TYPO3\CMS\Impexp\Controller\ExportController\processPresets
‪array processPresets(array $presetAction, array $inData)
Definition: ExportController.php:151
‪TYPO3\CMS\Impexp\Controller\ExportController\$export
‪Export $export
Definition: ExportController.php:55
‪TYPO3\CMS\Core\Messaging\AbstractMessage\ERROR
‪const ERROR
Definition: AbstractMessage.php:31
‪TYPO3\CMS\Impexp\Controller\ExportController\getTableSelectOptions
‪array getTableSelectOptions(string $excludeList='')
Definition: ExportController.php:439
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:26
‪TYPO3\CMS\Impexp\Controller\ImportExportController\$pageRenderer
‪PageRenderer $pageRenderer
Definition: ImportExportController.php:85
‪TYPO3\CMS\Impexp\Controller\ExportController\makeSaveForm
‪makeSaveForm(array $inData)
Definition: ExportController.php:413
‪TYPO3\CMS\Impexp\Controller\ImportExportController\$iconFactory
‪IconFactory $iconFactory
Definition: ImportExportController.php:84