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; // Get Typoscript Settings $this->settings = $this->configurationManager->getConfiguration( ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, 'Events', 'Pi1' ); // Set properties $this->restUrl = $this->settings['destinationData']['restUrl']; $this->restLicenseKey = $this->settings['destinationData']['license']; $this->restType = $this->settings['destinationData']['restType']; $this->restLimit = $this->settings['destinationData']['restLimit']; $this->restMode = $this->settings['destinationData']['restMode']; $this->restTemplate = $this->settings['destinationData']['restTemplate']; $this->categoriesPid = (int) $this->settings['destinationData']['categoriesPid']; $this->categoryParentUid = (int) $this->settings['destinationData']['categoryParentUid']; } public function import( string $restExperience, int $storagePid, ?int $regionUid, string $filesFolder ): int { $this->restExperience = $restExperience; $this->storagePid = $storagePid; $this->regionUid = $regionUid; $this->filesFolder = $filesFolder; // Get configuration $frameworkConfiguration = $this->configurationManager->getConfiguration( ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK ); // Set storage pid $persistenceConfiguration = [ 'persistence' => [ 'storagePid' => $this->storagePid, ], ]; // Set Configuration $this->configurationManager->setConfiguration(array_merge($frameworkConfiguration, $persistenceConfiguration)); $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__); $this->logger->info('Starting Destination Data Import Service'); $restUrl = $this->restUrl . '?experience=' . $this->restExperience . '&licensekey=' . $this->restLicenseKey . '&type=' . $this->restType . '&mode=' . $this->restMode . '&limit=' . $this->restLimit . '&template=' . $this->restTemplate; $this->logger->info('Try to get data from ' . $restUrl); try { $fetchedData = $this->fetchData($restUrl); } catch (\Exception $e) { $this->logger->error('Could not receive data.'); return 1; } return $this->processData($fetchedData); } private function fetchData(string $restUrl): array { $jsonContent = file_get_contents($restUrl); if (is_string($jsonContent) === false) { throw new \Exception('Could not receive data.', 1639495835); } $jsonResponse = json_decode($jsonContent, true); if (is_array($jsonResponse) === false) { throw new \Exception('Could not receive data.', 1639495835); } $this->logger->info('Received data with ' . count($jsonResponse['items']) . ' items'); return $jsonResponse; } public function processData(array $data): int { $this->logger->info('Processing json ' . count($data['items'])); // Get selected region $selectedRegion = null; if (is_int($this->regionUid)) { $selectedRegion = $this->regionRepository->findByUid($this->regionUid); } foreach ($data['items'] as $event) { $this->logger->info('Processing event ' . substr($event['title'], 0, 20)); // Event already exists? If not create one! $this->tmpCurrentEvent = $this->getOrCreateEvent($event['global_id'], $event['title']); // Set language UID $this->tmpCurrentEvent->setLanguageUid(-1); // Set selected Region if ($selectedRegion instanceof Region) { $this->tmpCurrentEvent->setRegion($selectedRegion); } // Set Title $this->tmpCurrentEvent->setTitle(substr($event['title'], 0, 254)); // Set Highlight (Is only set in rest if true) if (isset($event['highlight']) && $event['highlight']) { $this->tmpCurrentEvent->setHighlight($event['highlight']); } // Set Texts if ($event['texts']) { $this->setTexts($event['texts']); } // Set address and geo data if ($event['name'] || $event['street'] || $event['city'] || $event['zip'] || $event['country'] || $event['web']) { $this->setAddress($event); } // Set LatLng if ($event['geo']['main']['latitude'] && $event['geo']['main']['longitude']) { $this->setLatLng($event['geo']['main']['latitude'], $event['geo']['main']['longitude']); } // Set Categories if ($event['categories']) { $this->setCategories($event['categories']); } // Set Organizer if ($event['addresses']) { $this->setOrganizer($event['addresses']); } // Set Social if ($event['media_objects']) { $this->setSocial($event['media_objects']); } // Set Tickets if ($event['media_objects']) { $this->setTickets($event['media_objects']); } // Set Dates if ($event['timeIntervals']) { $this->setDates( $event['timeIntervals'], (bool) $this->getAttributeValue($event, 'DETAILS_ABGESAGT') ); } // Set Assets if ($event['media_objects']) { $this->setAssets($event['media_objects']); } // Update and persist $this->logger->info('Persist database'); $this->eventRepository->update($this->tmpCurrentEvent); $this->persistenceManager->persistAll(); } $this->doSlugUpdate(); $this->logger->info('Finished import'); return 0; } /** * * @param array $categories */ private function setCategories(array $categories): void { if ($this->categoryParentUid === 0) { return; } $sysParentCategory = $this->sysCategoriesRepository->findByUid($this->categoryParentUid); if (!$sysParentCategory instanceof Category) { $this->logger->warning( 'Could not fetch system parent category by uid.', ['uid' => $this->categoryParentUid] ); return; } foreach ($categories as $categoryTitle) { $tmpSysCategory = $this->sysCategoriesRepository->findOneByTitle($categoryTitle); if (!$tmpSysCategory) { $this->logger->info('Creating new category: ' . $categoryTitle); $tmpSysCategory = $this->objectManager->get(Category::class); $tmpSysCategory->setTitle($categoryTitle); $tmpSysCategory->setParent($sysParentCategory); $tmpSysCategory->setPid($this->categoriesPid); $this->sysCategoriesRepository->add($tmpSysCategory); $this->tmpCurrentEvent->addCategory($tmpSysCategory); } else { $this->tmpCurrentEvent->addCategory($tmpSysCategory); } } } private function setDates( array $timeIntervals, bool $canceled ): void { // @TODO: does not seem to work --> //$currentEventDates = $this->tmpCurrentEvent->getDates(); //$this->tmpCurrentEvent->removeAllDates($currentEventDates); // <-- // TODO: Workaround delete dates $currentEventDates = $this->tmpCurrentEvent->getDates(); $this->logger->info('Found ' . count($currentEventDates) . ' to delete'); foreach ($currentEventDates as $currentDate) { $this->dateRepository->remove($currentDate); } $today = new \DateTime('today'); $today = $today->getTimestamp(); foreach ($timeIntervals as $date) { // Check if dates are given as interval or not if (empty($date['interval'])) { if (strtotime($date['start']) > $today) { $this->logger->info('Setup single date'); $start = new \DateTime($date['start'], new \DateTimeZone($date['tz'])); $end = new \DateTime($date['end'], new \DateTimeZone($date['tz'])); $this->logger->info('Start transformed ' . $start->format('Y-m-d H:i')); $this->logger->info('End transformed ' . $end->format('Y-m-d H:i')); $this->tmpCurrentEvent->addDate(Date::createFromDestinationData( $start, $end, $canceled )); } } else { if ($date['freq'] == 'Daily' && empty($date['weekdays']) && !empty($date['repeatUntil'])) { $this->logger->info('Setup daily interval dates'); $this->logger->info('Start ' . $date['start']); $this->logger->info('End ' . $date['repeatUntil']); $start = new \DateTime($date['start'], new \DateTimeZone($date['tz'])); $until = new \DateTime($date['repeatUntil'], new \DateTimeZone($date['tz'])); $i = (int) strtotime($start->format('l'), $start->getTimestamp()); while ($i !== 0 && $i <= $until->getTimestamp()) { $i = (int) strtotime('+1 day', $i); if ($i >= $today) { $eventStart = new \DateTime(); $eventStart->setTimestamp($i); $eventStart->setTime((int) $start->format('H'), (int) $start->format('i')); $eventEnd = new \DateTime(); $eventEnd->setTimestamp($i); $eventEnd->setTime((int) $until->format('H'), (int) $until->format('i')); $this->tmpCurrentEvent->addDate(Date::createFromDestinationData( $eventStart, $eventEnd, $canceled )); } } } elseif ($date['freq'] == 'Weekly' && !empty($date['weekdays']) && !empty($date['repeatUntil'])) { foreach ($date['weekdays'] as $day) { $this->logger->info('Setup weekly interval dates for ' . $day); $this->logger->info('Start ' . $date['start']); $this->logger->info('End ' . $date['repeatUntil']); $start = new \DateTime($date['start'], new \DateTimeZone($date['tz'])); $until = new \DateTime($date['repeatUntil'], new \DateTimeZone($date['tz'])); for ($i = strtotime($day, $start->getTimestamp()); $i <= $until->getTimestamp(); $i = strtotime('+1 week', $i)) { if ($i >= $today) { $eventStart = new \DateTime(); $eventStart->setTimestamp($i); $eventStart->setTime((int) $start->format('H'), (int) $start->format('i')); $eventEnd = new \DateTime(); $eventEnd->setTimestamp($i); $eventEnd->setTime((int) $until->format('H'), (int) $until->format('i')); $this->tmpCurrentEvent->addDate(Date::createFromDestinationData( $eventStart, $eventEnd, $canceled )); } } } } } } $this->logger->info('Finished setup dates'); } private function setOrganizer(array $addresses): void { foreach ($addresses as $address) { if ($address['rel'] == "organizer") { $tmpOrganizer = $this->organizerRepository->findOneByName($address['name']); if ($tmpOrganizer) { $this->tmpCurrentEvent->setOrganizer($tmpOrganizer); continue; } $tmpOrganizer = $this->objectManager->get(Organizer::class); $tmpOrganizer->setLanguageUid(-1); $tmpOrganizer->setName($address['name'] ?? ''); $tmpOrganizer->setCity($address['city'] ?? ''); $tmpOrganizer->setZip($address['zip'] ?? ''); $tmpOrganizer->setStreet($address['street'] ?? ''); $tmpOrganizer->setPhone($address['phone'] ?? ''); $tmpOrganizer->setWeb($address['web'] ?? ''); $tmpOrganizer->setEmail($address['email'] ?? ''); $tmpOrganizer->setDistrict($address['district'] ?? ''); $this->organizerRepository->add($tmpOrganizer); $this->tmpCurrentEvent->setOrganizer($tmpOrganizer); } } } /** * @param array $event */ private function setAddress(array $event): void { $this->tmpCurrentEvent->setName($event['name'] ?? ''); $this->tmpCurrentEvent->setStreet($event['street'] ?? ''); $this->tmpCurrentEvent->setCity($event['city'] ?? ''); $this->tmpCurrentEvent->setZip($event['zip'] ?? ''); $this->tmpCurrentEvent->setCountry($event['country'] ?? ''); $this->tmpCurrentEvent->setPhone($event['phone'] ?? ''); $this->tmpCurrentEvent->setWeb($event['web'] ?? ''); } /** * @param array $media */ private function setSocial(array $media): void { foreach ($media as $link) { if ($link['rel'] == "socialmedia" && $link['value'] == "Facebook") { $this->tmpCurrentEvent->setFacebook($link['url']); } if ($link['rel'] == "socialmedia" && $link['value'] == "YouTube") { $this->tmpCurrentEvent->setYouTube($link['url']); } if ($link['rel'] == "socialmedia" && $link['value'] == "Instagram") { $this->tmpCurrentEvent->setInstagram($link['url']); } } } /** * @param array $media */ private function setTickets(array $media): void { foreach ($media as $link) { if ($link['rel'] == "ticket") { $this->tmpCurrentEvent->setTicket($link['url']); break; } elseif ($link['rel'] == "booking" && !$this->multiArrayKeyExists('ticket', $media)) { $this->tmpCurrentEvent->setTicket($link['url']); break; } elseif ($link['rel'] == "PRICE_KARTENLINK" && !$this->multiArrayKeyExists('ticket', $media) && !$this->multiArrayKeyExists('booking', $media)) { $this->tmpCurrentEvent->setTicket($link['url']); } } } private function multiArrayKeyExists(string $needle, array $haystack): bool { foreach ($haystack as $key => $value) { if ($needle == $key) { return true; } if (is_array($value)) { if ($this->multiArrayKeyExists($needle, $value) == true) { return true; } } } return false; } private function setLatLng(string $lat, string $lng): void { $this->tmpCurrentEvent->setLatitude($lat); $this->tmpCurrentEvent->setLongitude($lng); } private function setTexts(array $texts): void { foreach ($texts as $text) { if (isset($text['value']) === false) { continue; } if ($text['rel'] == "details" && $text['type'] == "text/plain") { $this->tmpCurrentEvent->setDetails(str_replace('\n\n', '\n', $text['value'])); } if ($text['rel'] == "teaser" && $text['type'] == "text/plain") { $this->tmpCurrentEvent->setTeaser(str_replace('\n\n', '\n', $text['value'])); } if ($text['rel'] == "PRICE_INFO" && $text['type'] == "text/plain") { $this->tmpCurrentEvent->setPriceInfo(str_replace('\n\n', '\n', $text['value'])); } } } private function getOrCreateEvent(string $globalId, string $title): Event { $event = $this->eventRepository->findOneByGlobalId($globalId); if ($event instanceof Event) { $this->logger->info( 'Found "' . substr($title, 0, 20) . '..." with global id ' . $globalId . ' in database' ); return $event; } // New event is created $this->logger->info(substr($title, 0, 20) . ' does not exist'); $event = $this->objectManager->get(Event::class); // Create event and persist $event->setGlobalId($globalId); $event->setCategories(new ObjectStorage()); $this->eventRepository->add($event); $this->persistenceManager->persistAll(); $this->logger->info( 'Not found "' . substr($title, 0, 20) . '..." with global id ' . $globalId . ' in database.' . ' Created new one.' ); return $event; } private function setAssets(array $assets): void { $this->logger->info("Set assets"); $storage = $this->resourceFactory->getDefaultStorage(); if (!$storage instanceof ResourceStorage) { $this->logger->error('No default storage defined. Cancel import.'); exit(); } $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( basename( urldecode($media_object['url']) ) ); $this->logger->info('File attached:' . $fileUrl); $this->logger->info('File attached sanitized:' . $orgFileNameSanitized); $targetFilePath = $this->environment->getPublicPath() . '/fileadmin/' . $this->filesFolder . $orgFileNameSanitized; // Check if file already exists if (file_exists($targetFilePath)) { $this->logger->info('File already exists'); } else { $this->logger->info("File don't exist " . $orgFileNameSanitized); // Load the file if ($filename = $this->loadFile($fileUrl)) { // Move file to defined folder $this->logger->info('Adding file ' . $filename); try { $targetFolder = $storage->getFolder($this->filesFolder); } catch (FolderDoesNotExistException $e) { $targetFolder = $storage->createFolder($this->filesFolder); } $storage->addFile($filename, $targetFolder, basename($fileUrl)); } else { $error = true; } } if ($error !== true) { if ($this->tmpCurrentEvent->getImages()->count() > 0) { $this->logger->info('Relation found'); // TODO: How to delete file references? } else { $this->logger->info('No relation found'); $fileIdentifier = $this->filesFolder . $orgFileNameSanitized; $file = $storage->getFile($fileIdentifier); if (!$file instanceof File) { $this->logger->warning('Could not find file.', [$fileIdentifier]); continue; } $this->metaDataRepository->update( $file->getUid(), [ 'title' => $media_object['value'], 'description' => $media_object['description'] ?? '', 'alternative' => 'DD Import' ] ); $this->createFileRelations( $file->getUid(), 'tx_events_domain_model_event', $this->tmpCurrentEvent->getUid(), 'images', $this->storagePid ); } } } $error = false; } } private function loadFile(string $fileUrl): string { $this->logger->info('Getting file ' . $fileUrl); $file = new \SplFileInfo($fileUrl); $temporaryFilename = GeneralUtility::tempnam($file->getBasename()); $response = $this->dataFetcher->fetchImage($fileUrl); $fileContent = $response->getBody()->__toString(); if ($response->getStatusCode() !== 200) { $this->logger->error('Cannot load file ' . $fileUrl); } if (GeneralUtility::writeFile($temporaryFilename, $fileContent, true) === false) { $this->logger->error('Could not write temporary file.'); return ''; } return $temporaryFilename; } private function createFileRelations( int $uid_local, string $tablenames, int $uid_foreign, string $fieldname, int $storagePid ): bool { $newId = 'NEW1234'; $data = array(); $data['sys_file_reference'][$newId] = array( 'table_local' => 'sys_file', 'uid_local' => $uid_local, 'tablenames' => $tablenames, 'uid_foreign' => $uid_foreign, 'fieldname' => $fieldname, 'pid' => $storagePid ); $data[$tablenames][$uid_foreign] = array( 'pid' => $storagePid, $fieldname => $newId ); $dataHandler = $this->objectManager->get(DataHandler::class); $dataHandler->start($data, array()); $dataHandler->process_datamap(); if (count($dataHandler->errorLog) === 0) { return true; } foreach ($dataHandler->errorLog as $error) { $this->logger->info($error); } return false; } private function doSlugUpdate(): void { $this->logger->info('Update slugs'); $slugHelper = GeneralUtility::makeInstance( SlugHelper::class, 'tx_events_domain_model_event', 'slug', $GLOBALS['TCA']['tx_events_domain_model_event']['columns']['slug']['config'] ); $connection = GeneralUtility::makeInstance(ConnectionPool::class) ->getConnectionForTable('tx_events_domain_model_event'); $queryBuilder = $connection->createQueryBuilder(); $queryBuilder->getRestrictions()->removeAll(); $statement = $queryBuilder->select('uid', 'global_id') ->from('tx_events_domain_model_event') ->where( $queryBuilder->expr()->orX( $queryBuilder->expr()->eq('slug', $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)), $queryBuilder->expr()->isNull('slug') ) ) ->execute(); while ($record = $statement->fetch()) { if (is_array($record) === false) { continue; } $queryBuilder = $connection->createQueryBuilder(); $queryBuilder->update('tx_events_domain_model_event') ->where( $queryBuilder->expr()->eq( 'uid', $queryBuilder->createNamedParameter($record['uid'], \PDO::PARAM_INT) ) ) ->set('slug', $slugHelper->sanitize((string)$record['global_id'])); $queryBuilder->getSQL(); $queryBuilder->execute(); } } /** * Fetch the boolean value for requested attribute. * * Returns first if multiple attributes with same key exist. * Casts "true" and "false" to true and false. */ private function getAttributeValue( array $event, string $attributeKey ): bool { $attributes = array_filter($event['attributes'] ?? [], function (array $attribute) use ($attributeKey) { $currentKey = $attribute['key'] ?? ''; return $currentKey === $attributeKey; }); if ($attributes === []) { return false; } $value = $attributes[0]['value'] ?? null; if ($value === 'true') { return true; } if ($value === 'false') { return false; } return (bool) $value; } }