diff --git a/Classes/Command/DestinationDataImportCommand.php b/Classes/Command/DestinationDataImportCommand.php index f1a643e..b045e76 100644 --- a/Classes/Command/DestinationDataImportCommand.php +++ b/Classes/Command/DestinationDataImportCommand.php @@ -7,7 +7,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use TYPO3\CMS\Core\Core\Bootstrap; -use Wrm\Events\Domain\DestinationData\Import; +use Wrm\Events\Domain\DestinationData\LegacyImportFactory; use Wrm\Events\Service\DestinationDataImportService; class DestinationDataImportCommand extends Command @@ -17,11 +17,18 @@ class DestinationDataImportCommand extends Command */ private $destinationDataImportService; + /** + * @var LegacyImportFactory + */ + private $importFactory; + public function __construct( - DestinationDataImportService $destinationDataImportService + DestinationDataImportService $destinationDataImportService, + LegacyImportFactory $importFactory ) { parent::__construct(); $this->destinationDataImportService = $destinationDataImportService; + $this->importFactory = $importFactory; } public function configure(): void @@ -72,12 +79,19 @@ class DestinationDataImportCommand extends Command $query = ''; } - return $this->destinationDataImportService->import(new Import( - $input->getArgument('rest-experience'), - $input->getArgument('storage-pid'), - $regionUid, - $input->getArgument('files-folder'), - $query - )); + $import = $this->importFactory->createFromArray([ + 'storage_pid' => $input->getArgument('storage-pid'), + + 'files_folder' => $input->getArgument('files-folder'), + + 'region_uid' => $regionUid, + + 'rest_experience' => $input->getArgument('rest-experience'), + 'rest_search_query' => $query, + ]); + + return $this->destinationDataImportService->import( + $import + ); } } diff --git a/Classes/Command/ImportDestinationDataViaAllConfigruationsCommand.php b/Classes/Command/ImportDestinationDataViaAllConfigruationsCommand.php new file mode 100644 index 0000000..26b807b --- /dev/null +++ b/Classes/Command/ImportDestinationDataViaAllConfigruationsCommand.php @@ -0,0 +1,54 @@ +destinationDataImportService = $destinationDataImportService; + $this->importFactory = $importFactory; + } + + public function configure(): void + { + $this->setDescription('Import Destination Data Events'); + $this->setHelp('Destination Data Events are imported from all configuration records.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + Bootstrap::initializeBackendAuthentication(); + + $finalResult = 0; + foreach ($this->importFactory->createAll() as $import) { + $result = $this->destinationDataImportService->import($import); + if ($result !== 0) { + $finalResult = $result; + } + } + + return $finalResult; + } +} diff --git a/Classes/Command/ImportDestinationDataViaConfigruationCommand.php b/Classes/Command/ImportDestinationDataViaConfigruationCommand.php new file mode 100644 index 0000000..459e30f --- /dev/null +++ b/Classes/Command/ImportDestinationDataViaConfigruationCommand.php @@ -0,0 +1,64 @@ +destinationDataImportService = $destinationDataImportService; + $this->importFactory = $importFactory; + } + + public function configure(): void + { + $this->setDescription('Import Destination Data Events'); + $this->setHelp('Destination Data Events are imported from given configuration record.'); + + $this->addArgument( + 'configurationUid', + InputArgument::REQUIRED, + 'UID of the configuration to import' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + Bootstrap::initializeBackendAuthentication(); + + $configurationUid = $input->getArgument('configurationUid'); + if (is_numeric($configurationUid)) { + $configurationUid = (int) $configurationUid; + } else { + throw new \Exception('No numeric uid for configuration provided.', 1643267138); + } + + $import = $this->importFactory->createFromUid( + $configurationUid + ); + return $this->destinationDataImportService->import( + $import + ); + } +} diff --git a/Classes/Domain/DestinationData/Import.php b/Classes/Domain/DestinationData/Import.php deleted file mode 100644 index 9f5a6dc..0000000 --- a/Classes/Domain/DestinationData/Import.php +++ /dev/null @@ -1,74 +0,0 @@ -restExperience = $restExperience; - $this->storagePid = $storagePid; - $this->regionUid = $regionUid; - $this->filesFolder = $filesFolder; - $this->searchQuery = $searchQuery; - } - - public function getRestExperience(): string - { - return $this->restExperience; - } - - public function getStoragePid(): int - { - return $this->storagePid; - } - - public function getRegionUid(): ?int - { - return $this->regionUid; - } - - public function getFilesFolder(): string - { - return $this->filesFolder; - } - - public function getSearchQuery(): string - { - return $this->searchQuery; - } -} diff --git a/Classes/Domain/DestinationData/ImportFactory.php b/Classes/Domain/DestinationData/ImportFactory.php new file mode 100644 index 0000000..32d423d --- /dev/null +++ b/Classes/Domain/DestinationData/ImportFactory.php @@ -0,0 +1,129 @@ +connectionPool = $connectionPool; + $this->extbasePersistenceSession = $extbasePersistenceSession; + $this->dataMapper = $dataMapper; + $this->resourceFactory = $resourceFactory; + } + + public function createFromUid(int $uid): Import + { + return $this->create($this->fetchImportRecord($uid)); + } + + /** + * @return Import[] + */ + public function createAll(): array + { + return array_map( + [$this, 'create'], + $this->fetchImportRecords() + ); + } + + private function fetchImportRecord(int $uid): array + { + $qb = $this->connectionPool->getQueryBuilderForTable('tx_events_domain_model_import'); + $qb->select('*'); + $qb->from('tx_events_domain_model_import'); + $qb->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid, \PDO::PARAM_INT))); + + $result = $qb->execute()->fetch(); + if (is_array($result) === false) { + throw new \Exception('Could not fetch import record with uid "' . $uid . '".', 1643267492); + } + + $result = array_map('strval', $result); + + return $result; + } + + private function fetchImportRecords(): array + { + $qb = $this->connectionPool->getQueryBuilderForTable('tx_events_domain_model_import'); + $qb->select('*'); + $qb->from('tx_events_domain_model_import'); + + $result = $qb->execute()->fetchAll(); + if (count($result) === 0) { + throw new \Exception('Could not fetch any import record.', 1643267492); + } + + foreach ($result as $key => $entry) { + $result[$key] = array_map('strval', $entry); + } + + return $result; + } + + /** + * Only public in order to be used by LegacyImportFactory. + * Make private once the class is removed. + * + * @internal + */ + public function create(array $data): Import + { + $this->createWorkarounds($data); + + $result = $this->dataMapper->map(Import::class, [$data])[0]; + + $this->cleanupWorkarounds(); + + return $result; + } + + private function createWorkarounds(array $data): void + { + $this->folderInstance = $this->resourceFactory->getFolderObjectFromCombinedIdentifier($data['files_folder']); + $this->extbasePersistenceSession->registerObject($this->folderInstance, $data['files_folder']); + } + + private function cleanupWorkarounds(): void + { + $this->extbasePersistenceSession->unregisterObject($this->folderInstance); + } +} diff --git a/Classes/Domain/DestinationData/LegacyImportFactory.php b/Classes/Domain/DestinationData/LegacyImportFactory.php new file mode 100644 index 0000000..07a85ae --- /dev/null +++ b/Classes/Domain/DestinationData/LegacyImportFactory.php @@ -0,0 +1,103 @@ +importFactory = $importFactory; + $this->resourceFactory = $resourceFactory; + $this->configurationManager = $configurationManager; + $this->extbasePersistenceSession = $extbasePersistenceSession; + } + + public function createFromArray(array $configuration): Import + { + $result = array_map('strval', $configuration); + + $result['uid'] = $this->getUniqueUid(); + + $result['files_folder'] = $this->migrateFileFolder($result['files_folder'] ?? ''); + + $result['region'] = $result['region_uid']; + unset($result['region_uid']); + + $result = $this->addCategorySettings($result); + + return $this->importFactory->create($result); + } + + private function getUniqueUid(): string + { + do { + // Only temporary solution as long as legacy exists. + // Cool solution would be to fetch highest uid + 100, but that's to much for now. + // Also this will vanish in future. + $uid = (string) random_int(999, PHP_INT_MAX); + } while ($this->extbasePersistenceSession->hasIdentifier($uid, Import::class)); + + return $uid; + } + + private function migrateFileFolder(string $fileFolder): string + { + $storage = $this->resourceFactory->getDefaultStorage(); + if ($storage === null) { + throw new \Exception('No default storage defined. Cancel import.', 1643290642); + } + + $uid = $storage->getUid(); + + return $uid . ':/' . trim($fileFolder, '/') . '/'; + } + + private function addCategorySettings(array $result): array + { + $settings = $this->configurationManager->getConfiguration( + ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, + 'Events', + 'Pi1' + ); + + $result['categories_pid'] = $settings['destinationData']['categoriesPid'] ?? ''; + $result['category_parent'] = $settings['destinationData']['categoryParentUid'] ?? ''; + + return $result; + } +} diff --git a/Classes/Domain/Model/Import.php b/Classes/Domain/Model/Import.php new file mode 100644 index 0000000..95b13b7 --- /dev/null +++ b/Classes/Domain/Model/Import.php @@ -0,0 +1,104 @@ +filesFolder = $filesFolder; + $this->storagePid = $storagePid; + + $this->categoriesPid = $categoriesPid; + $this->categoryParent = $categoryParent; + + $this->restExperience = $restExperience; + $this->restSearchQuery = $restSearchQuery; + + $this->region = $region; + } + + public function getStoragePid(): int + { + return $this->storagePid; + } + + public function getFilesFolder(): Folder + { + return $this->filesFolder; + } + + public function getCategoriesPid(): int + { + return $this->categoriesPid; + } + + public function getCategoryParent(): ?Category + { + return $this->categoryParent; + } + + public function getRegion(): ?Region + { + return $this->region; + } + + public function getRestExperience(): string + { + return $this->restExperience; + } + + public function getSearchQuery(): string + { + return $this->restSearchQuery; + } +} diff --git a/Classes/Service/DestinationDataImportService.php b/Classes/Service/DestinationDataImportService.php index 342a83d..2b07900 100644 --- a/Classes/Service/DestinationDataImportService.php +++ b/Classes/Service/DestinationDataImportService.php @@ -2,7 +2,6 @@ namespace Wrm\Events\Service; -use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\DataHandling\SlugHelper; @@ -11,7 +10,6 @@ use TYPO3\CMS\Core\Log\LogManager; use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException; use TYPO3\CMS\Core\Resource\File; use TYPO3\CMS\Core\Resource\Index\MetaDataRepository; -use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Resource\ResourceStorage; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Configuration\ConfigurationManager; @@ -19,17 +17,16 @@ use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Object\ObjectManager; use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager; use TYPO3\CMS\Extbase\Persistence\ObjectStorage; -use Wrm\Events\Domain\DestinationData\Import; use Wrm\Events\Domain\Model\Category; use Wrm\Events\Domain\Model\Date; use Wrm\Events\Domain\Model\Event; +use Wrm\Events\Domain\Model\Import; use Wrm\Events\Domain\Model\Organizer; use Wrm\Events\Domain\Model\Region; use Wrm\Events\Domain\Repository\CategoryRepository; use Wrm\Events\Domain\Repository\DateRepository; use Wrm\Events\Domain\Repository\EventRepository; use Wrm\Events\Domain\Repository\OrganizerRepository; -use Wrm\Events\Domain\Repository\RegionRepository; use Wrm\Events\Service\DestinationDataImportService\DataFetcher; class DestinationDataImportService @@ -39,21 +36,6 @@ class DestinationDataImportService */ private $import; - /** - * @var int - */ - private $categoriesPid; - - /** - * @var int - */ - private $categoryParentUid; - - /** - * @var Environment - */ - private $environment; - /** * @var Event */ @@ -69,11 +51,6 @@ class DestinationDataImportService */ private $eventRepository; - /** - * @var RegionRepository - */ - private $regionRepository; - /** * @var OrganizerRepository */ @@ -109,11 +86,6 @@ class DestinationDataImportService */ private $persistenceManager; - /** - * @var ResourceFactory - */ - private $resourceFactory; - /** * @var DataFetcher */ @@ -122,52 +94,35 @@ class DestinationDataImportService /** * ImportService constructor. * @param EventRepository $eventRepository - * @param RegionRepository $regionRepository * @param OrganizerRepository $organizerRepository * @param DateRepository $dateRepository * @param CategoryRepository $sysCategoriesRepository * @param MetaDataRepository $metaDataRepository * @param ConfigurationManager $configurationManager * @param PersistenceManager $persistenceManager - * @param ResourceFactory $resourceFactory * @param ObjectManager $objectManager - * @param Environment $environment * @param DataFetcher $dataFetcher */ public function __construct( EventRepository $eventRepository, - RegionRepository $regionRepository, OrganizerRepository $organizerRepository, DateRepository $dateRepository, CategoryRepository $sysCategoriesRepository, MetaDataRepository $metaDataRepository, ConfigurationManager $configurationManager, PersistenceManager $persistenceManager, - ResourceFactory $resourceFactory, ObjectManager $objectManager, - Environment $environment, DataFetcher $dataFetcher ) { $this->eventRepository = $eventRepository; - $this->regionRepository = $regionRepository; $this->organizerRepository = $organizerRepository; $this->dateRepository = $dateRepository; $this->sysCategoriesRepository = $sysCategoriesRepository; $this->metaDataRepository = $metaDataRepository; $this->configurationManager = $configurationManager; $this->persistenceManager = $persistenceManager; - $this->resourceFactory = $resourceFactory; $this->objectManager = $objectManager; - $this->environment = $environment; $this->dataFetcher = $dataFetcher; - - $settings = $this->configurationManager->getConfiguration( - ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, - 'Events', - 'Pi1' - )['destinationData'] ?? []; - $this->categoriesPid = (int) $settings['categoriesPid']; - $this->categoryParentUid = (int) $settings['categoryParentUid']; } public function import( @@ -207,10 +162,7 @@ class DestinationDataImportService $this->logger->info('Processing json ' . count($data['items'])); // Get selected region - $selectedRegion = null; - if (is_int($this->import->getRegionUid())) { - $selectedRegion = $this->regionRepository->findByUid($this->import->getRegionUid()); - } + $selectedRegion = $this->import->getRegion(); foreach ($data['items'] as $event) { $this->logger->info('Processing event ' . substr($event['title'], 0, 20)); @@ -298,16 +250,8 @@ class DestinationDataImportService */ private function setCategories(array $categories): void { - if ($this->categoryParentUid === 0) { - return; - } - - $sysParentCategory = $this->sysCategoriesRepository->findByUid($this->categoryParentUid); + $sysParentCategory = $this->import->getCategoryParent(); if (!$sysParentCategory instanceof Category) { - $this->logger->warning( - 'Could not fetch system parent category by uid.', - ['uid' => $this->categoryParentUid] - ); return; } @@ -318,7 +262,7 @@ class DestinationDataImportService $tmpSysCategory = $this->objectManager->get(Category::class); $tmpSysCategory->setTitle($categoryTitle); $tmpSysCategory->setParent($sysParentCategory); - $tmpSysCategory->setPid($this->categoriesPid); + $tmpSysCategory->setPid($this->import->getCategoriesPid()); $this->sysCategoriesRepository->add($tmpSysCategory); $this->tmpCurrentEvent->addCategory($tmpSysCategory); } else { @@ -563,18 +507,14 @@ class DestinationDataImportService { $this->logger->info("Set assets"); - $storage = $this->resourceFactory->getDefaultStorage(); - if (!$storage instanceof ResourceStorage) { - $this->logger->error('No default storage defined. Cancel import.'); - exit(); - } + $importFolder = $this->import->getFilesFolder(); $error = false; foreach ($assets as $media_object) { if ($media_object['rel'] == "default" && $media_object['type'] == "image/jpeg") { $fileUrl = urldecode($media_object['url']); - $orgFileNameSanitized = $storage->sanitizeFileName( + $orgFileNameSanitized = $importFolder->getStorage()->sanitizeFileName( basename( urldecode($media_object['url']) ) @@ -583,11 +523,7 @@ class DestinationDataImportService $this->logger->info('File attached:' . $fileUrl); $this->logger->info('File attached sanitized:' . $orgFileNameSanitized); - $targetFilePath = $this->environment->getPublicPath() . '/fileadmin/' . $this->import->getFilesFolder() - . $orgFileNameSanitized; - // Check if file already exists - - if (file_exists($targetFilePath)) { + if ($importFolder->hasFile($orgFileNameSanitized)) { $this->logger->info('File already exists'); } else { $this->logger->info("File don't exist " . $orgFileNameSanitized); @@ -596,13 +532,7 @@ class DestinationDataImportService // Move file to defined folder $this->logger->info('Adding file ' . $filename); - try { - $targetFolder = $storage->getFolder($this->import->getFilesFolder()); - } catch (FolderDoesNotExistException $e) { - $targetFolder = $storage->createFolder($this->import->getFilesFolder()); - } - - $storage->addFile($filename, $targetFolder, basename($fileUrl)); + $importFolder->addFile($filename, basename($fileUrl)); } else { $error = true; } @@ -614,12 +544,17 @@ class DestinationDataImportService // TODO: How to delete file references? } else { $this->logger->info('No relation found'); - $fileIdentifier = $this->import->getFilesFolder() . $orgFileNameSanitized; - $file = $storage->getFile($fileIdentifier); - if (!$file instanceof File) { - $this->logger->warning('Could not find file.', [$fileIdentifier]); + if ($importFolder->hasFile($orgFileNameSanitized) === false) { + $this->logger->warning('Could not find file.', [$orgFileNameSanitized]); continue; } + + $file = $importFolder->getStorage()->getFileInFolder($orgFileNameSanitized, $importFolder); + if (!$file instanceof File) { + $this->logger->warning('Could not find file.', [$orgFileNameSanitized]); + continue; + } + $this->metaDataRepository->update( $file->getUid(), [ diff --git a/Classes/Service/DestinationDataImportService/DataFetcher.php b/Classes/Service/DestinationDataImportService/DataFetcher.php index e69015e..7b892cd 100644 --- a/Classes/Service/DestinationDataImportService/DataFetcher.php +++ b/Classes/Service/DestinationDataImportService/DataFetcher.php @@ -8,7 +8,7 @@ use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\ResponseInterface; use TYPO3\CMS\Core\Log\Logger; use TYPO3\CMS\Core\Log\LogManager; -use Wrm\Events\Domain\DestinationData\Import; +use Wrm\Events\Domain\Model\Import; /** * Provides API to fetch data from remote. diff --git a/Classes/Service/DestinationDataImportService/UrlFactory.php b/Classes/Service/DestinationDataImportService/UrlFactory.php index de89146..7a829e0 100644 --- a/Classes/Service/DestinationDataImportService/UrlFactory.php +++ b/Classes/Service/DestinationDataImportService/UrlFactory.php @@ -5,7 +5,7 @@ namespace Wrm\Events\Service\DestinationDataImportService; use TYPO3\CMS\Core\Http\Uri; use TYPO3\CMS\Extbase\Configuration\ConfigurationManager; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; -use Wrm\Events\Domain\DestinationData\Import; +use Wrm\Events\Domain\Model\Import; /** * Factory to create URLs used during import of Destination Data. diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 29756c2..74b1df2 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -20,6 +20,18 @@ services: command: 'events:destinationdataimport' description: 'Import Destination Data Events' + Wrm\Events\Command\ImportDestinationDataViaConfigruationCommand: + tags: + - name: 'console.command' + command: 'events:destinationdataimportviaconfiguration' + description: 'Import Destination Data Events' + + Wrm\Events\Command\ImportDestinationDataViaAllConfigruationsCommand: + tags: + - name: 'console.command' + command: 'events:destinationdataimportviaallconfigurations' + description: 'Import Destination Data Events from all configurations' + Wrm\Events\Command\RemoveAllCommand: tags: - name: 'console.command' diff --git a/Configuration/TCA/tx_events_domain_model_import.php b/Configuration/TCA/tx_events_domain_model_import.php new file mode 100644 index 0000000..54c1cdd --- /dev/null +++ b/Configuration/TCA/tx_events_domain_model_import.php @@ -0,0 +1,150 @@ + [ + 'title' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import', + 'label' => 'title', + 'label_alt' => 'rest_experience', + 'label_alt_force' => true, + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'cruser_id' => 'cruser_id', + 'delete' => 'deleted', + 'enablecolumns' => [ + 'disabled' => 'hidden', + ], + 'searchFields' => 'title', + 'iconfile' => 'EXT:events/Resources/Public/Icons/tx_events_domain_model_import.svg' + ], + 'types' => [ + '1' => [ + 'showitem' => 'title, hidden, --div--;LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.div.typo3, --palette--;;typo3_storage, --palette--;;categories, --palette--;;relations, --div--;LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.div.rest, rest_experience, rest_search_query' + ], + ], + 'palettes' => [ + 'typo3_storage' => [ + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.palette.typo3_storage', + 'showitem' => 'storage_pid, files_folder' + ], + 'categories' => [ + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.palette.categories', + 'showitem' => 'category_parent, categories_pid' + ], + 'relations' => [ + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.palette.relations', + 'showitem' => 'region' + ], + ], + 'columns' => [ + 'hidden' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.visible', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => true + ] + ], + ], + ], + + 'title' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.title', + 'description' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.title.description', + 'config' => [ + 'type' => 'input', + 'size' => 50, + 'max' => 255, + ], + ], + 'storage_pid' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.storage_pid', + 'description' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.storage_pid.description', + 'config' => [ + 'type' => 'group', + 'internal_type' => 'db', + 'allowed' => 'pages', + 'size' => 1, + 'maxitems' => 1, + 'minitems' => 1, + ], + ], + 'region' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.region', + 'config' => [ + 'type' => 'group', + 'internal_type' => 'db', + 'allowed' => 'tx_events_domain_model_region', + 'size' => 1, + 'maxitems' => 1, + 'minitems' => 0, + ], + ], + 'categories_pid' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.categories_pid', + 'description' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.categories_pid.description', + 'config' => [ + 'type' => 'group', + 'internal_type' => 'db', + 'allowed' => 'pages', + 'size' => 1, + 'maxitems' => 1, + 'minitems' => 0, + ], + ], + 'category_parent' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.category_parent', + 'description' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.category_parent.description', + 'config' => [ + 'type' => 'group', + 'internal_type' => 'db', + 'allowed' => 'sys_category', + 'size' => 1, + 'maxitems' => 1, + 'minitems' => 0, + ], + ], + 'files_folder' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.files_folder', + 'description' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.files_folder.description', + 'config' => [ + 'type' => 'group', + 'internal_type' => 'folder', + 'size' => 1, + 'maxitems' => 1, + 'minitems' => 1, + ], + ], + + 'rest_experience' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.rest_experience', + 'description' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.rest_experience.description', + 'config' => [ + 'type' => 'input', + 'size' => 50, + 'max' => 255, + ], + ], + 'rest_search_query' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.rest_search_query', + 'description' => 'LLL:EXT:events/Resources/Private/Language/locallang_csh_import.xlf:tx_events_domain_model_import.rest_search_query.description', + 'config' => [ + 'type' => 'input', + 'size' => 50, + 'max' => 255, + ], + ], + ], +]; diff --git a/Documentation/Changelog/2.3.0.rst b/Documentation/Changelog/2.3.0.rst index fc0ea8e..691def1 100644 --- a/Documentation/Changelog/2.3.0.rst +++ b/Documentation/Changelog/2.3.0.rst @@ -9,6 +9,12 @@ Nothing Features -------- +* Import configuration can now be configured as database records within TYPO3 folders. + This allows for better support (e.g. permission of backend users and validation and wizards like page selection). + Two new import commands are added. + One allows to process all available import configurations. + The other allows to process a single import configuration which needs to be configured. + * Add new option ``query`` to ``DestinationDataImportCommand``. It is appended as ``q`` parameter to the initial search URL to fetch data for import. The value is documented at https://developer.et4.de/reference/current/#eT4META-search-param-q.html. diff --git a/Resources/Private/Language/locallang_csh_import.xlf b/Resources/Private/Language/locallang_csh_import.xlf new file mode 100644 index 0000000..6419e68 --- /dev/null +++ b/Resources/Private/Language/locallang_csh_import.xlf @@ -0,0 +1,71 @@ + + + +
+ + + Import Configuration + + + TYPO3 + + + REST + + + Relations + + + TYPO3 Storages + + + Categories + + + Title + + + Only for backend search and UX, not used for actual import. + + + Storage Page + + + TYPO3 page to use for storing imported data. + + + Region + + + Categories Storage Folder + + + TYPO3 page to use for storing imported categories. + + + Parent Category + + + Existing TYPO3 category used as parent for all categories created during import. + + + Folder + + + TYPO3 folder to use for storing imported files. + + + Experience + + + See: https://developer.et4.de/reference/current/#eT4META-search-param-experience.html (todo check, was not loadable) + + + Search Query + + + See: https://developer.et4.de/reference/current/#eT4META-search-param-q.html (todo check, was not loadable) + + + + diff --git a/Resources/Public/Icons/tx_events_domain_model_import.svg b/Resources/Public/Icons/tx_events_domain_model_import.svg new file mode 100644 index 0000000..f0baaf6 --- /dev/null +++ b/Resources/Public/Icons/tx_events_domain_model_import.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Tests/Functional/Import/DestinationDataTest/AbstractTest.php b/Tests/Functional/Import/DestinationDataTest/AbstractTest.php index b1fdaa4..84717b3 100644 --- a/Tests/Functional/Import/DestinationDataTest/AbstractTest.php +++ b/Tests/Functional/Import/DestinationDataTest/AbstractTest.php @@ -14,6 +14,10 @@ use Wrm\Events\Tests\ClientFactory; abstract class AbstractTest extends FunctionalTestCase { + protected $coreExtensionsToLoad = [ + 'filelist', + ]; + protected $testExtensionsToLoad = [ 'typo3conf/ext/events', ]; @@ -65,9 +69,11 @@ abstract class AbstractTest extends FunctionalTestCase return $requests; } - protected function executeCommand(array $argumentsAndOptions): CommandTester - { - $subject = $this->getContainer()->get(DestinationDataImportCommand::class); + protected function executeCommand( + array $argumentsAndOptions, + string $command = DestinationDataImportCommand::class + ): CommandTester { + $subject = $this->getContainer()->get($command); self::assertInstanceOf(Command::class, $subject); $tester = new CommandTester($subject); diff --git a/Tests/Functional/Import/DestinationDataTest/Assertions/ImportsAllConfigurationTest.csv b/Tests/Functional/Import/DestinationDataTest/Assertions/ImportsAllConfigurationTest.csv new file mode 100644 index 0000000..e492273 --- /dev/null +++ b/Tests/Functional/Import/DestinationDataTest/Assertions/ImportsAllConfigurationTest.csv @@ -0,0 +1,67 @@ +"tx_events_domain_model_organizer",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,"uid","pid","cruser_id","deleted","hidden","starttime","endtime","sys_language_uid","l10n_parent","t3ver_oid","t3ver_wsid","t3ver_state","t3ver_stage","name","street","district","city","zip","phone","web","email",,,,,,,,,,,,,,,,,,,,,, +,"1","2","0","0","0","0","0","-1","0","0","0","0","0","Schillerhaus Rudolstadt","Schillerstraße 25",,"Rudolstadt","07407","+ 49 3672 / 486470","http://schillerhaus.rudolstadt.de","schillerhaus@rudolstadt.de",,,,,,,,,,,,,,,,,,,,,, +,"2","2","0","0","0","0","0","-1","0","0","0","0","0","Stadtbibliothek Rudolstadt","Schulplatz 13",,"Rudolstadt","07407","0 36 72 - 48 64 20","http://www.stadtbibliothek-rudolstadt.de ","stadtbibliothek@rudolstadt.de",,,,,,,,,,,,,,,,,,,,,, +,"3","2","0","0","0","0","0","-1","0","0","0","0","0","Lutherkirche","Caspar-Schulte-Straße",,"Rudolstadt","07407","03672 - 48 96 13",,,,,,,,,,,,,,,,,,,,,,,, +,"4","3","0","0","0","0","0","-1","0","0","0","0","0","Schillerhaus Rudolstadt","Schillerstraße 25",,"Rudolstadt","07407","+ 49 3672 / 486470","http://schillerhaus.rudolstadt.de","schillerhaus@rudolstadt.de",,,,,,,,,,,,,,,,,,,,,, +,"5","3","0","0","0","0","0","-1","0","0","0","0","0","Stadtbibliothek Rudolstadt","Schulplatz 13",,"Rudolstadt","07407","0 36 72 - 48 64 20","http://www.stadtbibliothek-rudolstadt.de ","stadtbibliothek@rudolstadt.de",,,,,,,,,,,,,,,,,,,,,, +,"6","3","0","0","0","0","0","-1","0","0","0","0","0","Lutherkirche","Caspar-Schulte-Straße",,"Rudolstadt","07407","03672 - 48 96 13",,,,,,,,,,,,,,,,,,,,,,,, +"tx_events_domain_model_event",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,"uid","pid","cruser_id","deleted","hidden","starttime","endtime","sys_language_uid","l10n_parent","t3ver_oid","t3ver_wsid","t3ver_state","t3ver_stage","title","subtitle","global_id","slug","highlight","teaser","details","price_info","name","street","district","city","zip","country","web","phone","ticket","facebook","youtube","instagram","latitude","longitude","images","categories","pages","dates","organizer","partner","region","references_events" +,"1","2","0","0","0","0","0","-1","0","0","0","0","0","Allerlei Weihnachtliches (Heute mit Johannes Geißer)",,"e_100347853","e-100347853","0",,"Die Lichter sind entzündet, die Plätzchen duften, man rückt endlich wieder näher zusammen und lauscht den Geschichten. Vier Schauspieler*innen unseres Theaters überraschen mit ihren weihnachtlichen Texten, die sie für uns ausgewählt haben. Dazu plaudern sie über persönliche Anekdoten und erinnern sich an ihre schönsten und verrücktesten Weihnachtsfeste. Und da der Genuss in der Vorweihnachtszeit nicht fehlen darf, wird an jedem Adventssonntag eine andere weihnachtliche Spezialität serviert. +Eintritt: 10 € (inkl. Gedeck mit weihnachtlicher Schillerlocke) +Um Voranmeldung unter 03672-486470 oder schillerhaus@rudolstadt.de wird gebeten. +Es gilt die 2G-PLUS-Regel.",,"Schillerhaus Rudolstadt","Schillerstraße 25",,"Rudolstadt","07407","Deutschland","http://www.schillerhaus.rudolstadt.de/","+ 49 3672 / 486470",,,,,"50.720971023259","11.335229873657","1","1",,"1","1",,"1", +,"2","2","0","0","0","0","0","-1","0","0","0","0","0","Tüftlerzeit",,"e_100354481","e-100354481","0",,"Die Tüftlerzeit wird dieses Mal ein weihnachtliches Angebot bereithalten. Alle kleinen Tüftler dürfen gespannt sein. +Voranmeldung über: kinderbibliothek@rudolstadt.de oder 03672-486420 + +Bitte beachten Sie die derzeit geltenden Zugangsregeln.",,"Stadtbibliothek Rudolstadt","Schulplatz 13",,"Rudolstadt","07407","Deutschland","http://www.stadtbibliothek-rudolstadt.de/","0 36 72 - 48 64 20",,,,,"50.720835175056","11.342568397522","1","1",,"3","2",,"1", +,"3","2","0","0","0","0","0","-1","0","0","0","0","0","Adventliche Orgelmusik (Orgel: KMD Frank Bettenhausen)",,"e_100350503","e-100350503","0",,"Immer mittwochs in der Adventszeit spielt Frank Bettenhausen solo und zusammen mit anderen Musikern auf der Steinmeyerorgel aus dem Jahr 1906. Bekannte Adventslieder, barocke und romantische Kompositionen stehen neben besinnlichen Texten von Pfarrer Johannes-Martin Weiss. + +Es gilt die 2G-PLUS-Regel.",,"Lutherkirche","Caspar-Schulte-Straße",,"Rudolstadt","07407","Deutschland",,"03672 - 48 96 13",,,,,"50.718688721183","11.327333450317","1","2",,"4","3",,"1", +,"4","3","0","0","0","0","0","-1","0","0","0","0","0","Allerlei Weihnachtliches (Heute mit Johannes Geißer)",,"e_100347853","e-100347853","0",,"Die Lichter sind entzündet, die Plätzchen duften, man rückt endlich wieder näher zusammen und lauscht den Geschichten. Vier Schauspieler*innen unseres Theaters überraschen mit ihren weihnachtlichen Texten, die sie für uns ausgewählt haben. Dazu plaudern sie über persönliche Anekdoten und erinnern sich an ihre schönsten und verrücktesten Weihnachtsfeste. Und da der Genuss in der Vorweihnachtszeit nicht fehlen darf, wird an jedem Adventssonntag eine andere weihnachtliche Spezialität serviert. +Eintritt: 10 € (inkl. Gedeck mit weihnachtlicher Schillerlocke) +Um Voranmeldung unter 03672-486470 oder schillerhaus@rudolstadt.de wird gebeten. +Es gilt die 2G-PLUS-Regel.",,"Schillerhaus Rudolstadt","Schillerstraße 25",,"Rudolstadt","07407","Deutschland","http://www.schillerhaus.rudolstadt.de/","+ 49 3672 / 486470",,,,,"50.720971023259","11.335229873657","1","1",,"1","4",,"1", +,"5","3","0","0","0","0","0","-1","0","0","0","0","0","Tüftlerzeit",,"e_100354481","e-100354481","0",,"Die Tüftlerzeit wird dieses Mal ein weihnachtliches Angebot bereithalten. Alle kleinen Tüftler dürfen gespannt sein. +Voranmeldung über: kinderbibliothek@rudolstadt.de oder 03672-486420 + +Bitte beachten Sie die derzeit geltenden Zugangsregeln.",,"Stadtbibliothek Rudolstadt","Schulplatz 13",,"Rudolstadt","07407","Deutschland","http://www.stadtbibliothek-rudolstadt.de/","0 36 72 - 48 64 20",,,,,"50.720835175056","11.342568397522","1","1",,"3","5",,"1", +,"6","3","0","0","0","0","0","-1","0","0","0","0","0","Adventliche Orgelmusik (Orgel: KMD Frank Bettenhausen)",,"e_100350503","e-100350503","0",,"Immer mittwochs in der Adventszeit spielt Frank Bettenhausen solo und zusammen mit anderen Musikern auf der Steinmeyerorgel aus dem Jahr 1906. Bekannte Adventslieder, barocke und romantische Kompositionen stehen neben besinnlichen Texten von Pfarrer Johannes-Martin Weiss. + +Es gilt die 2G-PLUS-Regel.",,"Lutherkirche","Caspar-Schulte-Straße",,"Rudolstadt","07407","Deutschland",,"03672 - 48 96 13",,,,,"50.718688721183","11.327333450317","1","2",,"4","6",,"1", +"tx_events_domain_model_date",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,"uid","pid","cruser_id","hidden","starttime","endtime","sys_language_uid","l10n_parent","t3ver_oid","t3ver_wsid","t3ver_state","event","start","end","canceled","postponed_date","canceled_link",,,,,,,,,,,,,,,,,,,,,,,,,, +,"1","2","0","0","0","0",-1,0,"0","0","0","1","4101372000","4101377400","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"2","2","0","0","0","0",-1,0,"0","0","0","2","4101112800","4101118200","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"3","2","0","0","0","0",-1,0,"0","0","0","2","4072600800","4072608000","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"4","2","0","0","0","0",-1,0,"0","0","0","2","4075020000","4075027200","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"5","2","0","0","0","0",-1,0,"0","0","0","3","4099831200","4099834800","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"6","2","0","0","0","0",-1,0,"0","0","0","3","4100436000","4100439600","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"7","2","0","0","0","0",-1,0,"0","0","0","3","4101040800","4101044400","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"8","2","0","0","0","0",-1,0,"0","0","0","3","4101645600","4101649200","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"9","3","0","0","0","0",-1,0,"0","0","0","4","4101372000","4101377400","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"10","3","0","0","0","0",-1,0,"0","0","0","5","4101112800","4101118200","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"11","3","0","0","0","0",-1,0,"0","0","0","5","4072600800","4072608000","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"12","3","0","0","0","0",-1,0,"0","0","0","5","4075020000","4075027200","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"13","3","0","0","0","0",-1,0,"0","0","0","6","4099831200","4099834800","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"14","3","0","0","0","0",-1,0,"0","0","0","6","4100436000","4100439600","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"15","3","0","0","0","0",-1,0,"0","0","0","6","4101040800","4101044400","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +,"16","3","0","0","0","0",-1,0,"0","0","0","6","4101645600","4101649200","no","0",,,,,,,,,,,,,,,,,,,,,,,,,,, +"sys_category",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,"uid","pid","cruser_id","hidden","starttime","endtime","sys_language_uid","l10n_parent","title","items","parent",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,1,2,0,0,0,0,0,0,"Top Category",0,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,2,2,0,0,0,0,0,0,"Event Category Parent",0,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,3,2,0,0,0,0,0,0,"Weihnachten",0,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,4,2,0,0,0,0,0,0,"Kinder",0,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,5,2,0,0,0,0,0,0,"Konzerte, Festivals, Show & Tanz",0,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +"sys_category_record_mm",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,"uid_local","uid_foreign","tablenames","fieldname",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,3,1,"tx_events_domain_model_event","categories",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,4,2,"tx_events_domain_model_event","categories",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,5,3,"tx_events_domain_model_event","categories",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,3,3,"tx_events_domain_model_event","categories",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,3,4,"tx_events_domain_model_event","categories",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,4,5,"tx_events_domain_model_event","categories",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,5,6,"tx_events_domain_model_event","categories",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,3,6,"tx_events_domain_model_event","categories",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/Tests/Functional/Import/DestinationDataTest/Fixtures/SecondImportConfiguration.xml b/Tests/Functional/Import/DestinationDataTest/Fixtures/SecondImportConfiguration.xml new file mode 100644 index 0000000..b0c3a41 --- /dev/null +++ b/Tests/Functional/Import/DestinationDataTest/Fixtures/SecondImportConfiguration.xml @@ -0,0 +1,25 @@ + + + + 2 + 2 + Second Example import configuration + + 3 + 1:/staedte/anderestadt/events/ + 2 + 2 + + 1 + + anderestadt + name:"Beispiel2" + + + + 1 + 3 + Storage + 254 + + diff --git a/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleImportConfiguration.xml b/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleImportConfiguration.xml new file mode 100644 index 0000000..814f478 --- /dev/null +++ b/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleImportConfiguration.xml @@ -0,0 +1,18 @@ + + + + 1 + 2 + Example import configuration + + 2 + 1:/staedte/beispielstadt/events/ + 2 + 2 + + 1 + + beispielstadt + name:"Beispiel" + + diff --git a/Tests/Functional/Import/DestinationDataTest/ImportCleansTransientFilesTest.php b/Tests/Functional/Import/DestinationDataTest/ImportCleansTransientFilesTest.php index eacfeb1..a3a6532 100644 --- a/Tests/Functional/Import/DestinationDataTest/ImportCleansTransientFilesTest.php +++ b/Tests/Functional/Import/DestinationDataTest/ImportCleansTransientFilesTest.php @@ -16,7 +16,10 @@ class ImportCleansTransientFilesTest extends AbstractTest */ public function cleansTransientFiles(): void { - $fileImportPath = 'staedte/beispielstadt/events/'; + $fileImportPathConfiguration = 'staedte/beispielstadt/events/'; + $fileImportPath = $this->getInstancePath() . '/fileadmin/' . $fileImportPathConfiguration; + GeneralUtility::mkdir_deep($fileImportPath); + $this->setUpConfiguration([ 'restUrl = https://example.com/some-path/', 'license = example-license', @@ -38,7 +41,7 @@ class ImportCleansTransientFilesTest extends AbstractTest $tester = $this->executeCommand([ 'storage-pid' => '2', 'rest-experience' => 'beispielstadt', - 'files-folder' => $fileImportPath, + 'files-folder' => $fileImportPathConfiguration, ]); self::assertSame(0, $tester->getStatusCode()); @@ -49,7 +52,7 @@ class ImportCleansTransientFilesTest extends AbstractTest self::assertSame('https://dam.destination.one/828118/f13bbf5602ffc406ebae2faa3527654dea84194666bce4925a1ca8bd3f50c5e9/tueftlerzeit-sfz-rudolstadt-jpg.jpg', (string)$requests[2]['request']->getUri()); self::assertSame('https://dam.destination.one/853436/109ac1cf87913e21b5e2b0ef0cc63d223a14374364952a855746a8e7c3fcfc36/lutherkirche-jpg.jpg', (string)$requests[3]['request']->getUri()); - $importedFiles = GeneralUtility::getFilesInDir($this->getInstancePath() . '/fileadmin/' . $fileImportPath); + $importedFiles = GeneralUtility::getFilesInDir($fileImportPath); self::assertIsArray($importedFiles, 'Failed to retrieve imported files from filesystem.'); self::assertSame( [ diff --git a/Tests/Functional/Import/DestinationDataTest/ImportDoesNotUseUploadsFolderTest.php b/Tests/Functional/Import/DestinationDataTest/ImportDoesNotUseUploadsFolderTest.php index 2bf07a9..e7f928f 100644 --- a/Tests/Functional/Import/DestinationDataTest/ImportDoesNotUseUploadsFolderTest.php +++ b/Tests/Functional/Import/DestinationDataTest/ImportDoesNotUseUploadsFolderTest.php @@ -16,7 +16,9 @@ class ImportDoesNotUseUploadsFolderTest extends AbstractTest */ public function doesNotUseUploadsFolder(): void { - $fileImportPath = 'staedte/beispielstadt/events/'; + $fileImportPathConfiguration = 'staedte/beispielstadt/events/'; + $fileImportPath = $this->getInstancePath() . '/fileadmin/' . $fileImportPathConfiguration; + GeneralUtility::mkdir_deep($fileImportPath); $this->setUpConfiguration([ 'restUrl = https://example.com/some-path/', @@ -39,7 +41,7 @@ class ImportDoesNotUseUploadsFolderTest extends AbstractTest $tester = $this->executeCommand([ 'storage-pid' => '2', 'rest-experience' => 'beispielstadt', - 'files-folder' => $fileImportPath, + 'files-folder' => $fileImportPathConfiguration, ]); self::assertSame(0, $tester->getStatusCode()); @@ -49,7 +51,7 @@ class ImportDoesNotUseUploadsFolderTest extends AbstractTest self::assertSame('https://dam.destination.one/828118/f13bbf5602ffc406ebae2faa3527654dea84194666bce4925a1ca8bd3f50c5e9/tueftlerzeit-sfz-rudolstadt-jpg.jpg', (string)$requests[2]['request']->getUri()); self::assertSame('https://dam.destination.one/853436/109ac1cf87913e21b5e2b0ef0cc63d223a14374364952a855746a8e7c3fcfc36/lutherkirche-jpg.jpg', (string)$requests[3]['request']->getUri()); - $importedFiles = GeneralUtility::getFilesInDir($this->getInstancePath() . '/fileadmin/' . $fileImportPath); + $importedFiles = GeneralUtility::getFilesInDir($fileImportPath); self::assertIsArray($importedFiles, 'Failed to retrieve imported files from filesystem.'); self::assertSame( [ diff --git a/Tests/Functional/Import/DestinationDataTest/ImportsAllConfigurationTest.php b/Tests/Functional/Import/DestinationDataTest/ImportsAllConfigurationTest.php new file mode 100644 index 0000000..5735996 --- /dev/null +++ b/Tests/Functional/Import/DestinationDataTest/ImportsAllConfigurationTest.php @@ -0,0 +1,109 @@ +getInstancePath() . '/fileadmin/' . $fileImportPathConfiguration1; + GeneralUtility::mkdir_deep($fileImportPath1); + + $fileImportPathConfiguration2 = 'staedte/anderestadt/events/'; + $fileImportPath2 = $this->getInstancePath() . '/fileadmin/' . $fileImportPathConfiguration2; + GeneralUtility::mkdir_deep($fileImportPath2); + + $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleRegion.xml'); + $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleCategory.xml'); + $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleImportConfiguration.xml'); + $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SecondImportConfiguration.xml'); + $this->setUpConfiguration([ + 'restUrl = https://example.com/some-path/', + 'license = example-license', + 'restType = Event', + 'restTemplate = ET2014A.json', + 'restLimit = 3', + 'restMode = next_months,12', + ]); + + $requests = &$this->setUpResponses([ + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/Response.json') ?: ''), + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/ExampleImage.jpg') ?: ''), + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/ExampleImage.jpg') ?: ''), + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/ExampleImage.jpg') ?: ''), + + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/Response.json') ?: ''), + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/ExampleImage.jpg') ?: ''), + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/ExampleImage.jpg') ?: ''), + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/ExampleImage.jpg') ?: ''), + ]); + + $tester = $this->executeCommand([], ImportDestinationDataViaAllConfigruationsCommand::class); + + self::assertSame(0, $tester->getStatusCode()); + + self::assertCount(8, $requests, 'Unexpected number of requests were made.'); + self::assertSame('https://example.com/some-path/?experience=beispielstadt&licensekey=example-license&type=Event&mode=next_months%2C12&limit=3&template=ET2014A.json&q=name%3A%22Beispiel%22', (string)$requests[0]['request']->getUri()); + self::assertSame('https://dam.destination.one/849917/279ac45b3fc701a7197131f627164fffd9f8cc77bc75165e2fc2b864ed606920/theater-rudolstadt_johannes-gei-er_photo-by-lisa-stern_web_-jpg.jpg', (string)$requests[1]['request']->getUri()); + self::assertSame('https://dam.destination.one/828118/f13bbf5602ffc406ebae2faa3527654dea84194666bce4925a1ca8bd3f50c5e9/tueftlerzeit-sfz-rudolstadt-jpg.jpg', (string)$requests[2]['request']->getUri()); + self::assertSame('https://dam.destination.one/853436/109ac1cf87913e21b5e2b0ef0cc63d223a14374364952a855746a8e7c3fcfc36/lutherkirche-jpg.jpg', (string)$requests[3]['request']->getUri()); + + self::assertSame('https://example.com/some-path/?experience=anderestadt&licensekey=example-license&type=Event&mode=next_months%2C12&limit=3&template=ET2014A.json&q=name%3A%22Beispiel2%22', (string)$requests[4]['request']->getUri()); + self::assertSame('https://dam.destination.one/849917/279ac45b3fc701a7197131f627164fffd9f8cc77bc75165e2fc2b864ed606920/theater-rudolstadt_johannes-gei-er_photo-by-lisa-stern_web_-jpg.jpg', (string)$requests[5]['request']->getUri()); + self::assertSame('https://dam.destination.one/828118/f13bbf5602ffc406ebae2faa3527654dea84194666bce4925a1ca8bd3f50c5e9/tueftlerzeit-sfz-rudolstadt-jpg.jpg', (string)$requests[6]['request']->getUri()); + self::assertSame('https://dam.destination.one/853436/109ac1cf87913e21b5e2b0ef0cc63d223a14374364952a855746a8e7c3fcfc36/lutherkirche-jpg.jpg', (string)$requests[7]['request']->getUri()); + + self::assertCount( + 0, + $this->getAllRecords('tx_events_domain_model_partner'), + 'Added unexpected partners.' + ); + self::assertCount( + 1, + $this->getAllRecords('tx_events_domain_model_region'), + 'Added or removed unexpected region.' + ); + $this->assertCSVDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Assertions/ImportsAllConfigurationTest.csv'); + + $importedFiles = GeneralUtility::getFilesInDir($fileImportPath1); + self::assertIsArray($importedFiles, 'Failed to retrieve imported files from filesystem.'); + self::assertSame( + [ + 'lutherkirche-jpg.jpg', + 'theater-rudolstadt_johannes-gei-er_photo-by-lisa-stern_web_-jpg.jpg', + 'tueftlerzeit-sfz-rudolstadt-jpg.jpg', + ], + array_values($importedFiles), + 'Got unexpected number of files' + ); + + $importedFiles = GeneralUtility::getFilesInDir($fileImportPath2); + self::assertIsArray($importedFiles, 'Failed to retrieve imported files from filesystem.'); + self::assertSame( + [ + 'lutherkirche-jpg.jpg', + 'theater-rudolstadt_johannes-gei-er_photo-by-lisa-stern_web_-jpg.jpg', + 'tueftlerzeit-sfz-rudolstadt-jpg.jpg', + ], + array_values($importedFiles), + 'Got unexpected number of files' + ); + + self::assertFileEquals( + __DIR__ . '/Assertions/EmptyLogFile.txt', + $this->getInstancePath() . '/typo3temp/var/log/typo3_0493d91d8e.log', + 'Logfile was not empty.' + ); + } +} diff --git a/Tests/Functional/Import/DestinationDataTest/ImportsExampleAsExpectedTest.php b/Tests/Functional/Import/DestinationDataTest/ImportsExampleAsExpectedTest.php index 413a580..74c847c 100644 --- a/Tests/Functional/Import/DestinationDataTest/ImportsExampleAsExpectedTest.php +++ b/Tests/Functional/Import/DestinationDataTest/ImportsExampleAsExpectedTest.php @@ -15,7 +15,9 @@ class ImportsExampleAsExpectedTest extends AbstractTest */ public function importsExampleAsExpected(): void { - $fileImportPath = 'staedte/beispielstadt/events/'; + $fileImportPathConfiguration = 'staedte/beispielstadt/events/'; + $fileImportPath = $this->getInstancePath() . '/fileadmin/' . $fileImportPathConfiguration; + GeneralUtility::mkdir_deep($fileImportPath); $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleRegion.xml'); $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleCategory.xml'); @@ -40,7 +42,7 @@ class ImportsExampleAsExpectedTest extends AbstractTest $tester = $this->executeCommand([ 'storage-pid' => '2', 'rest-experience' => 'beispielstadt', - 'files-folder' => $fileImportPath, + 'files-folder' => $fileImportPathConfiguration, 'region-uid' => '1', ]); @@ -64,7 +66,7 @@ class ImportsExampleAsExpectedTest extends AbstractTest ); $this->assertCSVDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Assertions/ImportsExampleAsExpected.csv'); - $importedFiles = GeneralUtility::getFilesInDir($this->getInstancePath() . '/fileadmin/' . $fileImportPath); + $importedFiles = GeneralUtility::getFilesInDir($fileImportPath); self::assertIsArray($importedFiles, 'Failed to retrieve imported files from filesystem.'); self::assertSame( [ diff --git a/Tests/Functional/Import/DestinationDataTest/ImportsSingleConfigurationTest.php b/Tests/Functional/Import/DestinationDataTest/ImportsSingleConfigurationTest.php new file mode 100644 index 0000000..da35ccf --- /dev/null +++ b/Tests/Functional/Import/DestinationDataTest/ImportsSingleConfigurationTest.php @@ -0,0 +1,84 @@ +getInstancePath() . '/fileadmin/' . $fileImportPathConfiguration; + GeneralUtility::mkdir_deep($fileImportPath); + + $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleRegion.xml'); + $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleCategory.xml'); + $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleImportConfiguration.xml'); + $this->setUpConfiguration([ + 'restUrl = https://example.com/some-path/', + 'license = example-license', + 'restType = Event', + 'restTemplate = ET2014A.json', + 'restLimit = 3', + 'restMode = next_months,12', + ]); + + $requests = &$this->setUpResponses([ + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/Response.json') ?: ''), + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/ExampleImage.jpg') ?: ''), + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/ExampleImage.jpg') ?: ''), + new Response(200, [], file_get_contents(__DIR__ . '/Fixtures/ExampleImage.jpg') ?: ''), + ]); + + $tester = $this->executeCommand([ + 'configurationUid' => '1', + ], ImportDestinationDataViaConfigruationCommand::class); + + self::assertSame(0, $tester->getStatusCode()); + + self::assertCount(4, $requests, 'Unexpected number of requests were made.'); + self::assertSame('https://example.com/some-path/?experience=beispielstadt&licensekey=example-license&type=Event&mode=next_months%2C12&limit=3&template=ET2014A.json&q=name%3A%22Beispiel%22', (string)$requests[0]['request']->getUri()); + self::assertSame('https://dam.destination.one/849917/279ac45b3fc701a7197131f627164fffd9f8cc77bc75165e2fc2b864ed606920/theater-rudolstadt_johannes-gei-er_photo-by-lisa-stern_web_-jpg.jpg', (string)$requests[1]['request']->getUri()); + self::assertSame('https://dam.destination.one/828118/f13bbf5602ffc406ebae2faa3527654dea84194666bce4925a1ca8bd3f50c5e9/tueftlerzeit-sfz-rudolstadt-jpg.jpg', (string)$requests[2]['request']->getUri()); + self::assertSame('https://dam.destination.one/853436/109ac1cf87913e21b5e2b0ef0cc63d223a14374364952a855746a8e7c3fcfc36/lutherkirche-jpg.jpg', (string)$requests[3]['request']->getUri()); + + self::assertCount( + 0, + $this->getAllRecords('tx_events_domain_model_partner'), + 'Added unexpected partners.' + ); + self::assertCount( + 1, + $this->getAllRecords('tx_events_domain_model_region'), + 'Added or removed unexpected region.' + ); + $this->assertCSVDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Assertions/ImportsExampleAsExpected.csv'); + + $importedFiles = GeneralUtility::getFilesInDir($fileImportPath); + self::assertIsArray($importedFiles, 'Failed to retrieve imported files from filesystem.'); + self::assertSame( + [ + 'lutherkirche-jpg.jpg', + 'theater-rudolstadt_johannes-gei-er_photo-by-lisa-stern_web_-jpg.jpg', + 'tueftlerzeit-sfz-rudolstadt-jpg.jpg', + ], + array_values($importedFiles), + 'Got unexpected number of files' + ); + + self::assertFileEquals( + __DIR__ . '/Assertions/EmptyLogFile.txt', + $this->getInstancePath() . '/typo3temp/var/log/typo3_0493d91d8e.log', + 'Logfile was not empty.' + ); + } +} diff --git a/Tests/Functional/Import/DestinationDataTest/ImportsWithoutCategoryIfNotProvidedTest.php b/Tests/Functional/Import/DestinationDataTest/ImportsWithoutCategoryIfNotProvidedTest.php index 54dddfe..3c630d7 100644 --- a/Tests/Functional/Import/DestinationDataTest/ImportsWithoutCategoryIfNotProvidedTest.php +++ b/Tests/Functional/Import/DestinationDataTest/ImportsWithoutCategoryIfNotProvidedTest.php @@ -15,7 +15,9 @@ class ImportsWithoutCategoryIfNotProvidedTest extends AbstractTest */ public function importsWithoutCategoryIfNotProvided(): void { - $fileImportPath = 'staedte/beispielstadt/events/'; + $fileImportPathConfiguration = 'staedte/beispielstadt/events/'; + $fileImportPath = $this->getInstancePath() . '/fileadmin/' . $fileImportPathConfiguration; + GeneralUtility::mkdir_deep($fileImportPath); $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleRegion.xml'); $this->setUpConfiguration([ @@ -39,7 +41,7 @@ class ImportsWithoutCategoryIfNotProvidedTest extends AbstractTest $tester = $this->executeCommand([ 'storage-pid' => '2', 'rest-experience' => 'beispielstadt', - 'files-folder' => $fileImportPath, + 'files-folder' => $fileImportPathConfiguration, 'region-uid' => '1', ]); @@ -69,7 +71,7 @@ class ImportsWithoutCategoryIfNotProvidedTest extends AbstractTest $this->assertCSVDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Assertions/ImportsWithoutCategoryIfNotProvided.csv'); - $importedFiles = GeneralUtility::getFilesInDir($this->getInstancePath() . '/fileadmin/' . $fileImportPath); + $importedFiles = GeneralUtility::getFilesInDir($fileImportPath); self::assertIsArray($importedFiles, 'Failed to retrieve imported files from filesystem.'); self::assertSame( [ diff --git a/Tests/Functional/Import/DestinationDataTest/ImportsWithoutRegionIfNotProvidedTest.php b/Tests/Functional/Import/DestinationDataTest/ImportsWithoutRegionIfNotProvidedTest.php index 90519da..250e883 100644 --- a/Tests/Functional/Import/DestinationDataTest/ImportsWithoutRegionIfNotProvidedTest.php +++ b/Tests/Functional/Import/DestinationDataTest/ImportsWithoutRegionIfNotProvidedTest.php @@ -15,7 +15,10 @@ class ImportsWithoutRegionIfNotProvidedTest extends AbstractTest */ public function importsWithoutRegionIfNotProvided(): void { - $fileImportPath = 'staedte/beispielstadt/events/'; + $fileImportPathConfiguration = 'staedte/beispielstadt/events/'; + $fileImportPath = $this->getInstancePath() . '/fileadmin/' . $fileImportPathConfiguration; + GeneralUtility::mkdir_deep($fileImportPath); + $this->importDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Fixtures/SingleCategory.xml'); $this->setUpConfiguration([ 'restUrl = https://example.com/some-path/', @@ -37,7 +40,7 @@ class ImportsWithoutRegionIfNotProvidedTest extends AbstractTest $tester = $this->executeCommand([ 'storage-pid' => '2', 'rest-experience' => 'beispielstadt', - 'files-folder' => $fileImportPath, + 'files-folder' => $fileImportPathConfiguration, 'region-uid' => '', ]); @@ -62,7 +65,7 @@ class ImportsWithoutRegionIfNotProvidedTest extends AbstractTest $this->assertCSVDataSet('EXT:events/Tests/Functional/Import/DestinationDataTest/Assertions/ImportsWithoutRegionIfNotProvided.csv'); - $importedFiles = GeneralUtility::getFilesInDir($this->getInstancePath() . '/fileadmin/' . $fileImportPath); + $importedFiles = GeneralUtility::getFilesInDir($fileImportPath); self::assertIsArray($importedFiles, 'Failed to retrieve imported files from filesystem.'); self::assertSame( [ diff --git a/Tests/Unit/Domain/DestinationData/ImportTest.php b/Tests/Unit/Domain/DestinationData/ImportTest.php deleted file mode 100644 index b31e18b..0000000 --- a/Tests/Unit/Domain/DestinationData/ImportTest.php +++ /dev/null @@ -1,126 +0,0 @@ -getRestExperience() - ); - } - - /** - * @test - */ - public function returnsStoragePid(): void - { - $subject = new Import( - '', - 20, - null, - '', - '' - ); - - self::assertSame( - 20, - $subject->getStoragePid() - ); - } - - /** - * @test - */ - public function returnsRegionUid(): void - { - $subject = new Import( - '', - 0, - 30, - '', - '' - ); - - self::assertSame( - 30, - $subject->getRegionUid() - ); - } - - /** - * @test - */ - public function returnsFilesFolder(): void - { - $subject = new Import( - '', - 0, - null, - 'test/folder', - '' - ); - - self::assertSame( - 'test/folder', - $subject->getFilesFolder() - ); - } - - /** - * @test - */ - public function returnsSearchQuery(): void - { - $subject = new Import( - '', - 0, - null, - 'test/folder', - 'name:"Test"' - ); - - self::assertSame( - 'name:"Test"', - $subject->getSearchQuery() - ); - } -} diff --git a/Tests/Unit/Domain/Model/ImportTest.php b/Tests/Unit/Domain/Model/ImportTest.php new file mode 100644 index 0000000..9c615b7 --- /dev/null +++ b/Tests/Unit/Domain/Model/ImportTest.php @@ -0,0 +1,183 @@ +prophesize(Folder::class); + + $subject = new Import( + $folder->reveal(), + 0, + '' + ); + + self::assertInstanceOf( + Import::class, + $subject + ); + } + + /** + * @test + */ + public function returnsRestExperience(): void + { + $folder = $this->prophesize(Folder::class); + + $subject = new Import( + $folder->reveal(), + 0, + 'experience' + ); + + self::assertSame( + 'experience', + $subject->getRestExperience() + ); + } + + /** + * @test + */ + public function returnsStoragePid(): void + { + $folder = $this->prophesize(Folder::class); + + $subject = new Import( + $folder->reveal(), + 20, + '' + ); + + self::assertSame( + 20, + $subject->getStoragePid() + ); + } + + /** + * @test + */ + public function returnsRegion(): void + { + $folder = $this->prophesize(Folder::class); + $region = $this->prophesize(Region::class); + + $subject = new Import( + $folder->reveal(), + 0, + '', + '', + 0, + null, + $region->reveal() + ); + + self::assertSame( + $region->reveal(), + $subject->getRegion() + ); + } + + /** + * @test + */ + public function returnsFilesFolder(): void + { + $folder = $this->prophesize(Folder::class); + + $subject = new Import( + $folder->reveal(), + 0, + '' + ); + + self::assertSame( + $folder->reveal(), + $subject->getFilesFolder() + ); + } + + /** + * @test + */ + public function returnsCategoriesPid(): void + { + $folder = $this->prophesize(Folder::class); + + $subject = new Import( + $folder->reveal(), + 0, + '', + '', + 10 + ); + + self::assertSame( + 10, + $subject->getCategoriesPid() + ); + } + + /** + * @test + */ + public function returnsCategoryParent(): void + { + $category = $this->prophesize(Category::class); + $folder = $this->prophesize(Folder::class); + + $subject = new Import( + $folder->reveal(), + 0, + '', + '', + 0, + $category->reveal() + ); + + self::assertSame( + $category->reveal(), + $subject->getCategoryParent() + ); + } + + /** + * @test + */ + public function returnsSearchQuery(): void + { + $folder = $this->prophesize(Folder::class); + + $subject = new Import( + $folder->reveal(), + 0, + '', + 'name:"Test"' + ); + + self::assertSame( + 'name:"Test"', + $subject->getSearchQuery() + ); + } +} diff --git a/Tests/Unit/Service/DestinationDataImportService/UrlFactoryTest.php b/Tests/Unit/Service/DestinationDataImportService/UrlFactoryTest.php index 5f2ed94..d45a03a 100644 --- a/Tests/Unit/Service/DestinationDataImportService/UrlFactoryTest.php +++ b/Tests/Unit/Service/DestinationDataImportService/UrlFactoryTest.php @@ -6,7 +6,7 @@ use PHPUnit\Framework\TestCase; use Prophecy\Prophecy\ObjectProphecy; use TYPO3\CMS\Extbase\Configuration\ConfigurationManager; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; -use Wrm\Events\Domain\DestinationData\Import; +use Wrm\Events\Domain\Model\Import; use Wrm\Events\Service\DestinationDataImportService\UrlFactory; use Wrm\Events\Tests\ProphecyTrait; diff --git a/composer.json b/composer.json index 2d57d84..20d99a4 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "typo3/cms-core": "^10.4 || ^11.5", "typo3/cms-extbase": "^10.4 || ^11.5", "typo3/cms-fluid": "^10.4 || ^11.5", - "typo3/cms-frontend": "^10.4 || ^11.5" + "typo3/cms-frontend": "^10.4 || ^11.5", + "typo3/cms-filelist": "^10.4 || ^11.5" }, "autoload": { "psr-4": { diff --git a/ext_tables.sql b/ext_tables.sql index 5ec72fb..bc02db3 100644 --- a/ext_tables.sql +++ b/ext_tables.sql @@ -96,3 +96,18 @@ CREATE TABLE tx_events_domain_model_date ( canceled_link varchar(1024) DEFAULT '' NOT NULL, ); + +CREATE TABLE tx_events_domain_model_import ( + title varchar(1024) DEFAULT '' NOT NULL, + + storage_pid int(11) unsigned DEFAULT '0' NOT NULL, + files_folder varchar(1024) DEFAULT '' NOT NULL, + + categories_pid int(11) unsigned DEFAULT '0' NOT NULL, + category_parent int(11) unsigned DEFAULT '0' NOT NULL, + + region int(11) unsigned DEFAULT '0' NOT NULL, + + rest_experience varchar(1024) DEFAULT '' NOT NULL, + rest_search_query varchar(1024) DEFAULT '' NOT NULL, +); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index f8a985f..36722d6 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,19 +1,14 @@ parameters: ignoreErrors: - - message: "#^Parameter \\#1 \\$restExperience of class Wrm\\\\Events\\\\Domain\\\\DestinationData\\\\Import constructor expects string, mixed given\\.$#" + message: "#^Parameter \\#2 \\$array of function array_map expects array, mixed given\\.$#" count: 1 - path: Classes/Command/DestinationDataImportCommand.php + path: Classes/Domain/DestinationData/ImportFactory.php - - message: "#^Parameter \\#2 \\$storagePid of class Wrm\\\\Events\\\\Domain\\\\DestinationData\\\\Import constructor expects int, mixed given\\.$#" + message: "#^Parameter \\#1 \\$min \\(999\\) of function random_int expects lower number than parameter \\#2 \\$max \\(int\\<1, max\\>\\)\\.$#" count: 1 - path: Classes/Command/DestinationDataImportCommand.php - - - - message: "#^Parameter \\#4 \\$filesFolder of class Wrm\\\\Events\\\\Domain\\\\DestinationData\\\\Import constructor expects string, mixed given\\.$#" - count: 1 - path: Classes/Command/DestinationDataImportCommand.php + path: Classes/Domain/DestinationData/LegacyImportFactory.php - message: "#^Call to method deleteFile\\(\\) on an unknown class TYPO3\\\\CMS\\\\Core\\\\Resource\\\\Storage\\.$#"