diff --git a/typo3/sysext/impexp/Classes/Controller/ImportExportController.php b/typo3/sysext/impexp/Classes/Controller/ImportExportController.php index d92b01af7d26f798825af1b8a326133f72324dcb..4cedcaec85962cdc6d3053a4d9511f1d78228b9b 100644 --- a/typo3/sysext/impexp/Classes/Controller/ImportExportController.php +++ b/typo3/sysext/impexp/Classes/Controller/ImportExportController.php @@ -833,26 +833,14 @@ class ImportExportController extends BaseScriptClass } // Perform import or preview depending: - $extensionInstallationMessage = ''; $inFile = $this->getFile($inData['file']); if ($inFile !== null && $inFile->exists()) { $this->standaloneView->assign('metaDataInFileExists', true); + $importInhibitedMessages = array(); if ($import->loadFile($inFile->getForLocalProcessing(false), 1)) { - // Check extension dependencies: - $extKeysToInstall = array(); - if (is_array($import->dat['header']['extensionDependencies'])) { - foreach ($import->dat['header']['extensionDependencies'] as $extKey) { - if (!ExtensionManagementUtility::isLoaded($extKey)) { - $extKeysToInstall[] = $extKey; - } - } - } - if (!empty($extKeysToInstall)) { - $extensionInstallationMessage = 'Before you can install this T3D file you need to install the extensions "' - . implode('", "', $extKeysToInstall) . '".'; - } + $importInhibitedMessages = $import->checkImportPrerequisites(); if ($inData['import_file']) { - if (empty($extKeysToInstall)) { + if (empty($importInhibitedMessages)) { $import->importData($this->id); BackendUtility::setUpdateSignal('updatePageTree'); } @@ -860,17 +848,22 @@ class ImportExportController extends BaseScriptClass $import->display_import_pid_record = $this->pageinfo; $this->standaloneView->assign('contentOverview', $import->displayContentOverview()); } + // Compile messages which are inhibiting a proper import and add them to output. + if (!empty($importInhibitedMessages)) { + $flashMessageQueue = GeneralUtility::makeInstance(FlashMessageService::class)->getMessageQueueByIdentifier('impexp.errors'); + foreach ($importInhibitedMessages as $message) { + $flashMessageQueue->addMessage(GeneralUtility::makeInstance( + FlashMessage::class, + $message, + '', + FlashMessage::ERROR + )); + } + } } // Print errors that might be: $errors = $import->printErrorLog(); $this->standaloneView->assign('errors', trim($errors)); - if ($extensionInstallationMessage) { - $this->standaloneView->assign( - 'extensionInstallationMessage', - '<div style="border: 1px black solid; margin: 10px 10px 10px 10px; padding: 10px 10px 10px 10px;">' - . $this->moduleTemplate->icons(1) . htmlspecialchars($extensionInstallationMessage) . '</div>' - ); - } } } diff --git a/typo3/sysext/impexp/Classes/Import.php b/typo3/sysext/impexp/Classes/Import.php index b2f5d056cbfb8ec476360fdd81c5b1f93e5a006f..8472ea1e30b980ed5f429eee15bdf86f14d84304 100644 --- a/typo3/sysext/impexp/Classes/Import.php +++ b/typo3/sysext/impexp/Classes/Import.php @@ -21,7 +21,9 @@ use TYPO3\CMS\Core\Resource\DuplicationBehavior; use TYPO3\CMS\Core\Resource\File; use TYPO3\CMS\Core\Resource\FileInterface; use TYPO3\CMS\Core\Resource\ResourceFactory; +use TYPO3\CMS\Core\Resource\ResourceStorage; use TYPO3\CMS\Core\Resource\StorageRepository; +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; use TYPO3\CMS\Core\Utility\PathUtility; @@ -74,7 +76,7 @@ class Import extends ImportExport /** * Array of current registered storage objects * - * @var array + * @var ResourceStorage[] */ protected $storageObjects = array(); @@ -256,29 +258,16 @@ class Import extends ImportExport $storageRecord = $this->dat['records']['sys_file_storage:' . $sysFileStorageUid]['data']; // continue with Local, writable and online storage only if ($storageRecord['driver'] === 'Local' && $storageRecord['is_writable'] && $storageRecord['is_online']) { - $useThisStorageUidInsteadOfTheOneInImport = 0; - /** @var $localStorage \TYPO3\CMS\Core\Resource\ResourceStorage */ foreach ($this->storageObjects as $localStorage) { - // check the available storage for Local, writable and online ones - if ($localStorage->getDriverType() === 'Local' && $localStorage->isWritable() && $localStorage->isOnline()) { - // check if there is already an identical storage present (same pathType and basePath) - $storageRecordConfiguration = ResourceFactory::getInstance()->convertFlexFormDataToConfigurationArray($storageRecord['configuration']); - $localStorageRecordConfiguration = $localStorage->getConfiguration(); - if ( - $storageRecordConfiguration['pathType'] === $localStorageRecordConfiguration['pathType'] - && $storageRecordConfiguration['basePath'] === $localStorageRecordConfiguration['basePath'] - ) { - // same storage is already present - $useThisStorageUidInsteadOfTheOneInImport = $localStorage->getUid(); - break; - } + if ($this->isEquivalentObjectStorage($localStorage, $storageRecord)) { + $this->import_mapId['sys_file_storage'][$sysFileStorageUid] = $localStorage->getUid(); + break; } } - if ($useThisStorageUidInsteadOfTheOneInImport > 0) { - // same storage is already present; map the to be imported one to the present one - $this->import_mapId['sys_file_storage'][$sysFileStorageUid] = $useThisStorageUidInsteadOfTheOneInImport; - } else { + + if (!isset($this->import_mapId['sys_file_storage'][$sysFileStorageUid])) { // Local, writable and online storage. Is allowed to be used to later write files in. + // Does currently not exist so add the record. $this->addSingle('sys_file_storage', $sysFileStorageUid, 0); } } else { @@ -314,6 +303,95 @@ class Import extends ImportExport unset($this->dat['header']['records']['sys_file_storage']); } + /** + * Determines whether the passed storage object and record (sys_file_storage) can be + * seen as equivalent during import. + * + * @param ResourceStorage $storageObject The storage object which should get compared + * @param array $storageRecord The storage record which should get compared + * @return bool Returns TRUE when both object storages can be seen as equivalent + */ + protected function isEquivalentObjectStorage(ResourceStorage $storageObject, array $storageRecord) { + // compare the properties: driver, writable and online + if ( + $storageObject->getDriverType() === $storageRecord['driver'] + && (bool)$storageObject->isWritable() === (bool)$storageRecord['is_writable'] + && (bool)$storageObject->isOnline() === (bool)$storageRecord['is_online'] + ) { + $storageRecordConfiguration = ResourceFactory::getInstance()->convertFlexFormDataToConfigurationArray($storageRecord['configuration']); + $storageObjectConfiguration = $storageObject->getConfiguration(); + // compare the properties: pathType and basePath + if ( + $storageRecordConfiguration['pathType'] === $storageObjectConfiguration['pathType'] + && $storageRecordConfiguration['basePath'] === $storageObjectConfiguration['basePath'] + ) { + return true; + } + } + return false; + } + + /** + * Checks any prerequisites necessary to get fullfilled before import + * + * @return array Messages explaining issues which need to get resolved before import + */ + public function checkImportPrerequisites() { + $messages = array(); + + // Check #1: Extension dependencies + $extKeysToInstall = array(); + if (is_array($this->dat['header']['extensionDependencies'])) { + foreach ($this->dat['header']['extensionDependencies'] as $extKey) { + if (!ExtensionManagementUtility::isLoaded($extKey)) { + $extKeysToInstall[] = $extKey; + } + } + } + if (!empty($extKeysToInstall)) { + $messages['missingExtensions'] = 'Before you can install this T3D file you need to install the extensions "' . implode('", "', $extKeysToInstall) . '".'; + } + + // Check #2: If the path for every local storage object exists. + // Else files can't get moved into a newly imported storage. + foreach ($this->dat['header']['records']['sys_file_storage'] as $sysFileStorageUid => $_) { + $storageRecord = $this->dat['records']['sys_file_storage:' . $sysFileStorageUid]['data']; + // continue with Local, writable and online storage only + if ($storageRecord['driver'] === 'Local' && $storageRecord['is_writable'] && $storageRecord['is_online']) { + $storageExists = false; + /** @var $localStorage \TYPO3\CMS\Core\Resource\ResourceStorage */ + foreach ($this->storageObjects as $localStorage) { + if ($this->isEquivalentObjectStorage($localStorage, $storageRecord)) { + // There is already an existing storage + $storageExists = true; + break; + } + } + + if (!$storageExists) { + // The storage from the import does not have an equivalent storage + // in the current instance (same driver, same path, etc.). Before + // the storage record can get inserted later on take care the path + // it points to really exists and is accessible. + $storageRecordUid = $storageRecord['uid']; + // Unset the storage record UID when trying to create the storage object + // as the record does not already exist in DB. The constructor of the + // storage object will check whether the target folder exists and set the + // isOnline flag depending on the outcome. + $storageRecord['uid'] = 0; + $resourceStorage = ResourceFactory::getInstance()->createStorageObject($storageRecord); + if (!$resourceStorage->isOnline()) { + $configuration = $resourceStorage->getConfiguration(); + $messages['resourceStorageFolderMissing_' . $storageRecordUid] = 'The resource storage "' . $resourceStorage->getName() . '" will get imported. The storage target directory "' . $configuration['basePath'] . '" does not exist. Please create the directory prior to starting the import!'; + } + + } + } + } + + return $messages; + } + /** * Imports the sys_file records and the binary files data from internal data array. * @@ -408,7 +486,7 @@ class Import extends ImportExport $sanitizedFolderMappings[$folderName] = $importFolder->getIdentifier(); } } catch (Exception $e) { - $this->error('Error: Folder could not be created for file "' . $fileRecord['identifier'] . '" with storage uid "' . $fileRecord['storage'] . '"'); + $this->error('Error: Folder "' . $folderName . '" could not be created for file "' . $fileRecord['identifier'] . '" with storage uid "' . $fileRecord['storage'] . '"'); continue; } } else { diff --git a/typo3/sysext/impexp/Resources/Private/Templates/ImportExport/Import.html b/typo3/sysext/impexp/Resources/Private/Templates/ImportExport/Import.html index 9458b7ba790ac3babac3d14fb41dbdbe092f1371..3c78e86c0154cc44c0869a0d1d06e047194dc30b 100644 --- a/typo3/sysext/impexp/Resources/Private/Templates/ImportExport/Import.html +++ b/typo3/sysext/impexp/Resources/Private/Templates/ImportExport/Import.html @@ -2,9 +2,26 @@ <f:section name="content"> <div> - <f:if condition="{extensionInstallationMessage}"> - <f:format.raw>{extensionInstallationMessage}</f:format.raw> - </f:if> + <f:flashMessages as="flashMessages" queueIdentifier="impexp.errors"> + <f:for each="{flashMessages}" as="flashMessage"> + <div class="alert {flashMessage.class}"> + <div class="media"> + <div class="media-left"> + <span class="fa-stack fa-lg"> + <i class="fa fa-circle fa-stack-2x"></i> + <i class="fa fa-{flashMessage.iconName} fa-stack-1x"></i> + </span> + </div> + <div class="media-body"> + <f:if condition="{flashMessage.title}"> + <h4 class="alert-title">{flashMessage.title}</h4> + </f:if> + <div class="alert-message">{flashMessage.message}</div> + </div> + </div> + </div> + </f:for> + </f:flashMessages> <div role="tabpanel"> <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active">