a87f744306de12c9effd4499bbe34b94414adbc4
[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 Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Template\DocumentTemplate;
20 use TYPO3\CMS\Backend\Template\ModuleTemplate;
21 use TYPO3\CMS\Backend\Tree\View\PageTreeView;
22 use TYPO3\CMS\Backend\Utility\BackendUtility;
23 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
24 use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
25 use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
26 use TYPO3\CMS\Core\Database\ConnectionPool;
27 use TYPO3\CMS\Core\Database\Query\QueryHelper;
28 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
29 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
30 use TYPO3\CMS\Core\Http\HtmlResponse;
31 use TYPO3\CMS\Core\Imaging\Icon;
32 use TYPO3\CMS\Core\Imaging\IconFactory;
33 use TYPO3\CMS\Core\Localization\LanguageService;
34 use TYPO3\CMS\Core\Messaging\FlashMessage;
35 use TYPO3\CMS\Core\Messaging\FlashMessageService;
36 use TYPO3\CMS\Core\Resource\DuplicationBehavior;
37 use TYPO3\CMS\Core\Resource\Exception;
38 use TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter;
39 use TYPO3\CMS\Core\Resource\ResourceFactory;
40 use TYPO3\CMS\Core\Type\Bitmask\Permission;
41 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
42 use TYPO3\CMS\Core\Utility\File\ExtendedFileUtility;
43 use TYPO3\CMS\Core\Utility\GeneralUtility;
44 use TYPO3\CMS\Core\Utility\MathUtility;
45 use TYPO3\CMS\Core\Utility\PathUtility;
46 use TYPO3\CMS\Fluid\View\StandaloneView;
47 use TYPO3\CMS\Impexp\Domain\Repository\PresetRepository;
48 use TYPO3\CMS\Impexp\Export;
49 use TYPO3\CMS\Impexp\Import;
50 use TYPO3\CMS\Impexp\View\ExportPageTreeView;
51
52 /**
53 * Main script class for the Import / Export facility
54 * @internal this is a TYPO3 Backend controller implementation and not part of TYPO3's Core API.
55 */
56 class ImportExportController
57 {
58 use PublicPropertyDeprecationTrait;
59 use PublicMethodDeprecationTrait;
60
61 /**
62 * @var array
63 */
64 private $deprecatedPublicProperties = [
65 'pageinfo' => 'Using ImportExportController::$pageinfo is deprecated and will not be possible anymore in TYPO3 v10.0.',
66 'id' => 'Using ImportExportController::$id is deprecated and will not be possible anymore in TYPO3 v10.0.',
67 'perms_clause' => 'Using ImportExportController::$perms_clause is deprecated and will not be possible anymore in TYPO3 v10.0.',
68 'extObj' => 'Using ImportExportController::$extObj is deprecated, the property will be removed in TYPO3 v10.0.',
69 'doc' => 'Using ImportExportController::$doc is deprecated, the property will be removed in TYPO3 v10.0.',
70 'content' => 'Using ImportExportController::$content is deprecated, the property will be removed in TYPO3 v10.0.',
71 'extClassConf' => 'Using ImportExportController::$extClassConf is deprecated, the property will be removed in TYPO3 v10.0.',
72 'modMenu_setDefaultList' => 'Using ImportExportController::$modMenu_setDefaultList is deprecated, the property will be removed in TYPO3 v10.0.',
73 'modMenu_dontValidateList' => 'Using ImportExportController::$modMenu_dontValidateList is deprecated, the property will be removed in TYPO3 v10.0.',
74 'modMenu_type' => 'Using ImportExportController::$modMenu_type is deprecated, the property will be removed in TYPO3 v10.0.',
75 'modTSconfig' => 'Using ImportExportController::$modTSconfig is deprecated, the property will be removed in TYPO3 v10.0.',
76 'MOD_SETTINGS' => 'Using ImportExportController::$MOD_SETTINGS is deprecated, the property will be removed in TYPO3 v10.0.',
77 'MOD_MENU' => 'Using ImportExportController::MOD_MENU is deprecated, the property will be removed in TYPO3 v10.0.',
78 'CMD' => 'Using ImportExportController::$CMD is deprecated, the property will be removed in TYPO3 v10.0.',
79 'MCONF' => 'Using ImportExportController::$MCONF is deprecated, the property will be removed in TYPO3 v10.0.',
80 ];
81
82 /**
83 * @var array
84 */
85 private $deprecatedPublicMethods = [
86 'init' => 'Using ImportExportController::init() is deprecated and will not be possible anymore in TYPO3 v10.0.',
87 'main' => 'Using ImportExportController::main() is deprecated and will not be possible anymore in TYPO3 v10.0.',
88 'exportData' => 'Using ImportExportController::exportData() is deprecated and will not be possible anymore in TYPO3 v10.0.',
89 'addRecordsForPid' => 'Using ImportExportController::addRecordsForPid() is deprecated and will not be possible anymore in TYPO3 v10.0.',
90 'exec_listQueryPid' => 'Using ImportExportController::exec_listQueryPid() is deprecated and will not be possible anymore in TYPO3 v10.0.',
91 'makeConfigurationForm' => 'Using ImportExportController::makeConfigurationForm() is deprecated and will not be possible anymore in TYPO3 v10.0.',
92 'makeAdvancedOptionsForm' => 'Using ImportExportController::makeAdvancedOptionsForm() is deprecated and will not be possible anymore in TYPO3 v10.0.',
93 'makeSaveForm' => 'Using ImportExportController::makeSaveForm() is deprecated and will not be possible anymore in TYPO3 v10.0.',
94 'importData' => 'Using ImportExportController::importData() is deprecated and will not be possible anymore in TYPO3 v10.0.',
95 'checkUpload' => 'Using ImportExportController::checkUpload() is deprecated and will not be possible anymore in TYPO3 v10.0.',
96 'getTableSelectOptions' => 'Using ImportExportController::getTableSelectOptions() is deprecated and will not be possible anymore in TYPO3 v10.0.',
97 'filterPageIds' => 'Using ImportExportController::filterPageIds() is deprecated and will not be possible anymore in TYPO3 v10.0.',
98 'getExtObjContent' => 'Using ImportExportController::getExtObjContent() is deprecated, the method will be removed in TYPO3 v10.0.',
99 'extObjContent' => 'Using ImportExportController::extObjContent() is deprecated, the method will be removed in TYPO3 v10.0.',
100 'extObjHeader' => 'Using ImportExportController::extObjHeader() is deprecated, the method will be removed in TYPO3 v10.0.',
101 'checkSubExtObj' => 'Using ImportExportController::checkSubExtObj() is deprecated, the method will be removed in TYPO3 v10.0.',
102 'checkExtObj' => 'Using ImportExportController::checkExtObj() is deprecated, the method will be removed in TYPO3 v10.0.',
103 'getExternalItemConfig' => 'Using ImportExportController::getExternalItemConfig() is deprecated, the method will be removed in TYPO3 v10.0.',
104 'handleExternalFunctionValue' => 'Using ImportExportController::handleExternalFunctionValue() is deprecated, the method will be removed in TYPO3 v10.0.',
105 'mergeExternalItems' => 'Using ImportExportController::mergeExternalItems() is deprecated, the method will be removed in TYPO3 v10.0.',
106 'menuConfig' => 'Using ImportExportController::menuConfig() is deprecated, the method will be removed in TYPO3 v10.0.',
107 ];
108
109 /**
110 * @var array|\TYPO3\CMS\Core\Resource\File[]
111 */
112 protected $uploadedFiles = [];
113
114 /**
115 * The integer value of the GET/POST var, 'id'. Used for submodules to the 'Web' module (page id)
116 *
117 * @var int
118 */
119 protected $id;
120
121 /**
122 * Array containing the current page.
123 *
124 * @var array
125 */
126 protected $pageinfo;
127
128 /**
129 * A WHERE clause for selection records from the pages table based on read-permissions of the current backend user.
130 *
131 * @var string
132 */
133 protected $perms_clause;
134
135 /**
136 * @var Export
137 */
138 protected $export;
139
140 /**
141 * @var Import
142 */
143 protected $import;
144
145 /**
146 * @var ExtendedFileUtility
147 */
148 protected $fileProcessor;
149
150 /**
151 * @var LanguageService
152 */
153 protected $lang;
154
155 /**
156 * @var string
157 */
158 protected $treeHTML = '';
159
160 /**
161 * @var IconFactory
162 */
163 protected $iconFactory;
164
165 /**
166 * The name of the module
167 *
168 * @var string
169 */
170 protected $moduleName = 'xMOD_tximpexp';
171
172 /**
173 * ModuleTemplate Container
174 *
175 * @var ModuleTemplate
176 */
177 protected $moduleTemplate;
178
179 /**
180 * The name of the shortcut for this page
181 *
182 * @var string
183 */
184 protected $shortcutName;
185
186 /**
187 * preset repository
188 *
189 * @var PresetRepository
190 */
191 protected $presetRepository;
192
193 /**
194 * @var StandaloneView
195 */
196 protected $standaloneView;
197
198 /**
199 * @var bool
200 */
201 protected $excludeDisabledRecords = false;
202
203 /**
204 * Return URL
205 *
206 * @var string
207 */
208 protected $returnUrl;
209
210 /**
211 * Loaded with the global array $MCONF which holds some module configuration from the conf.php file of backend modules.
212 *
213 * @see init()
214 * @var array
215 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
216 */
217 protected $MCONF = [];
218
219 /**
220 * The value of GET/POST var, 'CMD'
221 *
222 * @see init()
223 * @var mixed
224 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
225 */
226 protected $CMD;
227
228 /**
229 * The module menu items array. Each key represents a key for which values can range between the items in the array of that key.
230 *
231 * @see init()
232 * @var array
233 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
234 */
235 protected $MOD_MENU = [
236 'function' => []
237 ];
238
239 /**
240 * Current settings for the keys of the MOD_MENU array
241 *
242 * @see $MOD_MENU
243 * @var array
244 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
245 */
246 protected $MOD_SETTINGS = [];
247
248 /**
249 * Module TSconfig based on PAGE TSconfig / USER TSconfig
250 *
251 * @see menuConfig()
252 * @var array
253 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Module has no TSconfig options
254 */
255 protected $modTSconfig;
256
257 /**
258 * If type is 'ses' then the data is stored as session-lasting data. This means that it'll be wiped out the next time the user logs in.
259 * Can be set from extension classes of this class before the init() function is called.
260 *
261 * @see menuConfig(), \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData()
262 * @var string
263 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
264 */
265 protected $modMenu_type = '';
266
267 /**
268 * dontValidateList can be used to list variables that should not be checked if their value is found in the MOD_MENU array. Used for dynamically generated menus.
269 * Can be set from extension classes of this class before the init() function is called.
270 *
271 * @see menuConfig(), \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData()
272 * @var string
273 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
274 */
275 protected $modMenu_dontValidateList = '';
276
277 /**
278 * List of default values from $MOD_MENU to set in the output array (only if the values from MOD_MENU are not arrays)
279 * Can be set from extension classes of this class before the init() function is called.
280 *
281 * @see menuConfig(), \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData()
282 * @var string
283 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
284 */
285 protected $modMenu_setDefaultList = '';
286
287 /**
288 * Contains module configuration parts from TBE_MODULES_EXT if found
289 *
290 * @see handleExternalFunctionValue()
291 * @var array
292 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
293 */
294 protected $extClassConf;
295
296 /**
297 * Generally used for accumulating the output content of backend modules
298 *
299 * @var string
300 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
301 */
302 protected $content = '';
303
304 /**
305 * @var \TYPO3\CMS\Backend\Template\DocumentTemplate
306 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
307 */
308 protected $doc;
309
310 /**
311 * May contain an instance of a 'Function menu module' which connects to this backend module.
312 *
313 * @see checkExtObj()
314 * @var \object
315 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
316 */
317 protected $extObj;
318
319 /**
320 * Constructor
321 */
322 public function __construct()
323 {
324 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
325 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
326 $this->presetRepository = GeneralUtility::makeInstance(PresetRepository::class);
327
328 $templatePath = ExtensionManagementUtility::extPath('impexp') . 'Resources/Private/';
329
330 $this->standaloneView = GeneralUtility::makeInstance(StandaloneView::class);
331 $this->standaloneView->setTemplateRootPaths([$templatePath . 'Templates/ImportExport/']);
332 $this->standaloneView->setLayoutRootPaths([$templatePath . 'Layouts/']);
333 $this->standaloneView->setPartialRootPaths([$templatePath . 'Partials/']);
334 $this->standaloneView->getRequest()->setControllerExtensionName('impexp');
335 }
336
337 /**
338 * Initializes the module and defining necessary variables for this module to run.
339 */
340 protected function init()
341 {
342 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
343 $this->MCONF['name'] = $this->moduleName;
344 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
345 $this->CMD = GeneralUtility::_GP('CMD');
346
347 $this->id = (int)GeneralUtility::_GP('id');
348 $this->perms_clause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
349 $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
350 $this->lang = $this->getLanguageService();
351 }
352
353 /**
354 * Main module function
355 *
356 * @throws \BadFunctionCallException
357 * @throws \InvalidArgumentException
358 * @throws \RuntimeException
359 */
360 protected function main()
361 {
362 $this->lang->includeLLFile('EXT:impexp/Resources/Private/Language/locallang.xlf');
363
364 // Start document template object
365 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Instantiation will be removed.
366 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
367 $this->doc->bodyTagId = 'imp-exp-mod';
368
369 $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
370 if (is_array($this->pageinfo)) {
371 $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
372 }
373 // Setting up the context sensitive menu:
374 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
375 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Impexp/ImportExport');
376 $this->moduleTemplate->addJavaScriptCode(
377 'ImpexpInLineJS',
378 'if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';'
379 );
380
381 // Input data grabbed:
382 $inData = GeneralUtility::_GP('tx_impexp');
383 if ($inData === null) {
384 // This happens if the post request was larger than allowed on the server
385 // We set the import action as default and output a user information
386 $inData = [
387 'action' => 'import',
388 ];
389 $flashMessage = GeneralUtility::makeInstance(
390 FlashMessage::class,
391 $this->lang->getLL('importdata_upload_nodata'),
392 $this->lang->getLL('importdata_upload_error'),
393 FlashMessage::ERROR
394 );
395 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
396 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
397 $defaultFlashMessageQueue->enqueue($flashMessage);
398 }
399 if (!array_key_exists('excludeDisabled', $inData)) {
400 // flag doesn't exist initially; state is on by default
401 $inData['excludeDisabled'] = 1;
402 }
403 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
404 $this->standaloneView->assign('moduleUrl', (string)$uriBuilder->buildUriFromRoute('xMOD_tximpexp'));
405 $this->standaloneView->assign('id', $this->id);
406 $this->standaloneView->assign('inData', $inData);
407
408 switch ((string)$inData['action']) {
409 case 'export':
410 $this->shortcutName = $this->lang->getLL('title_export');
411 // Call export interface
412 $this->exportData($inData);
413 $this->standaloneView->setTemplate('Export.html');
414 break;
415 case 'import':
416 $backendUser = $this->getBackendUser();
417 $isEnabledForNonAdmin = (bool)($backendUser->getTSConfig()['options.']['impexp.']['enableImportForNonAdminUser'] ?? false);
418 if (!$backendUser->isAdmin() && $isEnabledForNonAdmin) {
419 throw new \RuntimeException(
420 'Import module is disabled for non admin users and '
421 . 'userTsConfig options.impexp.enableImportForNonAdminUser is not enabled.',
422 1464435459
423 );
424 }
425 $this->shortcutName = $this->lang->getLL('title_import');
426 if (GeneralUtility::_POST('_upload')) {
427 $this->checkUpload();
428 }
429 // Finally: If upload went well, set the new file as the import file:
430 if (!empty($this->uploadedFiles[0])) {
431 // Only allowed extensions....
432 $extension = $this->uploadedFiles[0]->getExtension();
433 if ($extension === 't3d' || $extension === 'xml') {
434 $inData['file'] = $this->uploadedFiles[0]->getCombinedIdentifier();
435 }
436 }
437 // Call import interface:
438 $this->importData($inData);
439 $this->standaloneView->setTemplate('Import.html');
440 break;
441 }
442
443 // Setting up the buttons and markers for docheader
444 $this->getButtons();
445 }
446
447 /**
448 * Injects the request object for the current request and gathers all data
449 *
450 * IMPORTING DATA:
451 *
452 * Incoming array has syntax:
453 * GETvar 'id' = import page id (must be readable)
454 *
455 * file = pointing to filename relative to public web path
456 *
457 * [all relation fields are clear, but not files]
458 * - page-tree is written first
459 * - then remaining pages (to the root of import)
460 * - 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)
461 * - then all internal relations are set and non-existing relations removed, relations to static tables preserved.
462 *
463 * EXPORTING DATA:
464 *
465 * Incoming array has syntax:
466 *
467 * file[] = file
468 * dir[] = dir
469 * list[] = table:pid
470 * record[] = table:uid
471 *
472 * pagetree[id] = (single id)
473 * pagetree[levels]=1,2,3, -1 = currently unpacked tree, -2 = only tables on page
474 * pagetree[tables][]=table/_ALL
475 *
476 * external_ref[tables][]=table/_ALL
477 *
478 * @param ServerRequestInterface $request the current request
479 * @return ResponseInterface the response with the content
480 */
481 public function mainAction(ServerRequestInterface $request): ResponseInterface
482 {
483 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Can be removed along with $this->doc.
484 $GLOBALS['SOBE'] = $this;
485
486 $this->init();
487 $this->main();
488 $this->moduleTemplate->setContent($this->standaloneView->render());
489 return new HtmlResponse($this->moduleTemplate->renderContent());
490 }
491
492 /**
493 * Create the panel of buttons for submitting the form or otherwise perform operations.
494 */
495 protected function getButtons()
496 {
497 $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
498 if ($this->getBackendUser()->mayMakeShortcut()) {
499 $shortcutButton = $buttonBar->makeShortcutButton()
500 ->setGetVariables(['tx_impexp'])
501 ->setDisplayName($this->shortcutName)
502 ->setModuleName($this->moduleName);
503 $buttonBar->addButton($shortcutButton);
504 }
505 // back button
506 if ($this->returnUrl) {
507 $backButton = $buttonBar->makeLinkButton()
508 ->setHref($this->returnUrl)
509 ->setTitle($this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
510 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL));
511 $buttonBar->addButton($backButton);
512 }
513 // Input data grabbed:
514 $inData = GeneralUtility::_GP('tx_impexp');
515 if ((string)$inData['action'] === 'import') {
516 if ($this->id && is_array($this->pageinfo) || $this->getBackendUser()->isAdmin() && !$this->id) {
517 if (is_array($this->pageinfo) && $this->pageinfo['uid']) {
518 // View
519 $onClick = BackendUtility::viewOnClick(
520 $this->pageinfo['uid'],
521 '',
522 BackendUtility::BEgetRootLine($this->pageinfo['uid'])
523 );
524 $viewButton = $buttonBar->makeLinkButton()
525 ->setTitle($this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
526 ->setHref('#')
527 ->setIcon($this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL))
528 ->setOnClick($onClick);
529 $buttonBar->addButton($viewButton);
530 }
531 }
532 }
533 }
534
535 /**************************
536 * EXPORT FUNCTIONS
537 **************************/
538
539 /**
540 * Export part of module
541 *
542 * @param array $inData Content of POST VAR tx_impexp[]..
543 * @throws \InvalidArgumentException
544 * @throws \RuntimeException
545 * @throws \TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException
546 */
547 protected function exportData($inData)
548 {
549 // BUILDING EXPORT DATA:
550 // Processing of InData array values:
551 $inData['filename'] = trim(preg_replace('/[^[:alnum:]._-]*/', '', preg_replace('/\\.(t3d|xml)$/', '', $inData['filename'])));
552 if (strlen($inData['filename'])) {
553 $inData['filename'] .= $inData['filetype'] === 'xml' ? '.xml' : '.t3d';
554 }
555 // Set exclude fields in export object:
556 if (!is_array($inData['exclude'])) {
557 $inData['exclude'] = [];
558 }
559 // Saving/Loading/Deleting presets:
560 $this->presetRepository->processPresets($inData);
561 // Create export object and configure it:
562 $this->export = GeneralUtility::makeInstance(Export::class);
563 $this->export->init(0);
564 $this->export->excludeMap = (array)$inData['exclude'];
565 $this->export->softrefCfg = (array)$inData['softrefCfg'];
566 $this->export->extensionDependencies = ($inData['extension_dep'] === '') ? [] : (array)$inData['extension_dep'];
567 $this->export->showStaticRelations = $inData['showStaticRelations'];
568 $this->export->includeExtFileResources = !$inData['excludeHTMLfileResources'];
569 $this->excludeDisabledRecords = (bool)$inData['excludeDisabled'];
570 $this->export->setExcludeDisabledRecords($this->excludeDisabledRecords);
571
572 // Static tables:
573 if (is_array($inData['external_static']['tables'])) {
574 $this->export->relStaticTables = $inData['external_static']['tables'];
575 }
576 // Configure which tables external relations are included for:
577 if (is_array($inData['external_ref']['tables'])) {
578 $this->export->relOnlyTables = $inData['external_ref']['tables'];
579 }
580 $saveFilesOutsideExportFile = false;
581 if (isset($inData['save_export']) && isset($inData['saveFilesOutsideExportFile']) && $inData['saveFilesOutsideExportFile'] === '1') {
582 $this->export->setSaveFilesOutsideExportFile(true);
583 $saveFilesOutsideExportFile = true;
584 }
585 $this->export->setHeaderBasics();
586 // Meta data setting:
587
588 $beUser = $this->getBackendUser();
589 $this->export->setMetaData(
590 $inData['meta']['title'],
591 $inData['meta']['description'],
592 $inData['meta']['notes'],
593 $beUser->user['username'],
594 $beUser->user['realName'],
595 $beUser->user['email']
596 );
597 // Configure which records to export
598 if (is_array($inData['record'])) {
599 foreach ($inData['record'] as $ref) {
600 $rParts = explode(':', $ref);
601 $this->export->export_addRecord($rParts[0], BackendUtility::getRecord($rParts[0], $rParts[1]));
602 }
603 }
604 // Configure which tables to export
605 if (is_array($inData['list'])) {
606 foreach ($inData['list'] as $ref) {
607 $rParts = explode(':', $ref);
608 if ($beUser->check('tables_select', $rParts[0])) {
609 $statement = $this->exec_listQueryPid($rParts[0], $rParts[1]);
610 while ($subTrow = $statement->fetch()) {
611 $this->export->export_addRecord($rParts[0], $subTrow);
612 }
613 }
614 }
615 }
616 // Pagetree
617 if (isset($inData['pagetree']['id'])) {
618 // Based on click-expandable tree
619 $idH = null;
620 if ($inData['pagetree']['levels'] == -1) {
621 $pagetree = GeneralUtility::makeInstance(ExportPageTreeView::class);
622 if ($this->excludeDisabledRecords) {
623 $pagetree->init(BackendUtility::BEenableFields('pages'));
624 }
625 $tree = $pagetree->ext_tree($inData['pagetree']['id'], $this->filterPageIds($this->export->excludeMap));
626 $this->treeHTML = $pagetree->printTree($tree);
627 $idH = $pagetree->buffer_idH;
628 } elseif ($inData['pagetree']['levels'] == -2) {
629 $this->addRecordsForPid($inData['pagetree']['id'], $inData['pagetree']['tables']);
630 } else {
631 // Based on depth
632 // Drawing tree:
633 // If the ID is zero, export root
634 if (!$inData['pagetree']['id'] && $beUser->isAdmin()) {
635 $sPage = [
636 'uid' => 0,
637 'title' => 'ROOT'
638 ];
639 } else {
640 $sPage = BackendUtility::getRecordWSOL('pages', $inData['pagetree']['id'], '*', ' AND ' . $this->perms_clause);
641 }
642 if (is_array($sPage)) {
643 $pid = $inData['pagetree']['id'];
644 $tree = GeneralUtility::makeInstance(PageTreeView::class);
645 $initClause = 'AND ' . $this->perms_clause . $this->filterPageIds($this->export->excludeMap);
646 if ($this->excludeDisabledRecords) {
647 $initClause .= BackendUtility::BEenableFields('pages');
648 }
649 $tree->init($initClause);
650 $HTML = $this->iconFactory->getIconForRecord('pages', $sPage, Icon::SIZE_SMALL)->render();
651 $tree->tree[] = ['row' => $sPage, 'HTML' => $HTML];
652 $tree->buffer_idH = [];
653 if ($inData['pagetree']['levels'] > 0) {
654 $tree->getTree($pid, $inData['pagetree']['levels'], '');
655 }
656 $idH = [];
657 $idH[$pid]['uid'] = $pid;
658 if (!empty($tree->buffer_idH)) {
659 $idH[$pid]['subrow'] = $tree->buffer_idH;
660 }
661 $pagetree = GeneralUtility::makeInstance(ExportPageTreeView::class);
662 $this->treeHTML = $pagetree->printTree($tree->tree);
663 $this->shortcutName .= ' (' . $sPage['title'] . ')';
664 }
665 }
666 // In any case we should have a multi-level array, $idH, with the page structure
667 // here (and the HTML-code loaded into memory for nice display...)
668 if (is_array($idH)) {
669 // Sets the pagetree and gets a 1-dim array in return with the pages (in correct submission order BTW...)
670 $flatList = $this->export->setPageTree($idH);
671 foreach ($flatList as $k => $value) {
672 $this->export->export_addRecord('pages', BackendUtility::getRecord('pages', $k));
673 $this->addRecordsForPid($k, $inData['pagetree']['tables']);
674 }
675 }
676 }
677 // After adding ALL records we set relations:
678 for ($a = 0; $a < 10; $a++) {
679 $addR = $this->export->export_addDBRelations($a);
680 if (empty($addR)) {
681 break;
682 }
683 }
684 // Finally files are added:
685 // MUST be after the DBrelations are set so that files from ALL added records are included!
686 $this->export->export_addFilesFromRelations();
687
688 $this->export->export_addFilesFromSysFilesRecords();
689
690 // If the download button is clicked, return file
691 if ($inData['download_export'] || $inData['save_export']) {
692 switch ((string)$inData['filetype']) {
693 case 'xml':
694 $out = $this->export->compileMemoryToFileContent('xml');
695 $fExt = '.xml';
696 break;
697 case 't3d':
698 $this->export->dontCompress = 1;
699 // intentional fall-through
700 // no break
701 default:
702 $out = $this->export->compileMemoryToFileContent();
703 $fExt = ($this->export->doOutputCompress() ? '-z' : '') . '.t3d';
704 }
705 // Filename:
706 $dlFile = $inData['filename'];
707 if (!$dlFile) {
708 $exportName = substr(preg_replace('/[^[:alnum:]_]/', '-', $inData['download_export_name']), 0, 20);
709 $dlFile = 'T3D_' . $exportName . '_' . date('Y-m-d_H-i') . $fExt;
710 }
711
712 // Export for download:
713 if ($inData['download_export']) {
714 $mimeType = 'application/octet-stream';
715 header('Content-Type: ' . $mimeType);
716 header('Content-Length: ' . strlen($out));
717 header('Content-Disposition: attachment; filename=' . PathUtility::basename($dlFile));
718 echo $out;
719 die;
720 }
721 // Export by saving:
722 if ($inData['save_export']) {
723 $saveFolder = $this->getDefaultImportExportFolder();
724 $lang = $this->getLanguageService();
725 if ($saveFolder !== false && $saveFolder->checkActionPermission('write')) {
726 $temporaryFileName = GeneralUtility::tempnam('export');
727 file_put_contents($temporaryFileName, $out);
728 $file = $saveFolder->addFile($temporaryFileName, $dlFile, 'replace');
729 if ($saveFilesOutsideExportFile) {
730 $filesFolderName = $dlFile . '.files';
731 $filesFolder = $saveFolder->createFolder($filesFolderName);
732 $temporaryFolderForExport = ResourceFactory::getInstance()->retrieveFileOrFolderObject($this->export->getTemporaryFilesPathForExport());
733 $temporaryFilesForExport = $temporaryFolderForExport->getFiles();
734 foreach ($temporaryFilesForExport as $temporaryFileForExport) {
735 $filesFolder->getStorage()->moveFile($temporaryFileForExport, $filesFolder);
736 }
737 $temporaryFolderForExport->delete();
738 }
739
740 /** @var FlashMessage $flashMessage */
741 $flashMessage = GeneralUtility::makeInstance(
742 FlashMessage::class,
743 sprintf($lang->getLL('exportdata_savedInSBytes'), $file->getPublicUrl(), GeneralUtility::formatSize(strlen($out))),
744 $lang->getLL('exportdata_savedFile'),
745 FlashMessage::OK
746 );
747 } else {
748 /** @var FlashMessage $flashMessage */
749 $flashMessage = GeneralUtility::makeInstance(
750 FlashMessage::class,
751 sprintf($lang->getLL('exportdata_badPathS'), $saveFolder->getPublicUrl()),
752 $lang->getLL('exportdata_problemsSavingFile'),
753 FlashMessage::ERROR
754 );
755 }
756 /** @var \TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService */
757 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
758 /** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
759 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
760 $defaultFlashMessageQueue->enqueue($flashMessage);
761 }
762 }
763
764 $this->makeConfigurationForm($inData);
765
766 $this->makeSaveForm($inData);
767
768 $this->makeAdvancedOptionsForm($inData);
769
770 $this->standaloneView->assign('errors', $this->export->errorLog);
771
772 // Generate overview:
773 $this->standaloneView->assign(
774 'contentOverview',
775 $this->export->displayContentOverview()
776 );
777 }
778
779 /**
780 * Adds records to the export object for a specific page id.
781 *
782 * @param int $k Page id for which to select records to add
783 * @param array $tables Array of table names to select from
784 * @param int $maxNumber @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
785 */
786 protected function addRecordsForPid($k, $tables, $maxNumber = null)
787 {
788 if (!is_array($tables)) {
789 return;
790 }
791 foreach ($GLOBALS['TCA'] as $table => $value) {
792 if ($table !== 'pages' && (in_array($table, $tables) || in_array('_ALL', $tables))) {
793 if ($this->getBackendUser()->check('tables_select', $table) && !$GLOBALS['TCA'][$table]['ctrl']['is_static']) {
794 if ($maxNumber !== null) {
795 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Remove this if in TYPO3 v10.0
796 // and the 3rd method argument. trigger_error() is called by method exec_listQueryPid() below
797 $statement = $this->exec_listQueryPid($table, $k, MathUtility::forceIntegerInRange($maxNumber, 1));
798 } else {
799 $statement = $this->exec_listQueryPid($table, $k);
800 }
801 while ($subTrow = $statement->fetch()) {
802 $this->export->export_addRecord($table, $subTrow);
803 }
804 }
805 }
806 }
807 }
808
809 /**
810 * Selects records from table / pid
811 *
812 * @param string $table Table to select from
813 * @param int $pid Page ID to select from
814 * @param int $limit @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
815 * @return \Doctrine\DBAL\Driver\Statement Query statement
816 */
817 protected function exec_listQueryPid($table, $pid, $limit = null)
818 {
819 // @deprecated In v10, remove this if and the method argument
820 if ($limit !== null) {
821 trigger_error(
822 'The third argument of addRecordsForPid() and exec_listQueryPid() has been'
823 . ' deprecated, do not limit exports anymore. The parameter will be removed in TYPO3 v10.0.',
824 E_USER_DEPRECATED
825 );
826 }
827
828 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
829
830 $orderBy = $GLOBALS['TCA'][$table]['ctrl']['sortby'] ?: $GLOBALS['TCA'][$table]['ctrl']['default_sortby'];
831 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
832
833 if ($this->excludeDisabledRecords === false) {
834 $queryBuilder->getRestrictions()
835 ->removeAll()
836 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
837 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
838 }
839
840 $queryBuilder->select('*')
841 ->from($table)
842 ->where(
843 $queryBuilder->expr()->eq(
844 'pid',
845 $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)
846 )
847 );
848
849 // @deprecated In v10, remove this if
850 if ($limit !== null) {
851 $queryBuilder->setMaxResults($limit);
852 }
853
854 foreach (QueryHelper::parseOrderBy((string)$orderBy) as $orderPair) {
855 list($fieldName, $order) = $orderPair;
856 $queryBuilder->addOrderBy($fieldName, $order);
857 }
858
859 $statement = $queryBuilder->execute();
860
861 // @deprecated In v10, remove this if, and the two getLL locallang target keys
862 if ($limit !== null && $statement->rowCount() == $limit) {
863 $limitWarning = sprintf($this->lang->getLL('makeconfig_anSqlQueryReturned'), $limit);
864 /** @var FlashMessage $flashMessage */
865 $flashMessage = GeneralUtility::makeInstance(
866 FlashMessage::class,
867 $this->lang->getLL('execlistqu_maxNumberLimit'),
868 $limitWarning,
869 FlashMessage::WARNING
870 );
871 /** @var \TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService */
872 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
873 /** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
874 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
875 $defaultFlashMessageQueue->enqueue($flashMessage);
876 }
877
878 return $statement;
879 }
880
881 /**
882 * Create configuration form
883 *
884 * @param array $inData Form configuration data
885 */
886 protected function makeConfigurationForm($inData)
887 {
888 $nameSuggestion = '';
889 // Page tree export options:
890 if (isset($inData['pagetree']['id'])) {
891 $this->standaloneView->assign('treeHTML', $this->treeHTML);
892
893 $opt = [
894 '-2' => $this->lang->getLL('makeconfig_tablesOnThisPage'),
895 '-1' => $this->lang->getLL('makeconfig_expandedTree'),
896 '0' => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
897 '1' => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
898 '2' => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
899 '3' => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
900 '4' => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
901 '999' => $this->lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi'),
902 ];
903 $this->standaloneView->assign('levelSelectOptions', $opt);
904 $this->standaloneView->assign('tableSelectOptions', $this->getTableSelectOptions('pages'));
905 $nameSuggestion .= 'tree_PID' . $inData['pagetree']['id'] . '_L' . $inData['pagetree']['levels'];
906 }
907 // Single record export:
908 if (is_array($inData['record'])) {
909 $records = [];
910 foreach ($inData['record'] as $ref) {
911 $rParts = explode(':', $ref);
912 $tName = $rParts[0];
913 $rUid = $rParts[1];
914 $nameSuggestion .= $tName . '_' . $rUid;
915 $rec = BackendUtility::getRecordWSOL($tName, $rUid);
916 if (!empty($rec)) {
917 $records[] = [
918 'icon' => $this->iconFactory->getIconForRecord($tName, $rec, Icon::SIZE_SMALL)->render(),
919 'title' => BackendUtility::getRecordTitle($tName, $rec, true),
920 'tableName' => $tName,
921 'recordUid' => $rUid
922 ];
923 }
924 }
925 $this->standaloneView->assign('records', $records);
926 }
927 // Single tables/pids:
928 if (is_array($inData['list'])) {
929
930 // Display information about pages from which the export takes place
931 $tableList = [];
932 foreach ($inData['list'] as $reference) {
933 $referenceParts = explode(':', $reference);
934 $tableName = $referenceParts[0];
935 if ($this->getBackendUser()->check('tables_select', $tableName)) {
936 // If the page is actually the root, handle it differently
937 // NOTE: we don't compare integers, because the number actually comes from the split string above
938 if ($referenceParts[1] === '0') {
939 $iconAndTitle = $this->iconFactory->getIcon('apps-pagetree-root', Icon::SIZE_SMALL)->render() . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
940 } else {
941 $record = BackendUtility::getRecordWSOL('pages', $referenceParts[1]);
942 $iconAndTitle = $this->iconFactory->getIconForRecord('pages', $record, Icon::SIZE_SMALL)->render()
943 . BackendUtility::getRecordTitle('pages', $record, true);
944 }
945
946 $tableList[] = [
947 'iconAndTitle' => sprintf($this->lang->getLL('makeconfig_tableListEntry'), $tableName, $iconAndTitle),
948 'reference' => $reference
949 ];
950 }
951 }
952 $this->standaloneView->assign('tableList', $tableList);
953 }
954
955 $this->standaloneView->assign('externalReferenceTableSelectOptions', $this->getTableSelectOptions());
956 $this->standaloneView->assign('externalStaticTableSelectOptions', $this->getTableSelectOptions());
957 $this->standaloneView->assign('nameSuggestion', $nameSuggestion);
958 }
959
960 /**
961 * Create advanced options form
962 *
963 * @param array $inData Form configuration data
964 */
965 protected function makeAdvancedOptionsForm($inData)
966 {
967 $loadedExtensions = ExtensionManagementUtility::getLoadedExtensionListArray();
968 $loadedExtensions = array_combine($loadedExtensions, $loadedExtensions);
969 $this->standaloneView->assign('extensions', $loadedExtensions);
970 $this->standaloneView->assign('inData', $inData);
971 }
972
973 /**
974 * Create configuration form
975 *
976 * @param array $inData Form configuration data
977 */
978 protected function makeSaveForm($inData)
979 {
980 $opt = $this->presetRepository->getPresets((int)$inData['pagetree']['id']);
981
982 $this->standaloneView->assign('presetSelectOptions', $opt);
983
984 $saveFolder = $this->getDefaultImportExportFolder();
985 if ($saveFolder) {
986 $this->standaloneView->assign('saveFolder', $saveFolder->getCombinedIdentifier());
987 }
988
989 // Add file options:
990 $opt = [];
991 $opt['xml'] = $this->lang->getLL('makesavefo_xml');
992 if ($this->export->compress) {
993 $opt['t3d_compressed'] = $this->lang->getLL('makesavefo_t3dFileCompressed');
994 }
995 $opt['t3d'] = $this->lang->getLL('makesavefo_t3dFile');
996
997 $this->standaloneView->assign('filetypeSelectOptions', $opt);
998
999 $fileName = '';
1000 if ($saveFolder) {
1001 $this->standaloneView->assign('saveFolder', $saveFolder->getPublicUrl());
1002 $this->standaloneView->assign('hasSaveFolder', true);
1003 }
1004 $this->standaloneView->assign('fileName', $fileName);
1005 }
1006
1007 /**************************
1008 * IMPORT FUNCTIONS
1009 **************************/
1010
1011 /**
1012 * Import part of module
1013 *
1014 * @param array $inData Content of POST VAR tx_impexp[]..
1015 * @throws \BadFunctionCallException
1016 * @throws \InvalidArgumentException
1017 * @throws \RuntimeException
1018 */
1019 protected function importData($inData)
1020 {
1021 $access = is_array($this->pageinfo);
1022 $beUser = $this->getBackendUser();
1023 if ($this->id && $access || $beUser->isAdmin() && !$this->id) {
1024 if ($beUser->isAdmin() && !$this->id) {
1025 $this->pageinfo = ['title' => '[root-level]', 'uid' => 0, 'pid' => 0];
1026 }
1027 if ($inData['new_import']) {
1028 unset($inData['import_mode']);
1029 }
1030 /** @var Import $import */
1031 $import = GeneralUtility::makeInstance(Import::class);
1032 $import->init();
1033 $import->update = $inData['do_update'];
1034 $import->import_mode = $inData['import_mode'];
1035 $import->enableLogging = $inData['enableLogging'];
1036 $import->global_ignore_pid = $inData['global_ignore_pid'];
1037 $import->force_all_UIDS = $inData['force_all_UIDS'];
1038 $import->showDiff = !$inData['notShowDiff'];
1039 $import->allowPHPScripts = $inData['allowPHPScripts'];
1040 $import->softrefInputValues = $inData['softrefInputValues'];
1041
1042 // OUTPUT creation:
1043
1044 // Make input selector:
1045 // must have trailing slash.
1046 $path = $this->getDefaultImportExportFolder();
1047 $exportFiles = $this->getExportFiles();
1048
1049 $this->shortcutName .= ' (' . $this->pageinfo['title'] . ')';
1050
1051 // Configuration
1052 $selectOptions = [''];
1053 foreach ($exportFiles as $file) {
1054 $selectOptions[$file->getCombinedIdentifier()] = $file->getPublicUrl();
1055 }
1056
1057 $this->standaloneView->assign('import', $import);
1058 $this->standaloneView->assign('inData', $inData);
1059 $this->standaloneView->assign('fileSelectOptions', $selectOptions);
1060
1061 if ($path) {
1062 $this->standaloneView->assign('importPath', sprintf($this->lang->getLL('importdata_fromPathS'), $path->getCombinedIdentifier()));
1063 } else {
1064 $this->standaloneView->assign('importPath', $this->lang->getLL('importdata_no_default_upload_folder'));
1065 }
1066 $this->standaloneView->assign('isAdmin', $beUser->isAdmin());
1067
1068 // Upload file:
1069 $tempFolder = $this->getDefaultImportExportFolder();
1070 if ($tempFolder) {
1071 $this->standaloneView->assign('tempFolder', $tempFolder->getCombinedIdentifier());
1072 $this->standaloneView->assign('hasTempUploadFolder', true);
1073 if (GeneralUtility::_POST('_upload')) {
1074 $this->standaloneView->assign('submitted', GeneralUtility::_POST('_upload'));
1075 $this->standaloneView->assign('noFileUploaded', $this->fileProcessor->internalUploadMap[1]);
1076 if ($this->uploadedFiles[0]) {
1077 $this->standaloneView->assign('uploadedFile', $this->uploadedFiles[0]->getName());
1078 }
1079 }
1080 }
1081
1082 // Perform import or preview depending:
1083 $inFile = $this->getFile($inData['file']);
1084 if ($inFile !== null && $inFile->exists()) {
1085 $this->standaloneView->assign('metaDataInFileExists', true);
1086 $importInhibitedMessages = [];
1087 if ($import->loadFile($inFile->getForLocalProcessing(false), 1)) {
1088 $importInhibitedMessages = $import->checkImportPrerequisites();
1089 if ($inData['import_file']) {
1090 if (empty($importInhibitedMessages)) {
1091 $import->importData($this->id);
1092 BackendUtility::setUpdateSignal('updatePageTree');
1093 }
1094 }
1095 $import->display_import_pid_record = $this->pageinfo;
1096 $this->standaloneView->assign('contentOverview', $import->displayContentOverview());
1097 }
1098 // Compile messages which are inhibiting a proper import and add them to output.
1099 if (!empty($importInhibitedMessages)) {
1100 $flashMessageQueue = GeneralUtility::makeInstance(FlashMessageService::class)->getMessageQueueByIdentifier('impexp.errors');
1101 foreach ($importInhibitedMessages as $message) {
1102 $flashMessageQueue->addMessage(GeneralUtility::makeInstance(
1103 FlashMessage::class,
1104 $message,
1105 '',
1106 FlashMessage::ERROR
1107 ));
1108 }
1109 }
1110 }
1111
1112 $this->standaloneView->assign('errors', $import->errorLog);
1113 }
1114 }
1115
1116 /****************************
1117 * Helper functions
1118 ****************************/
1119
1120 /**
1121 * Returns a \TYPO3\CMS\Core\Resource\Folder object for saving export files
1122 * to the server and is also used for uploading import files.
1123 *
1124 * @throws \InvalidArgumentException
1125 * @return \TYPO3\CMS\Core\Resource\Folder|null
1126 */
1127 protected function getDefaultImportExportFolder()
1128 {
1129 $defaultImportExportFolder = null;
1130
1131 $defaultTemporaryFolder = $this->getBackendUser()->getDefaultUploadTemporaryFolder();
1132 if ($defaultTemporaryFolder !== null) {
1133 $importExportFolderName = 'importexport';
1134 $createFolder = !$defaultTemporaryFolder->hasFolder($importExportFolderName);
1135 if ($createFolder === true) {
1136 try {
1137 $defaultImportExportFolder = $defaultTemporaryFolder->createFolder($importExportFolderName);
1138 } catch (Exception $folderAccessException) {
1139 }
1140 } else {
1141 $defaultImportExportFolder = $defaultTemporaryFolder->getSubfolder($importExportFolderName);
1142 }
1143 }
1144
1145 return $defaultImportExportFolder;
1146 }
1147
1148 /**
1149 * Check if a file has been uploaded
1150 *
1151 * @throws \InvalidArgumentException
1152 * @throws \UnexpectedValueException
1153 */
1154 protected function checkUpload()
1155 {
1156 $file = GeneralUtility::_GP('file');
1157 // Initializing:
1158 $this->fileProcessor = GeneralUtility::makeInstance(ExtendedFileUtility::class);
1159 $this->fileProcessor->setActionPermissions();
1160 $conflictMode = empty(GeneralUtility::_GP('overwriteExistingFiles')) ? DuplicationBehavior::__default : DuplicationBehavior::REPLACE;
1161 $this->fileProcessor->setExistingFilesConflictMode(DuplicationBehavior::cast($conflictMode));
1162 $this->fileProcessor->start($file);
1163 $result = $this->fileProcessor->processData();
1164 if (!empty($result['upload'])) {
1165 foreach ($result['upload'] as $uploadedFiles) {
1166 $this->uploadedFiles += $uploadedFiles;
1167 }
1168 }
1169 }
1170
1171 /**
1172 * Returns option array to be used in Fluid
1173 *
1174 * @param string $excludeList Table names (and the string "_ALL") to exclude. Comma list
1175 * @return array
1176 */
1177 protected function getTableSelectOptions($excludeList = '')
1178 {
1179 $optValues = [];
1180 if (!GeneralUtility::inList($excludeList, '_ALL')) {
1181 $optValues['_ALL'] = '[' . $this->lang->getLL('ALL_tables') . ']';
1182 }
1183 foreach ($GLOBALS['TCA'] as $table => $_) {
1184 if ($this->getBackendUser()->check('tables_select', $table) && !GeneralUtility::inList($excludeList, $table)) {
1185 $optValues[$table] = $table;
1186 }
1187 }
1188 return $optValues;
1189 }
1190
1191 /**
1192 * Filter page IDs by traversing exclude array, finding all
1193 * excluded pages (if any) and making an AND NOT IN statement for the select clause.
1194 *
1195 * @param array $exclude Exclude array from import/export object.
1196 * @return string AND where clause part to filter out page uids.
1197 */
1198 protected function filterPageIds($exclude)
1199 {
1200 // Get keys:
1201 $exclude = array_keys($exclude);
1202 // Traverse
1203 $pageIds = [];
1204 foreach ($exclude as $element) {
1205 list($table, $uid) = explode(':', $element);
1206 if ($table === 'pages') {
1207 $pageIds[] = (int)$uid;
1208 }
1209 }
1210 // Add to clause:
1211 if (!empty($pageIds)) {
1212 return ' AND uid NOT IN (' . implode(',', $pageIds) . ')';
1213 }
1214 return '';
1215 }
1216
1217 /**
1218 * @return BackendUserAuthentication
1219 */
1220 protected function getBackendUser(): BackendUserAuthentication
1221 {
1222 return $GLOBALS['BE_USER'];
1223 }
1224
1225 /**
1226 * @return LanguageService
1227 */
1228 protected function getLanguageService(): LanguageService
1229 {
1230 return $GLOBALS['LANG'];
1231 }
1232
1233 /**
1234 * Gets all export files.
1235 *
1236 * @throws \InvalidArgumentException
1237 * @return array|\TYPO3\CMS\Core\Resource\File[]
1238 */
1239 protected function getExportFiles()
1240 {
1241 $exportFiles = [];
1242
1243 $folder = $this->getDefaultImportExportFolder();
1244 if ($folder !== null) {
1245
1246 /** @var FileExtensionFilter $filter */
1247 $filter = GeneralUtility::makeInstance(FileExtensionFilter::class);
1248 $filter->setAllowedFileExtensions(['t3d', 'xml']);
1249 $folder->getStorage()->addFileAndFolderNameFilter([$filter, 'filterFileList']);
1250
1251 $exportFiles = $folder->getFiles();
1252 }
1253
1254 return $exportFiles;
1255 }
1256
1257 /**
1258 * Gets a file by combined identifier.
1259 *
1260 * @param string $combinedIdentifier
1261 * @return \TYPO3\CMS\Core\Resource\File|null
1262 */
1263 protected function getFile($combinedIdentifier)
1264 {
1265 try {
1266 $file = ResourceFactory::getInstance()->getFileObjectFromCombinedIdentifier($combinedIdentifier);
1267 } catch (\Exception $exception) {
1268 $file = null;
1269 }
1270
1271 return $file;
1272 }
1273
1274 /**
1275 * Initializes the internal MOD_MENU array setting and unsetting items based on various conditions. It also merges in external menu items from the global array TBE_MODULES_EXT (see mergeExternalItems())
1276 * Then MOD_SETTINGS array is cleaned up (see \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData()) so it contains only valid values. It's also updated with any SET[] values submitted.
1277 * Also loads the modTSconfig internal variable.
1278 *
1279 * @see init(), $MOD_MENU, $MOD_SETTINGS, \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData(), mergeExternalItems()
1280 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
1281 */
1282 protected function menuConfig()
1283 {
1284 // Page / user TSconfig settings and blinding of menu-items
1285 $this->modTSconfig['properties'] = BackendUtility::getPagesTSconfig($this->id)['mod.'][$this->MCONF['name'] . '.'] ?? [];
1286 $this->MOD_MENU['function'] = $this->mergeExternalItems($this->MCONF['name'], 'function', $this->MOD_MENU['function']);
1287 $blindActions = $this->modTSconfig['properties']['menu.']['function.'] ?? [];
1288 foreach ($blindActions as $key => $value) {
1289 if (!$value && array_key_exists($key, $this->MOD_MENU['function'])) {
1290 unset($this->MOD_MENU['function'][$key]);
1291 }
1292 }
1293 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->MCONF['name'], $this->modMenu_type, $this->modMenu_dontValidateList, $this->modMenu_setDefaultList);
1294 }
1295
1296 /**
1297 * Merges menu items from global array $TBE_MODULES_EXT
1298 *
1299 * @param string $modName Module name for which to find value
1300 * @param string $menuKey Menu key, eg. 'function' for the function menu.
1301 * @param array $menuArr The part of a MOD_MENU array to work on.
1302 * @return array Modified array part.
1303 * @internal
1304 * @see \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::insertModuleFunction(), menuConfig()
1305 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
1306 */
1307 protected function mergeExternalItems($modName, $menuKey, $menuArr)
1308 {
1309 $mergeArray = $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey];
1310 if (is_array($mergeArray)) {
1311 foreach ($mergeArray as $k => $v) {
1312 if (((string)$v['ws'] === '' || $this->getBackendUser()->workspace === 0 && GeneralUtility::inList($v['ws'], 'online')) || $this->getBackendUser()->workspace === -1 && GeneralUtility::inList($v['ws'], 'offline') || $this->getBackendUser()->workspace > 0 && GeneralUtility::inList($v['ws'], 'custom')) {
1313 $menuArr[$k] = $this->getLanguageService()->sL($v['title']);
1314 }
1315 }
1316 }
1317 return $menuArr;
1318 }
1319
1320 /**
1321 * Loads $this->extClassConf with the configuration for the CURRENT function of the menu.
1322 *
1323 * @param string $MM_key The key to MOD_MENU for which to fetch configuration. 'function' is default since it is first and foremost used to get information per "extension object" (I think that is what its called)
1324 * @param string $MS_value The value-key to fetch from the config array. If NULL (default) MOD_SETTINGS[$MM_key] will be used. This is useful if you want to force another function than the one defined in MOD_SETTINGS[function]. Call this in init() function of your Script Class: handleExternalFunctionValue('function', $forcedSubModKey)
1325 * @see getExternalItemConfig(), init()
1326 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
1327 */
1328 protected function handleExternalFunctionValue($MM_key = 'function', $MS_value = null)
1329 {
1330 if ($MS_value === null) {
1331 $MS_value = $this->MOD_SETTINGS[$MM_key];
1332 }
1333 $this->extClassConf = $this->getExternalItemConfig($this->MCONF['name'], $MM_key, $MS_value);
1334 }
1335
1336 /**
1337 * Returns configuration values from the global variable $TBE_MODULES_EXT for the module given.
1338 * For example if the module is named "web_info" and the "function" key ($menuKey) of MOD_SETTINGS is "stat" ($value) then you will have the values of $TBE_MODULES_EXT['webinfo']['MOD_MENU']['function']['stat'] returned.
1339 *
1340 * @param string $modName Module name
1341 * @param string $menuKey Menu key, eg. "function" for the function menu. See $this->MOD_MENU
1342 * @param string $value Optionally the value-key to fetch from the array that would otherwise have been returned if this value was not set. Look source...
1343 * @return mixed The value from the TBE_MODULES_EXT array.
1344 * @see handleExternalFunctionValue()
1345 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
1346 */
1347 protected function getExternalItemConfig($modName, $menuKey, $value = '')
1348 {
1349 if (isset($GLOBALS['TBE_MODULES_EXT'][$modName])) {
1350 return (string)$value !== '' ? $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey][$value] : $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey];
1351 }
1352 return null;
1353 }
1354
1355 /**
1356 * Creates an instance of the class found in $this->extClassConf['name'] in $this->extObj if any (this should hold three keys, "name", "path" and "title" if a "Function menu module" tries to connect...)
1357 * This value in extClassConf might be set by an extension (in an ext_tables/ext_localconf file) which thus "connects" to a module.
1358 * The array $this->extClassConf is set in handleExternalFunctionValue() based on the value of MOD_SETTINGS[function]
1359 * If an instance is created it is initiated with $this passed as value and $this->extClassConf as second argument. Further the $this->MOD_SETTING is cleaned up again after calling the init function.
1360 *
1361 * @see handleExternalFunctionValue(), \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::insertModuleFunction(), $extObj
1362 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
1363 */
1364 protected function checkExtObj()
1365 {
1366 if (is_array($this->extClassConf) && $this->extClassConf['name']) {
1367 $this->extObj = GeneralUtility::makeInstance($this->extClassConf['name']);
1368 $this->extObj->init($this, $this->extClassConf);
1369 // Re-write:
1370 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->MCONF['name'], $this->modMenu_type, $this->modMenu_dontValidateList, $this->modMenu_setDefaultList);
1371 }
1372 }
1373
1374 /**
1375 * Calls the checkExtObj function in sub module if present.
1376 *
1377 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
1378 */
1379 protected function checkSubExtObj()
1380 {
1381 if (is_object($this->extObj)) {
1382 $this->extObj->checkExtObj();
1383 }
1384 }
1385
1386 /**
1387 * Calls the 'header' function inside the "Function menu module" if present.
1388 * A header function might be needed to add JavaScript or other stuff in the head.
1389 * This can't be done in the main function because the head is already written.
1390 *
1391 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
1392 */
1393 protected function extObjHeader()
1394 {
1395 if (is_callable([$this->extObj, 'head'])) {
1396 $this->extObj->head();
1397 }
1398 }
1399
1400 /**
1401 * Calls the 'main' function inside the "Function menu module" if present
1402 *
1403 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
1404 */
1405 protected function extObjContent()
1406 {
1407 if ($this->extObj === null) {
1408 $flashMessage = GeneralUtility::makeInstance(
1409 FlashMessage::class,
1410 $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:no_modules_registered'),
1411 $this->getLanguageService()->getLL('title'),
1412 FlashMessage::ERROR
1413 );
1414 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1415 /** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
1416 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1417 $defaultFlashMessageQueue->enqueue($flashMessage);
1418 } else {
1419 $this->extObj->pObj = $this;
1420 if (is_callable([$this->extObj, 'main'])) {
1421 $this->content .= $this->extObj->main();
1422 }
1423 }
1424 }
1425
1426 /**
1427 * Return the content of the 'main' function inside the "Function menu module" if present
1428 *
1429 * @return string
1430 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
1431 */
1432 protected function getExtObjContent()
1433 {
1434 $savedContent = $this->content;
1435 $this->content = '';
1436 $this->extObjContent();
1437 $newContent = $this->content;
1438 $this->content = $savedContent;
1439 return $newContent;
1440 }
1441 }