vendor/elements/alpstein-bundle/src/Command/ImportCommand.php line 452

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Elements\Bundle\AlpsteinBundle\Command;
  4. use Carbon\Carbon;
  5. use Elements\Bundle\AlpsteinBundle\Services\AlpsteinConfig;
  6. use Elements\Bundle\RecurringDatesTypeBundle\Model\RecurringDateDefinition;
  7. use GuzzleHttp\Client;
  8. use Pimcore\Console\AbstractCommand;
  9. use Pimcore\Console\Traits\DryRun;
  10. use Pimcore\Db;
  11. use Pimcore\Log\ApplicationLogger;
  12. use Pimcore\Model\Asset\Image;
  13. use Pimcore\Model\DataObject;
  14. use Pimcore\Model\DataObject\AlpsteinPoi;
  15. use Pimcore\Model\DataObject\AlpsteinProperty;
  16. use Pimcore\Model\DataObject\AlpsteinRegion;
  17. use Pimcore\Model\DataObject\AlpsteinRegionCategory;
  18. use Pimcore\Model\DataObject\AlpsteinTour;
  19. use Pimcore\Model\DataObject\AlpsteinKey;
  20. use Pimcore\Model\DataObject\AlpsteinVideo;
  21. use Pimcore\Model\DataObject\Data\Geobounds;
  22. use Pimcore\Model\DataObject\Data\GeoCoordinates;
  23. use Pimcore\Model\DataObject\Data\ObjectMetadata;
  24. use Pimcore\Model\DataObject\Data\Video;
  25. use Pimcore\Model\DataObject\AlpsteinCategory;
  26. use Pimcore\Model\DataObject\Service;
  27. use Pimcore\Model\DataObject\ULicense;
  28. use Symfony\Component\Console\Input\InputInterface;
  29. use Symfony\Component\Console\Input\InputOption;
  30. use Symfony\Component\Console\Output\OutputInterface;
  31. class ImportCommand extends AbstractCommand
  32. {
  33.     use DryRun;
  34.     const TIMESTAMPFILE '/alpstein_import_timestamp.tmp';
  35.     protected $alpsteinConfig;
  36.     protected $defaultLicense;
  37.     protected $doRemoveICCProfile;
  38.     protected $minimumRanking;
  39.     protected $importOnlyCCCompliant;
  40.     protected $validLanguage = [];
  41.     protected $categoryFolder;
  42.     protected $regionCache;
  43.     protected $regionFolder;
  44.     protected $regionCategoryFolder;
  45.     protected $tourFolder;
  46.     protected $poiFolder;
  47.     protected $videosFolder;
  48.     protected $assetIconFolder;
  49.     protected $assetImageFolder '/Alpstein/Images/';
  50.     protected $assetImageFolderObject;
  51.     protected $assetPropertiesFolder;
  52.     protected $objectPropertiesFolder;
  53.     protected $objectKeyFolder;
  54.     protected $importedTourIds = [];
  55.     protected $importedPoiIds = [];
  56.     protected $importedRegions = [];
  57.     protected $options = [];
  58.     protected $numToursPerImportIteration 0;
  59.     protected $importTime 0;
  60.     /**
  61.      * @var Client
  62.      */
  63.     private $client;
  64.     private ApplicationLogger $applicationLogger;
  65.     protected function configure()
  66.     {
  67.         $this
  68.             ->setName('alpstein:import')
  69.             ->setDescription('Migrate existing structure to new file extensions')
  70.             ->addOption('full''f'null'start Full Import')
  71.             ->addOption('regions''r'null'also includes regions in Import')
  72.             ->addOption('pois''p'null'also includes pois in Import')
  73.             ->addOption('override''o'null'Override Images')
  74.             ->addOption('delta''d'null'Only Import changed Tours')
  75.             ->addOption('skip-images'nullnull'Ignore Images')
  76.             ->addOption('tourId'nullInputOption::VALUE_OPTIONAL'Import selected Tour only')
  77.             ->addOption('tours-per-iterations'nullInputOption::VALUE_OPTIONAL'Import the tours in smaller packages, the value is the number of tours per package. Use 0 for not splitting tours into packages'0)
  78.         ;
  79.         $this->configureDryRunOption();
  80.     }
  81.     /**
  82.      * ImportCommand constructor.
  83.      * @param AlpsteinConfig $alpsteinConfig
  84.      * @param Client $client
  85.      */
  86.     public function __construct(AlpsteinConfig $alpsteinConfigClient $clientApplicationLogger $applicationLogger)
  87.     {
  88.         parent::__construct();
  89.         $this->alpsteinConfig $alpsteinConfig->getConfig();
  90.         $this->doRemoveICCProfile $alpsteinConfig->doRemoveICCProfile();
  91.         $this->minimumRanking $alpsteinConfig->getMinimumRanking();
  92.         $this->importOnlyCCCompliant $alpsteinConfig->getImportOnlyCCCompliant();
  93.         $this->client $client;
  94.         $this->applicationLogger $applicationLogger;
  95.     }
  96.     /**
  97.      * @param InputInterface $input
  98.      * @param OutputInterface $output
  99.      * @return int|null|void
  100.      * @throws \GuzzleHttp\Exception\GuzzleException
  101.      */
  102.     protected function execute(InputInterface $inputOutputInterface $output)
  103.     {
  104.         if( $input->getOption('full') ){
  105.             $this->options[] = 'full';
  106.         }
  107.         if( $input->getOption('regions') ){
  108.             $this->options[] = 'regions';
  109.         }
  110.         if( $input->getOption('pois') ){
  111.             $this->options[] = 'pois';
  112.         }
  113.         if( $input->getOption('override') ){
  114.             $this->options[] = 'override';
  115.         }
  116.         if( $input->getOption('delta') ){
  117.             $this->options[] = 'delta';
  118.         }
  119.         if( $input->getOption('skip-images') ){
  120.             $this->options[] = 'skip-images';
  121.         }
  122.         if($input->getOption('tours-per-iterations')) {
  123.             $this->numToursPerImportIteration = (int)$input->getOption('tours-per-iterations');
  124.         }
  125.         $tourId $input->getOption('tourId');
  126.         $this->validLanguage = ['de'];
  127.         foreach( \Pimcore\Tool::getValidLanguages() as $lang ){
  128.             if( $lang != 'de' ){
  129.                 $this->validLanguage[] = $lang;
  130.             }
  131.         }
  132.         $defaultLicense ULicense::getByCode('AlpsteinDefault'1);
  133.         if (!$defaultLicense instanceof ULicense) {
  134.             $defaultLicense = new ULicense();
  135.             $defaultLicense->setKey('AlpsteinDefaultLicense');
  136.             $defaultLicense->setParent(Service::createFolderByPath('Alpstein/License'));
  137.             $defaultLicense->setPublished(true);
  138.             $defaultLicense->setCode('AlpsteinDefault');
  139.             $defaultLicense->setName('Outdooractive AGB''de');
  140.             $defaultLicense->setName('Outdooractive Terms of Service''en');
  141.             $defaultLicense->save();
  142.         }
  143.         $this->defaultLicense $defaultLicense;
  144.         $this->categoryFolder Service::createFolderByPath('/Alpstein/Categories');
  145.         $this->regionFolder Service::createFolderByPath('/Alpstein/Regions');
  146.         $this->regionCategoryFolder Service::createFolderByPath('/Alpstein/RegionCategories');
  147.         $this->assetIconFolder \Pimcore\Model\Asset\Service::createFolderByPath('/Alpstein/Icons');
  148.         $this->tourFolder Service::createFolderByPath('/Alpstein/Tour');
  149.         $this->poiFolder Service::createFolderByPath('/Alpstein/Poi');
  150.         $this->assetImageFolderObject \Pimcore\Model\Asset\Service::createFolderByPath$this->assetImageFolder );
  151.         $this->assetPropertiesFolder \Pimcore\Model\Asset\Service::createFolderByPath'/Alpstein/Properties');
  152.         $this->objectPropertiesFolder Service::createFolderByPath'/Alpstein/Properties');
  153.         $this->videosFolder Service::createFolderByPath'/Alpstein/Videos');
  154.         $this->objectKeyFolder Service::createFolderByPath'/Alpstein/Key');
  155.         $this->importTime time();
  156.         try {
  157.             if( is_array($this->alpsteinConfig['project']) ){
  158.                 foreach( $this->alpsteinConfig['project'] as $key => $project ){
  159.                     $alpsteinConfig = [
  160.                         'project' => $project,
  161.                         'apiKey' => $this->alpsteinConfig['apiKey'][$key]
  162.                     ];
  163.                     $alpsteinConfig['keyObject'] = $this->createAndGetKey$alpsteinConfig['project'], $alpsteinConfig['apiKey'] );
  164.                     $this->importCategories$alpsteinConfig );
  165.                     if ($tourId != '') {
  166.                         $this->importSingleTour($tourId$alpsteinConfig);
  167.                     } else {
  168.                         if (in_array('regions'$this->options)) {
  169.                             $this->importRegions$alpsteinConfig );
  170.                         }
  171.                         if (in_array('pois'$this->options)) {
  172.                             $this->importPois$alpsteinConfig );
  173.                         }
  174.                         $this->importTours$alpsteinConfig );
  175.                     }
  176.                 }
  177.             } else {
  178.                 $this->alpsteinConfig['keyObject'] = $this->createAndGetKey$this->alpsteinConfig['project'], $this->alpsteinConfig['apiKey'] );
  179.                 if ($tourId != '') {
  180.                     $this->importSingleTour($tourId$this->alpsteinConfig);
  181.                 } else {
  182.                     $this->importCategories$this->alpsteinConfig );
  183.                     if (in_array('regions'$this->options)) {
  184.                         $this->importRegions$this->alpsteinConfig );
  185.                     }
  186.                     if (in_array('pois'$this->options)) {
  187.                         $this->importPois$this->alpsteinConfig );
  188.                     }
  189.                     $this->importTours$this->alpsteinConfig );
  190.                 }
  191.             }
  192.             if (!empty($this->importedTourIds) && !in_array('delta'$this->options) && $tourId == '') {
  193.                 $tours = new AlpsteinTour\Listing();
  194.                 $tours->addConditionParam('alpsteinId IS NOT NULL AND alpsteinId != "" AND alpsteinId NOT IN (' implode(','$this->importedTourIds) . ')');
  195.                 foreach ($tours as $tour) {
  196.                     $tour->setPublished(false);
  197.                     $tour->save();
  198.                 }
  199.             }
  200.         } catch (\Exception $exception) {
  201.             $this->applicationLogger->info("Error:\n{$exception->getMessage()}\n\n{$exception->getTraceAsString()}", ['component' => 'AlpsteinImportCommand']);
  202.         }
  203.         if ($tourId == '') {
  204.             file_put_contents(PIMCORE_PRIVATE_VAR self::TIMESTAMPFILE$this->importTime);
  205.         }
  206.         return 0;
  207.     }
  208.     /**
  209.      * @param $project
  210.      * @param $apiKey
  211.      * @return AlpsteinKey|AlpsteinKey\Listing
  212.      * @throws \Exception
  213.      */
  214.     private function createAndGetKey$project$apiKey ){
  215.         $keyObject AlpsteinKey::getByApiKey$apiKey, ['limit' => 1] );
  216.         if( !$keyObject instanceof AlpsteinKey ){
  217.             $keyObject = new AlpsteinKey();
  218.             $keyObject->setName($project);
  219.             $keyObject->setApiKey($apiKey);
  220.             $keyObject->setKey\Pimcore\File::getValidFilename($project '-' $apiKey) );
  221.             $keyObject->setParent$this->objectKeyFolder );
  222.             $keyObject->setPublishedtrue );
  223.             $keyObject->save();
  224.         }
  225.         return $keyObject;
  226.     }
  227.     /**
  228.      * Import Categories
  229.      * @param $alpsteinConfig
  230.      * @throws \GuzzleHttp\Exception\GuzzleException
  231.      */
  232.     private function importCategories$alpsteinConfig )
  233.     {
  234.         foreach( $this->validLanguage as $language ){
  235.             $url 'https://www.outdooractive.com/api/project/' $alpsteinConfig['project'] . '/category/tree?lang=' $language '&key=' $alpsteinConfig['apiKey'];
  236.             $client $this->client;
  237.             $requestType 'GET';
  238.             $config = [
  239.                 \GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => [
  240.                     'max' => 2
  241.                 ],
  242.                 \GuzzleHttp\RequestOptions::TIMEOUT => 60
  243.             ];
  244.             $config[\GuzzleHttp\RequestOptions::HEADERS] = [
  245.                 'Accept' => 'application/json'
  246.             ];
  247.             /**
  248.              * @var $response \GuzzleHttp\Psr7\Response
  249.              */
  250.             try {
  251.                 $response $client->request($requestType$url$config);
  252.             } catch (\Throwable $exception) {
  253.                 $this->dump('################### ERROR ###################');
  254.                 $this->dump($exception->getMessage());
  255.                 $this->dump($exception->getTraceAsString());
  256.                 $this->dump('################### ERROR ###################');
  257.             }
  258.             if ($response && $response->getStatusCode() == 200) {
  259.                 $data $response->getBody();
  260.                 $categoryArray json_decode$data->getContents() );
  261.                 foreach( $categoryArray as $itemArray ){
  262.                     // Top Category
  263.                     foreach( $itemArray as $item ){
  264.                         $parentId $this->createCategory$item$alpsteinConfig['keyObject'], $this->categoryFolder->getId(), $language );
  265.                         // Sub Category
  266.                         foreach( $item->category ?? [] as $subCategory ){
  267.                             $this->createCategory$subCategory$alpsteinConfig['keyObject'], $parentId$language );
  268.                         }
  269.                     }
  270.                 }
  271.             }
  272.         }
  273.     }
  274.     /**
  275.      * Import Categories
  276.      * @param $alpsteinConfig
  277.      * @throws \GuzzleHttp\Exception\GuzzleException
  278.      */
  279.     private function importRegions$alpsteinConfig )
  280.     {
  281.         foreach( $this->validLanguage as $language ){
  282.             $url 'https://www.outdooractive.com/api/project/' $alpsteinConfig['project'] . '/region/tree?lang=' $language '&key=' $alpsteinConfig['apiKey'];
  283.             $client $this->client;
  284.             $requestType 'GET';
  285.             $config = [
  286.                 \GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => [
  287.                     'max' => 2
  288.                 ],
  289.                 \GuzzleHttp\RequestOptions::TIMEOUT => 60
  290.             ];
  291.             $config[\GuzzleHttp\RequestOptions::HEADERS] = [
  292.                 'Accept' => 'application/json'
  293.             ];
  294.             /**
  295.              * @var $response \GuzzleHttp\Psr7\Response
  296.              */
  297.             try {
  298.                 $response $client->request($requestType$url$config);
  299.             } catch (\Throwable $exception) {
  300.                 $this->dump('################### ERROR ###################');
  301.                 $this->dump($exception->getMessage());
  302.                 $this->dump($exception->getTraceAsString());
  303.                 $this->dump('################### ERROR ###################');
  304.             }
  305.             if ($response && $response->getStatusCode() == 200) {
  306.                 $data $response->getBody();
  307.                 $regionArray json_decode$data->getContents() );
  308.                 foreach( $regionArray->region as $regionData ){
  309.                     $this->createRegion($regionData$this->regionFolder->getId(), $language$alpsteinConfig['keyObject']);
  310.                 }
  311.             }
  312.         }
  313.     }
  314.     /**
  315.      * @param $item
  316.      * @param null $parentId
  317.      * @param string $language
  318.      * @param $keyObject AlpsteinKey
  319.      * @return int
  320.      * @throws \Exception
  321.      */
  322.     private function createCategory$item$keyObject$parentId null$language 'de' ){
  323.         $category AlpsteinCategory::getByCategoryId( (int)$item->id);
  324.         if( !$category instanceof AlpsteinCategory ){
  325.             $category = new AlpsteinCategory();
  326.             $category->setParentId$parentId );
  327.             $category->setPublishedtrue );
  328.             $category->setKey\Pimcore\File::getValidFilename$item->name '-' $item->id ) );
  329.             $category->setCategoryId((int)$item->id);
  330.             if( $language == 'de' && !$category->getIcon() instanceof Image ){
  331.                 if( isset($item->iconUrl) ){
  332.                     preg_match('/^.*\.(jpg|jpeg|png|gif|svg)$/i'$item->iconUrl$reg);
  333.                     $imageName \Pimcore\File::getValidFilename$item->name '-' $item->id ) . '.' $reg[1];
  334.                     $image Image::getByPath$this->assetIconFolder->getFullPath() . '/' $imageName );
  335.                     if( !$image instanceof Image ){
  336.                         try {
  337.                             $iconUrl \Pimcore\Tool::getHttpData($item->iconUrl, [], [], ['timeout' => 60]);
  338.                         } catch (\Throwable $exception) {
  339.                             $this->dump('################### ERROR ###################');
  340.                             $this->dump($exception->getMessage());
  341.                             $this->dump($exception->getTraceAsString());
  342.                             $this->dump('################### ERROR ###################');
  343.                         }
  344.                         if( $iconUrl ){
  345.                             $image = new Image();
  346.                             $image->setData$iconUrl );
  347.                             $image->setFilename$imageName \Pimcore\File::getValidFilename$item->name '-' $item->id ) . '.' $reg[1] );
  348.                             $image->setParent$this->assetIconFolder );
  349.                             $image->save();
  350.                             $category->setIcon($image);
  351.                         }
  352.                     }else{
  353.                         $category->setIcon($image);
  354.                     }
  355.                 }
  356.             }
  357.         }
  358.         $catAlpsteinKeys $category->getAlpsteinKey();
  359.         $hasKey false;
  360.         foreach( $catAlpsteinKeys as $keyObj ){
  361.             if( $keyObj->getApiKey() == $keyObject->getApiKey() ){
  362.                 $hasKey true;
  363.                 break;
  364.             }
  365.         }
  366.         if( !$hasKey ){
  367.             $catAlpsteinKeys[] = $keyObject;
  368.         }
  369.         $category->setAlpsteinKey$catAlpsteinKeys );
  370.         $category->setModificationDatetime() );
  371.         $category->setName$item->name$language );
  372.         $category->save();
  373.         return $category->getId();
  374.     }
  375.     /**
  376.      * @param $regionData
  377.      * @param $parentId
  378.      * @param string $language
  379.      * @param $keyObject AlpsteinKey
  380.      * @throws \Exception
  381.      */
  382.     private function createRegion$regionData$parentId$language 'de'$keyObject ){
  383.         if (array_key_exists($language$this->importedRegions) && array_key_exists($regionData->id$this->importedRegions[$language]) && ($region $this->importedRegions[$language][$regionData->id])) {
  384.             return $region;
  385.         }
  386.         $region AlpsteinRegion::getByAlpsteinId( (int)$regionData->id);
  387.         if( !$region instanceof AlpsteinRegion ){
  388.             $region = new AlpsteinRegion();
  389.             $region->setParentId$parentId );
  390.             $region->setPublishedtrue );
  391.             $region->setKey\Pimcore\File::getValidFilename$regionData->name '-' $regionData->id ) );
  392.             $region->setAlpsteinId((int)$regionData->id);
  393.         }
  394.         $alpsteinKeys $region->getAlpsteinKey();
  395.         $hasKey false;
  396.         foreach( $alpsteinKeys as $keyObj ){
  397.             if( $keyObj->getApiKey() == $keyObject->getApiKey() ){
  398.                 $hasKey true;
  399.                 break;
  400.             }
  401.         }
  402.         if( !$hasKey ){
  403.             $alpsteinKeys[] = $keyObject;
  404.         }
  405.         $region->setAlpsteinKey$alpsteinKeys );
  406.         $region->setModificationDatetime() );
  407.         $region->setName$regionData->name$language );
  408.         $region->setRegionType$regionData->type );
  409.         $region->setLevel$regionData->level );
  410.         $region->setHasTour($regionData->hasTour);
  411.         if ($regionData->bbox) {
  412.             $geodata explode(','$regionData->bbox);
  413.             $southWest = new GeoCoordinates($geodata[1], $geodata[0]);
  414.             $northEast = new GeoCoordinates($geodata[3], $geodata[2]);
  415.             $bbox = new Geobounds($northEast$southWest);
  416.             $region->setBbox($bbox);
  417.         }
  418.         $region->setCategory($this->createRegionCategory($regionData->categoryId$regionData->categoryTitle$language$keyObject));
  419.         $region->save();
  420.         $subregions $region->getSubRegions();
  421.         $containingRegions = [];
  422.         foreach ($subregions ?: [] as $subRegionObj) {
  423.             $containingRegions[] = $subRegionObj->getAlpsteinId();
  424.         }
  425.         if (!empty($regionData->region)) {
  426.             foreach ($regionData->region as $subRegionData) {
  427.                 $subRegion $this->createRegion($subRegionData$region->getId(), $language$keyObject);
  428.                 if (!in_array($subRegion->getAlpsteinId(), $containingRegions)) {
  429.                     $subregions[] = $subRegion;
  430.                     $containingRegions[] = $subRegion->getAlpsteinId();
  431.                 }
  432.             }
  433.         }
  434.         $region->setSubRegions($subregions);
  435.         $region->save();
  436.         $this->importedRegions[$language][$regionData->id] = $region;
  437.         return $region;
  438.     }
  439.     /**
  440.      * @param $regionCategoryId
  441.      * @param $regionCategoryName
  442.      * @param string $language
  443.      * @param $keyObject AlpsteinKey
  444.      * @return AlpsteinRegionCategory
  445.      * @throws \Exception
  446.      */
  447.     private function createRegionCategory$regionCategoryId$regionCategoryName$language 'de'$keyObject ){
  448.         $regionCategory AlpsteinRegionCategory::getByAlpsteinId( (int)$regionCategoryId);
  449.         if( !$regionCategory instanceof AlpsteinRegionCategory ){
  450.             $regionCategory = new AlpsteinRegionCategory();
  451.             $regionCategory->setParentId$this->regionCategoryFolder->getId() );
  452.             $regionCategory->setPublishedtrue );
  453.             $regionCategory->setKey\Pimcore\File::getValidFilename$regionCategoryName '-' $regionCategoryId ) );
  454.             $regionCategory->setAlpsteinId((int)$regionCategoryId);
  455.         }
  456.         $alpsteinKeys $regionCategory->getAlpsteinKey();
  457.         $hasKey false;
  458.         foreach( $alpsteinKeys as $keyObj ){
  459.             if( $keyObj->getApiKey() == $keyObject->getApiKey() ){
  460.                 $hasKey true;
  461.                 break;
  462.             }
  463.         }
  464.         if( !$hasKey ){
  465.             $alpsteinKeys[] = $keyObject;
  466.         }
  467.         $regionCategory->setAlpsteinKey$alpsteinKeys );
  468.         $regionCategory->setName$regionCategoryName$language );
  469.         $regionCategory->setModificationDatetime() );
  470.         $regionCategory->save();
  471.         return $regionCategory;
  472.     }
  473.     private function importSingleTour ($tourObjectId$alpsteinConfig) {
  474.         $tour AlpsteinTour::getById($tourObjectId);
  475.         if ($tour instanceof AlpsteinTour) {
  476.             $lastImport 0;
  477.             $importTimeStampFile PIMCORE_PRIVATE_VAR '/alpstein-single-import-' $tourObjectId '.txt';
  478.             if (file_exists($importTimeStampFile)) {
  479.                 $lastImport file_get_contents($importTimeStampFile);
  480.             }
  481.             if ($lastImport strtotime('-2 hours')) {
  482.                 file_put_contents((string)time(), $importTimeStampFile);
  483.                 $tourId $tour->getAlpsteinId();
  484.                 foreach ($this->validLanguage as $language) {
  485.                     $url 'https://www.outdooractive.com/api/project/' $alpsteinConfig['project'] . '/oois/' $tourId '?lang=' $language '&key=' $alpsteinConfig['apiKey'];
  486.                     $client $this->client;
  487.                     $requestType 'GET';
  488.                     $config = [
  489.                         \GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => [
  490.                             'max' => 2
  491.                         ],
  492.                         \GuzzleHttp\RequestOptions::TIMEOUT => 60
  493.                     ];
  494.                     $config[\GuzzleHttp\RequestOptions::HEADERS] = [
  495.                         'Accept' => 'application/json'
  496.                     ];
  497.                     /**
  498.                      * @var $response \GuzzleHttp\Psr7\Response
  499.                      */
  500.                     try {
  501.                         $response $client->request($requestType$url$config);
  502.                     } catch (\Throwable $exception) {
  503.                         $this->dump('################### ERROR ###################');
  504.                         $this->dump($exception->getMessage());
  505.                         $this->dump($exception->getTraceAsString());
  506.                         $this->dump('################### ERROR ###################');
  507.                     }
  508.                     if ($response && $response->getStatusCode() == 200) {
  509.                         $data $response->getBody();
  510.                         $tourArray json_decode($data->getContents());
  511.                         if( $tourArray->tour[0] && in_array($language$tourArray->tour[0]->meta->translation ) ){
  512.                             $this->createTour$tourArray->tour[0], $language$alpsteinConfig['keyObject'] );
  513.                         }
  514.                     }
  515.                 }
  516.                 unlink($importTimeStampFile);
  517.             }
  518.         }
  519.     }
  520.     private function importTours$alpsteinConfig ){
  521.         $tourIds $this->importTourIdList($alpsteinConfig);
  522.         $tourIdPackages $this->createTourIdPackages($tourIds);
  523.         $numPackages count($tourIdPackages);
  524.         try {
  525.             foreach ($tourIdPackages as $key => $tourIds) {
  526.                 $this->applicationLogger->info('Tour Import Package #' $key+"/{$numPackages}", ['component' => 'AlpsteinImportCommand']);
  527.                 $this->dump("Importing package #" . ($key 1) . "/{$numPackages}; package size: " count($tourIds));
  528.                 foreach ($this->validLanguage as $language) {
  529.                     foreach ($tourIds as $tourId) {
  530.                         $url 'https://www.outdooractive.com/api/project/' $alpsteinConfig['project'] . '/oois/' $tourId '?lang=' $language '&key=' $alpsteinConfig['apiKey'];
  531.                         $client $this->client;
  532.                         $requestType 'GET';
  533.                         $config = [
  534.                             \GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => [
  535.                                 'max' => 2
  536.                             ],
  537.                             \GuzzleHttp\RequestOptions::TIMEOUT => 60
  538.                         ];
  539.                         $config[\GuzzleHttp\RequestOptions::HEADERS] = [
  540.                             'Accept' => 'application/json'
  541.                         ];
  542.                         /**
  543.                          * @var $response \GuzzleHttp\Psr7\Response
  544.                          */
  545.                         try {
  546.                             $response $client->request($requestType$url$config);
  547.                         } catch (\Throwable $exception) {
  548.                             $this->dump('################### ERROR ###################');
  549.                             $this->dump($exception->getMessage());
  550.                             $this->dump($exception->getTraceAsString());
  551.                             $this->dump('################### ERROR ###################');
  552.                         }
  553.                         if ($response && $response->getStatusCode() == 200) {
  554.                             $data $response->getBody();
  555.                             $tourArray json_decode($data->getContents());
  556.                             if ($tourArray->tour[0] && in_array($language$tourArray->tour[0]->meta->translation)) {
  557.                                 $this->createTour($tourArray->tour[0], $language$alpsteinConfig['keyObject']);
  558.                             }
  559.                         }
  560.                         $this->dump(" ");
  561.                     }
  562.                 }
  563.                 \Pimcore::collectGarbage();
  564.             }
  565.         } catch (\Throwable $e) {
  566.             $this->applicationLogger->info("Error while importing tour\n{$e->getMessage()}\n\n{$e->getTraceAsString()}", ['component' => 'AlpsteinImportCommand']);
  567.         }
  568.         $this->dump('----> Done');
  569.     }
  570.     private function importPois$alpsteinConfig ){
  571.         $poiIds $this->importPoiIdList($alpsteinConfig);
  572.         foreach ($this->validLanguage as $language) {
  573.             foreach( $poiIds as $poiId ){
  574.                 $url 'https://www.outdooractive.com/api/project/' $alpsteinConfig['project'] . '/oois/' $poiId '?lang=' $language '&key=' $alpsteinConfig['apiKey'];
  575.                 $client $this->client;
  576.                 $requestType 'GET';
  577.                 $config = [
  578.                     \GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => [
  579.                         'max' => 2
  580.                     ],
  581.                     \GuzzleHttp\RequestOptions::TIMEOUT => 60
  582.                 ];
  583.                 $config[\GuzzleHttp\RequestOptions::HEADERS] = [
  584.                     'Accept' => 'application/json'
  585.                 ];
  586.                 /**
  587.                  * @var $response \GuzzleHttp\Psr7\Response
  588.                  */
  589.                 try {
  590.                     $response $client->request($requestType$url$config);
  591.                 } catch (\Throwable $exception) {
  592.                     $this->dump('################### ERROR ###################');
  593.                     $this->dump($exception->getMessage());
  594.                     $this->dump($exception->getTraceAsString());
  595.                     $this->dump('################### ERROR ###################');
  596.                 }
  597.                 if ($response && $response->getStatusCode() == 200) {
  598.                     $data $response->getBody();
  599.                     $poiArray json_decode($data->getContents());
  600.                     if( $poiArray->poi[0] && in_array($language$poiArray->poi[0]->meta->translation ) ){
  601.                         $this->createPoi$poiArray->poi[0], $language$alpsteinConfig['keyObject'] );
  602.                     }
  603.                 }
  604.                 $this->dump(" ");
  605.             }
  606.         }
  607.         $this->dump('----> Done');
  608.     }
  609.     /**
  610.      * Import List of Id's
  611.      * @param string $language
  612.      * @return array
  613.      * @throws \GuzzleHttp\Exception\GuzzleException
  614.      */
  615.     private function importTourIdList$alpsteinConfig$language 'de' ) : array {
  616.         $tourIds = [];
  617.         $url 'https://www.outdooractive.com/api/project/' $alpsteinConfig['project'] . '/tours?key=' $alpsteinConfig['apiKey'];
  618.         if (in_array('delta'$this->options) && file_exists(PIMCORE_PRIVATE_VAR self::TIMESTAMPFILE)) {
  619.             $timestamp Carbon::createFromTimestamp((int)file_get_contents(PIMCORE_PRIVATE_VAR self::TIMESTAMPFILE));
  620.             $url .= '&lastModifiedAfter=' $timestamp->format('d.m.Y');
  621.         }
  622.         $client $this->client;
  623.         $requestType 'GET';
  624.         $config = [
  625.             \GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => [
  626.                 'max' => 2
  627.             ],
  628.             \GuzzleHttp\RequestOptions::TIMEOUT => 60
  629.         ];
  630.         $config[\GuzzleHttp\RequestOptions::HEADERS] = [
  631.             'Accept' => 'application/json'
  632.         ];
  633.         /**
  634.          * @var $response \GuzzleHttp\Psr7\Response
  635.          */
  636.         try {
  637.             $response $client->request($requestType$url$config);
  638.         } catch (\Throwable $exception) {
  639.             $this->dump('################### ERROR ###################');
  640.             $this->dump($exception->getMessage());
  641.             $this->dump($exception->getTraceAsString());
  642.             $this->dump('################### ERROR ###################');
  643.             $this->applicationLogger->info("error importing tour: \n{$exception->getMessage()}", ['component' => 'AlpsteinImportCommand']);
  644.         }
  645.         if ($response && $response->getStatusCode() == 200) {
  646.             $data $response->getBody();
  647.             $tourArray json_decode($data->getContents());
  648.             foreach ($tourArray->data ?? [] as $itemArray) {
  649.                 $tourIds[] = $itemArray->id;
  650.             }
  651.         }
  652.         return $tourIds;
  653.     }
  654.     /**
  655.      * Import List of Id's
  656.      * @param string $language
  657.      * @return array
  658.      * @throws \GuzzleHttp\Exception\GuzzleException
  659.      */
  660.     private function importPoiIdList$alpsteinConfig$language 'de' ) : array {
  661.         $poiIds = [];
  662.         $url 'https://www.outdooractive.com/api/project/' $alpsteinConfig['project'] . '/pois?key=' $alpsteinConfig['apiKey'];
  663.         if (in_array('delta'$this->options) && file_exists(PIMCORE_PRIVATE_VAR self::TIMESTAMPFILE)) {
  664.             $timestamp Carbon::createFromTimestamp((int)file_get_contents(PIMCORE_PRIVATE_VAR self::TIMESTAMPFILE));
  665.             $url .= '&lastModifiedAfter=' $timestamp->format('d.m.Y');
  666.         }
  667.         $client $this->client;
  668.         $requestType 'GET';
  669.         $config = [
  670.             \GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => [
  671.                 'max' => 2
  672.             ],
  673.             \GuzzleHttp\RequestOptions::TIMEOUT => 60
  674.         ];
  675.         $config[\GuzzleHttp\RequestOptions::HEADERS] = [
  676.             'Accept' => 'application/json'
  677.         ];
  678.         /**
  679.          * @var $response \GuzzleHttp\Psr7\Response
  680.          */
  681.         try {
  682.             $response $client->request($requestType$url$config);
  683.         } catch (\Throwable $exception) {
  684.             $this->dump('################### ERROR ###################');
  685.             $this->dump($exception->getMessage());
  686.             $this->dump($exception->getTraceAsString());
  687.             $this->dump('################### ERROR ###################');
  688.         }
  689.         if ($response && $response->getStatusCode() == 200) {
  690.             $data $response->getBody();
  691.             $poiArray json_decode($data->getContents());
  692.             foreach ($poiArray->data as $itemArray) {
  693.                 $poiIds[] = $itemArray->id;
  694.             }
  695.         }
  696.         return $poiIds;
  697.     }
  698.     // Create or Update Tour
  699.     private function createTour$tour$language 'de'$keyObject ){
  700.         /** not yet considered Fields:
  701.          * - lineOptions
  702.          * - riskPotential
  703.          * - exposition
  704.          * - wayType
  705.          * - elements
  706.          * - difficulties
  707.          * - literature
  708.          * - maps
  709.          * - bookWorks
  710.          * - mapWorks
  711.          * - localizedTitle
  712.          * - type
  713.          * - frontendtype
  714.          * - license
  715.          */
  716.         $tourObject AlpsteinTour::getByAlpsteinId( (int)$tour->id, [ 'limit' => 1'unpublished' => true ] );
  717.         // ignore tours not meeting minimumRanking condition and unpublish if already exists
  718.         if ($this->minimumRanking && $tour->ranking $this->minimumRanking) {
  719.             if ($tourObject instanceof AlpsteinTour) {
  720.                 $tourObject->setPublished(false);
  721.                 $tourObject->save();
  722.             }
  723.             return;
  724.         }
  725.         if( !$tourObject instanceof AlpsteinTour ){
  726.             $tourObject = new AlpsteinTour();
  727.             $tourObject->setKey\Pimcore\File::getValidFilename( ($tour->title ?? 'no-title') . '-' $tour->id ) );
  728.             $tourObject->setAlpsteinId( (int)$tour->id );
  729.             $tourObject->setParent$this->tourFolder );
  730.             $tourObject->save();
  731.         }
  732.         $catAlpsteinKeys $tourObject->getAlpsteinKey();
  733.         $hasKey false;
  734.         foreach( $catAlpsteinKeys as $keyObj ){
  735.             if( $keyObj->getApiKey() == $keyObject->getApiKey() ){
  736.                 $hasKey true;
  737.                 break;
  738.             }
  739.         }
  740.         if( !$hasKey ){
  741.             $catAlpsteinKeys[] = $keyObject;
  742.         }
  743.         $tourObject->setAlpsteinKey$catAlpsteinKeys );
  744.         $this->dump('-> Tour: ' $tourObject->getName('de') . '(' $tourObject->getId() . ')');
  745.         $published false;
  746.         if( $tour->meta->workflow->state == 'published' ){
  747.             $published true;
  748.         }
  749.         if ($tourObject->getId() == '' ) {
  750.             p_r($tour->meta->workflow); die();
  751.         }
  752.         $tourObject->setPublished$published );
  753.         $tourObject->setName$tour->title ?? ''$language );
  754.         $tourObject->setRanking($tour->ranking ?? '');
  755.         // Destination
  756.         $tourObject->setDestination$tour->destination ?? ''$language );
  757.         //ShortText
  758.         $tourObject->setShortText$tour->shortText ?? ''$language );
  759.         // Longtext
  760.         $tourObject->setLongText$tour->longText ?? ''$language );
  761.         // Import all Images and return an array [ primaryImage, allImages ]
  762.         $primaryId null;
  763.         $primaryTitle 'primaryImage';
  764.         if( isset($tour->primaryImage) && isset($tour->primaryImage->id) ){
  765.             $primaryId $tour->primaryImage->id;
  766.             if(isset($tour->primaryImage->title)) {
  767.                 $primaryTitle $tour->primaryImage->title;
  768.             }
  769.         }
  770.         if (!in_array('skip-images'$this->options)) {
  771.             if( isset( $tour->images ) ){
  772.                 $images $this->loadAllImages$tourObject$tour->id$tour->images->image$primaryId$language$primaryTitle );
  773.                 if( !is_null$images'primaryImage' ] ) ){
  774.                     $tourObject->setPrimaryImage$images'primaryImage' ] );
  775.                 }
  776.                 if( !is_null$images'videos' ] ) ){
  777.                     $tourObject->setVideos$images'videos' ] );
  778.                 }
  779.                 if( in_array'full'$this->options ) ){
  780.                     if( !is_null$images'allImages' ] ) ){
  781.                         $tourObject->setImages$images'allImages' ] );
  782.                     }
  783.                 }
  784.             }
  785.         }
  786.         // set Category
  787.         $category AlpsteinCategory::getByCategoryId$tour->category->id, [ 'limit' => 1'unpublished' => true ] );
  788.         if( $category ){
  789.             $tourObject->setCategory([ $category ]);
  790.         }
  791.         // Opened
  792.         if (isset( $tour->opened ) && $tour->opened !== null){
  793.             $tourObject->setOpened($tour->opened);
  794.         } else {
  795.             $tourObject->setOpened(true);
  796.         }
  797.         // -- Regions
  798.         $oldRegions $tourObject->getRegions();
  799.         if( isset($tour->regions) && isset($tour->regions->region) ) {
  800.             $regions = [];
  801.             foreach ($tour->regions->region as $regionData) {
  802.                 if (!isset($this->regionCache[$regionData->id]) instanceof AlpsteinRegion) {
  803.                     $this->regionCache[$regionData->id] = AlpsteinRegion::getByAlpsteinId((string) $regionData->id1);
  804.                 }
  805.                 $region $this->regionCache[$regionData->id];
  806.                 if ($region instanceof AlpsteinRegion) {
  807.                     $objectMetadata = new ObjectMetadata('regions', ['isStartingRegion'], $region);
  808.                     $oldIsStartingRegion null;
  809.                     if($tourObject->getDoNotUpdateStartingRegion()) { //if not set, always update starting region
  810.                         $oldIsStartingRegion $this->getStartingRegionFromOldRegionsArray($region$oldRegions);
  811.                     }
  812.                     if($oldIsStartingRegion === null) {
  813.                         if (isset($regionData->isStartRegion)) {
  814.                             $objectMetadata->setIsStartingRegion(true);
  815.                         } else {
  816.                             $objectMetadata->setIsStartingRegion(false);
  817.                         }
  818.                     } else { //old region did exist + getDoNotUpdateStartingRegion === true
  819.                         $objectMetadata->setIsStartingRegion($oldIsStartingRegion);
  820.                     }
  821.                     $regions[] = $objectMetadata;
  822.                 }
  823.             }
  824.             $tourObject->setRegions($regions);
  825.         }
  826.         // -- Texte
  827.         if( in_array'full'$this->options ) ){
  828.             // global
  829.             $tourObject->setStartingPointDesc$tour->startingPointDescr ?? ''$language );
  830.             // transit
  831.             $tourObject->setPublicTransit$tour->publicTransit ?? ''$language );
  832.             // getting There
  833.             $tourObject->setGettingThere$tour->gettingThere ?? ''$language );
  834.             // parking
  835.             $tourObject->setParking$tour->parking ?? ''$language );
  836.             // Safety Guide
  837.             $tourObject->setSafetyGuidlines$tour->safetyGuidlines ?? ''$language );
  838.             // Tip
  839.             $tourObject->setTip$tour->tip ?? ''$language );
  840.             // Additional Information
  841.             $tourObject->setEquipment$tour->equipment ?? ''$language );
  842.             // Additional Information
  843.             $tourObject->setAdditionalInformation$tour->additionalInformation ?? ''$language );
  844.             // Direction
  845.             $tourObject->setDirections$tour->directions ?? ''$language);
  846.             // -- Labels
  847.             // top
  848.             $tourObject->setTop$tour->labels->top ?? '' );
  849.             //publicTransportFriendly
  850.             if( isset( $tour->labels) ){
  851.                 $tourObject->setPublicTransportFriendly$tour->labels->publicTransportFriendly ?? '' );
  852.             }
  853.             // winter activity
  854.             $tourObject->setWinterActivity$tour->winterActivity ?? '' );
  855.             // -- Data
  856.             // Publisher ID
  857.             $tourObject->setPublisherID$tour->meta->source->id ?? '' );
  858.             // Time Min
  859.             $tourObject->setTimeMin$tour->time->min ?? '' );
  860.             // Length
  861.             $tourObject->setLength$tour->length ?? '' );
  862.             // Elevation
  863.             $tourObject->setElevationAscent$tour->elevation->ascent ?? '' );
  864.             $tourObject->setElevationDescent$tour->elevation->descent ?? '' );
  865.             $tourObject->setElevationMinAltitude$tour->elevation->minAltitude ?? '' );
  866.             $tourObject->setElevationMaxAltitude$tour->elevation->maxAltitude ?? '' );
  867.             $tourObject->setElevationProfileId$tour->elevationProfile->id ?? '');
  868.             // Rating
  869.             $tourObject->setRatingCondition$tour->rating->condition ?? '' );
  870.             $tourObject->setRatingDifficulty$tour->rating->difficulty ?? '' );
  871.             $tourObject->setRatingTechnique$tour->rating->technique ?? '' );
  872.             $tourObject->setRatingQualityExp$tour->rating->qualityOfExperience ?? '' );
  873.             $tourObject->setRatingLandscape$tour->rating->landscape ?? '' );
  874.             // -- Season
  875.             foreach( [ 'jan''feb''mar''apr''may''jun''jul''aug''sep''oct''nov''dec' ] as $season ){
  876.                 $setter 'set' ucfirst($season);
  877.                 $tourObject->$setter$tour->season->{$season} );
  878.             }
  879.             // -- License
  880.             $license false;
  881.             if (isset($tour->meta) && isset($tour->meta->license)) {
  882.                 $license $this->getLicenseByFid($tour->meta->license->short);
  883.             }
  884.             if ($license) {
  885.                 $tourObject->setProperty('ulicense_object''object'$license);
  886.             } else {
  887.                 $tourObject->setProperty('ulicense_object''object'$this->defaultLicense);
  888.             }
  889.             $pois = [];
  890.             if( isset( $tour->pois ) ){
  891.                 foreach ($tour->pois->poi ?? [] as $poi) {
  892.                     $poiObject AlpsteinPoi::getByAlpsteinId($poi->id1);
  893.                     if ($poiObject instanceof AlpsteinPoi) {
  894.                         $objectMetadata = new ObjectMetadata('pois', ['isElevationProfilePoi''isReststopPoi''isRecommendationPoi'], $poiObject);
  895.                         if ($poi->isElevationProfilePoi ?? false) {
  896.                             $objectMetadata->setIsElevationProfilePoi(true);
  897.                         }
  898.                         if ($poi->isReststopPoi ?? false) {
  899.                             $objectMetadata->setIsReststopPoi(true);
  900.                         }
  901.                         if ($poi->isRecommendationPoi ?? false) {
  902.                             $objectMetadata->setIsRecommendationPoi(true);
  903.                         }
  904.                         $pois[] = $objectMetadata;
  905.                     }
  906.                 }
  907.             }
  908.             $tourObject->setPois($pois);
  909.             // -- GEO
  910.             // Destination
  911.             $tourObject->setDestination$tour->destination ?? ''$language );
  912.             // startingPoint
  913.             $startingGeo = new GeoCoordinates();
  914.             $startingGeo->setLatitude$tour->startingPoint->lat );
  915.             $startingGeo->setLongitude$tour->startingPoint->lon );
  916.             $tourObject->setStartingPoint$startingGeo );
  917.             // geoPositions (tour)
  918.             $tourObject->setGeometry$tour->geometry );
  919.             // -- Properties
  920.             if( isset( $tour->properties)){
  921.                 $tourProperties $this->tourProperties$tour->properties->property ?? ''$language );
  922.                 $tourObject->setTourProperties$tourProperties );
  923.             }
  924.         } // end only full import
  925.         $tourObject->save();
  926.         $this->importedTourIds[] = $tourObject->getAlpsteinId();
  927.     }
  928.     // Create or Update Poi
  929.     private function createPoi$poi$language 'de'$keyObject ){
  930.         /** not yet considered Fields:
  931.          * - frontendtype
  932.          * - license
  933.          */
  934.         $poiObject AlpsteinPoi::getByAlpsteinId( (int)$poi->id, [ 'limit' => 1'unpublished' => true ] );
  935.         // ignore tours not meeting minimumRanking condition and unpublish if already exists
  936.         if ($this->minimumRanking && $poi->ranking $this->minimumRanking) {
  937.             if ($poiObject instanceof AlpsteinPoi) {
  938.                 $poiObject->setPublished(false);
  939.                 $poiObject->save();
  940.             }
  941.             return;
  942.         }
  943.         if( !$poiObject instanceof AlpsteinPoi ){
  944.             $poiObject = new AlpsteinPoi();
  945.             $poiObject->setKey\Pimcore\File::getValidFilename$poi->title '-' $poi->id ) );
  946.             $poiObject->setAlpsteinId( (int)$poi->id );
  947.             $poiObject->setParent$this->poiFolder );
  948.             $poiObject->save();
  949.         }
  950.         $catAlpsteinKeys $poiObject->getAlpsteinKey();
  951.         $hasKey false;
  952.         foreach( $catAlpsteinKeys as $keyObj ){
  953.             if( $keyObj->getApiKey() == $keyObject->getApiKey() ){
  954.                 $hasKey true;
  955.                 break;
  956.             }
  957.         }
  958.         if( !$hasKey ){
  959.             $catAlpsteinKeys[] = $keyObject;
  960.         }
  961.         $poiObject->setAlpsteinKey$catAlpsteinKeys );
  962.         $this->dump('-> POI: ' $poiObject->getName('de') . '(' $poiObject->getId() . ')');
  963.         $published false;
  964.         if( $poi->meta->workflow->state == 'published' ){
  965.             $published true;
  966.         }
  967.         if ($poiObject->getId() == '' ) {
  968.             p_r($poi->meta->workflow); die();
  969.         }
  970.         $poiObject->setPublished$published );
  971.         $poiObject->setName$poi->title ?? ''$language );
  972.         $poiObject->setRanking($poi->ranking ?? '');
  973.         $poiObject->setFrontendType($poi->frontendtype ?? '');
  974.         //ShortText
  975.         $poiObject->setShortText$poi->shortText ?? ''$language );
  976.         // Longtext
  977.         $poiObject->setLongText$poi->longText ?? ''$language );
  978.         // Import all Images and return an array [ primaryImage, allImages ]
  979.         $primaryId null;
  980.         $primaryTitle 'primaryImage';
  981.         if( isset($poi->primaryImage) && isset($poi->primaryImage->id) ){
  982.             $primaryId $poi->primaryImage->id;
  983.             if(isset($poi->primaryImage->title)) {
  984.                 $primaryTitle $poi->primaryImage->title;
  985.             }
  986.         }
  987.         if (!in_array('skip-images'$this->options)) {
  988.             if (isset($poi->images)) {
  989.                 $images $this->loadAllImages($poiObject$poi->id$poi->images->image$primaryId$language,
  990.                     $primaryTitle);
  991.                 if (!is_null($images['primaryImage'])) {
  992.                     $poiObject->setPrimaryImage($images['primaryImage']);
  993.                 }
  994.                 if (!is_null($images['videos'])) {
  995.                     $poiObject->setVideos($images['videos']);
  996.                 }
  997.                 if (in_array('full'$this->options)) {
  998.                     if (!is_null($images['allImages'])) {
  999.                         $poiObject->setImages($images['allImages']);
  1000.                     }
  1001.                 }
  1002.             }
  1003.         }
  1004.         // set Category
  1005.         $category AlpsteinCategory::getByCategoryId$poi->category->id, [ 'limit' => 1'unpublished' => true ] );
  1006.         if( $category ){
  1007.             $poiObject->setCategory([ $category ]);
  1008.         }
  1009.         // -- Labels
  1010.         // top
  1011.         $poiObject->setTop$poi->labels->top ?? '' );
  1012.         // winter activity
  1013.         $poiObject->setWinterActivity$poi->winterActivity ?? '' );
  1014.         $poiObject->setAltitude$poi->altitude ?? '' );
  1015.         // contact data
  1016.         $poiObject->setStreet($poi->address->street ?? '');
  1017.         $poiObject->setZipcode($poi->address->zipcode ?? '');
  1018.         $poiObject->setTown($poi->address->town ?? '');
  1019.         $poiObject->setHomepage($poi->homepage ?? '');
  1020.         $poiObject->setEmail($poi->email ?? '');
  1021.         $poiObject->setPhone($poi->phone ?? '');
  1022.         // Texte
  1023.         $poiObject->setPublicTransit($poi->publicTransit ?? ''$language);
  1024.         $poiObject->setGettingThere($poi->gettingThere ?? ''$language);
  1025.         $poiObject->setParking($poi->parking ?? ''$language);
  1026.         // Publisher ID
  1027.         $poiObject->setPublisherID$poi->meta->source->id ?? '');
  1028.         // -- Regions
  1029.         if( isset($poi->regions) && isset($poi->regions->region) ) {
  1030.             $regions = [];
  1031.             foreach ($poi->regions->region as $regionData) {
  1032.                 if (!isset($this->regionCache[$regionData->id]) || !$this->regionCache[$regionData->id] instanceof AlpsteinRegion) {
  1033.                     $this->regionCache[$regionData->id] = AlpsteinRegion::getByAlpsteinId((string) $regionData->id1);
  1034.                 }
  1035.                 $region $this->regionCache[$regionData->id];
  1036.                 if ($region instanceof AlpsteinRegion) {
  1037.                     $objectMetadata = new ObjectMetadata('regions', ['isStartingRegion'], $region);
  1038.                     if ($regionData->isStartRegion) {
  1039.                         $objectMetadata->setIsStartingRegion(true);
  1040.                     }
  1041.                     $regions[] = $objectMetadata;
  1042.                 }
  1043.             }
  1044.             $poiObject->setRegions($regions);
  1045.         }
  1046.         // -- GEO
  1047.         // positions
  1048.         if (isset($poi->geometry)) {
  1049.             $posData explode(','$poi->geometry);
  1050.             $poiObject->setPosition( new GeoCoordinates($posData[0], $posData[1]) );
  1051.         }
  1052.         // -- Properties
  1053.         $poiProperties $this->tourProperties$poi->properties->property ?? [], $language );
  1054.         $poiObject->setPoiProperties$poiProperties );
  1055.         $poiObject->setBusinessHours($poi->businessHours ?? ''$language);
  1056.         $this->poiSetOpeningTimes($poiObject$poi->openingHours ?? []);
  1057.         $poiObject->save();
  1058.         $this->importedTourIds[] = $poiObject->getAlpsteinId();
  1059.     }
  1060.     private function poiSetOpeningTimes($object$openingHours = []) {
  1061.         $dates $object->getOpeningTimes();
  1062.         if (!$dates) {
  1063.             $dates = new RecurringDateDefinition();
  1064.         }
  1065.         $definitionArray = [];
  1066.         if (!empty($openingHours->weekday->openTime)) {
  1067.             // as no daterange is given for the general openingtimes, but recurring dates needs a
  1068.             // daterange (otherwise for fromDate and toDate the current date from the import will be used),
  1069.             // the first day of the year is used for fromDate and the last day of the next year is for toDate
  1070.             $times = [];
  1071.             $dayMapping = [
  1072.                 'Monday' => 'mo',
  1073.                 'Tuesday' => 'tu',
  1074.                 'Wednesday' => 'we',
  1075.                 'Thursday' => 'th',
  1076.                 'Friday' => 'fr',
  1077.                 'Saturday' => 'sa',
  1078.                 'Sunday' => 'su',
  1079.             ];
  1080.             // start to get the times for every weekday
  1081.             foreach ($openingHours->weekday->openTime ?? [] as $date) {
  1082.                 foreach ($date->openHours ?? [] as $openHour) {
  1083.                     $timeFrom $openHour->from ?? null;
  1084.                     $timeTo $openHour->to ?? null;
  1085.                     $weekday $dayMapping[$date->weekday];
  1086.                     $times[$weekday]['times'][] = [
  1087.                         'start' => $timeFrom == '24:00' '23:59' $timeFrom,
  1088.                         'end' => $timeTo == '24:00' '23:59' $timeTo
  1089.                     ];
  1090.                 }
  1091.             }
  1092.             // combine all identical time entries to only get one entry for the same time on different weekdays
  1093.             $identicalTimes = [];
  1094.             $hashes = [];
  1095.             foreach ($times as $weekday => $time) {
  1096.                 $hash hash('sha256'serialize($time));
  1097.                 if (isset($hashes[$hash])) {
  1098.                     $identicalTimes[$hash] = array_merge($identicalTimes[$hash], [
  1099.                         $weekday => true
  1100.                     ]);
  1101.                 } else {
  1102.                     $hashes[$hash] = $weekday;
  1103.                     $identicalTimes[$hash] = array_merge($time, [
  1104.                         $weekday => true,
  1105.                         'fromDate' => Carbon::today()->setMonth(1)->setDay(1)->format('Y-m-d'),
  1106.                         'toDate' => Carbon::today()->setMonth(12)->setDay(31)->addYear()->format('Y-m-d'),
  1107.                     ]);
  1108.                 }
  1109.             }
  1110.             foreach ($identicalTimes as $entries) {
  1111.                 $definitionArray[] = [
  1112.                     'type' => RecurringDateDefinition::DEFINITION_TYPE_RECURRING_DATE_TIME,
  1113.                     'values' => $entries,
  1114.                 ];
  1115.             }
  1116.         }
  1117.         if (!empty($openingHours->special->openTime)) {
  1118.             $times = ['times' => []];
  1119.             foreach ($openingHours->special->openTime as $openTime) {
  1120.                 foreach ($openTime->openHours ?? [] as $openHour) {
  1121.                     $timeFrom $openHour->from ?? null;
  1122.                     $timeTo $openHour->to ?? null;
  1123.                     $times = [
  1124.                         'times' => [
  1125.                             [
  1126.                                 'start' => $timeFrom == '24:00' '23:59' $timeFrom,
  1127.                                 'end' => $timeTo == '24:00' '23:59' $timeTo
  1128.                             ]
  1129.                         ],
  1130.                         'mo' => in_array('MON'$openHour->days ?? []),
  1131.                         'tu' => in_array('TUE'$openHour->days ?? []),
  1132.                         'we' => in_array('WED'$openHour->days ?? []),
  1133.                         'th' => in_array('THU'$openHour->days ?? []),
  1134.                         'fr' => in_array('FRI'$openHour->days ?? []),
  1135.                         'sa' => in_array('SAT'$openHour->days ?? []),
  1136.                         'su' => in_array('SUN'$openHour->days ?? []),
  1137.                     ];
  1138.                 }
  1139.                 // special opening times depend on a time range, and can also be excluded
  1140.                 $definitionArray[] = [
  1141.                     'type' => RecurringDateDefinition::DEFINITION_TYPE_RECURRING_DATE_TIME,
  1142.                     'values' =>  array_merge([
  1143.                         'fromDate' => (string)$openTime->dateFrom,
  1144.                         'toDate' => (string)$openTime->dateTo,
  1145.                         'excluded' => $openTime->isOpen != 'true',
  1146.                     ], $times)
  1147.                 ];
  1148.             }
  1149.         }
  1150.         $dates->setDefinitionArray($definitionArray);
  1151.         $object->setOpeningTimes($dates);
  1152.     }
  1153.     /**
  1154.      * @param DataObject $relatedObject
  1155.      * @param $folderName
  1156.      * @param $imagesArray
  1157.      * @param string $language
  1158.      * @return array
  1159.      * @throws \Exception
  1160.      */
  1161.     private function loadAllImagesDataObject $relatedObject$folderName$imagesArray$primaryId null$language 'de'$primaryTitle 'primaryImage' ) : array{
  1162.         $this->dump('-> LoadImages');
  1163.         $imagesArray $imagesArray ?? [];
  1164.         $assetImage = [
  1165.             'primaryImage' => null,
  1166.             'allImages' => null,
  1167.             'videos' => null
  1168.         ];
  1169.         // Get Image Folder for Tour
  1170.         $folder \Pimcore\Model\Asset\Service::createFolderByPath$this->assetImageFolder $folderName );
  1171.         // Load Available Images
  1172.         $availableImages = [];
  1173.         foreach( $folder->getChildren() as $avImg ){
  1174.             try {
  1175.                 $availableImages$avImg->getMetadata('alpstein-img-id') ] = $avImg;
  1176.             } catch(\Exception $e) {
  1177.                 $this->applicationLogger->info("error accessing metadata (Folder: {$folder->getId()}, Image: {$avImg->getId()}): \n{$e->getMessage()}\n\n{$e->getTraceAsString()}", ['component' => 'AlpsteinImportCommand']);
  1178.             }
  1179.         }
  1180.         $this->dump('---> Images Available: ' . (bool)!empty($availableImages));
  1181.         // get all new Images
  1182.         foreach( $imagesArray as $image ){
  1183.             try {
  1184.                 $inGallery $image->gallery ?? '';
  1185.                 $isVideo $image->isVideo ?? '';
  1186.                 $isPrimary $image->primary ?? '';
  1187.                 if( !$isPrimary && !is_null($primaryId) ){
  1188.                     $isPrimary $primaryId == $image->id true false;
  1189.                 }
  1190.                 $date = new Carbon($image->meta->date->lastModified);
  1191.                 // -- License
  1192.                 $license false;
  1193.                 if (isset($image->meta) && isset($image->meta->license)) {
  1194.                     $license $this->getLicenseByFid($image->meta->license->short);
  1195.                 }
  1196.                 $this->dump('-------> Image: ' . ($image->title ?? ' no - title') . ' Primary: ' . (bool)$isPrimary );
  1197.                 if ($isVideo) {
  1198.                     if ($image->video && $image->video->youtubeId) { // only youtube supported for now
  1199.                         $videoObject AlpsteinVideo::getByAlpsteinId(trim($image->id), 1);
  1200.                         if (!$videoObject instanceof AlpsteinVideo) {
  1201.                             $videoObject = new AlpsteinVideo();
  1202.                             $videoObject->setPublished(true);
  1203.                             $videoObject->setKey(Service::getValidKey(trim($image->id), 'object'));
  1204.                             $videoObject->setParent($this->videosFolder);
  1205.                             $videoObject->setAlpsteinId(trim($image->id));
  1206.                         }
  1207.                         $videoData = new Video();
  1208.                         $videoData->setType('youtube');
  1209.                         $videoData->setData((string)$image->video->youtubeId);
  1210.                         $videoData->setTitle((string)$image->title ?? '');
  1211.                         $videoData->setDescription((string)$image->description);
  1212.                         $videoObject->setVideo($videoData$language);
  1213.                         $videoObject->setSource$image->source ?? '' );
  1214.                         if ($license) {
  1215.                             $videoObject->setProperty('ulicense_object''object'$license);
  1216.                         } else {
  1217.                             $videoObject->setProperty('ulicense_object''object'$this->defaultLicense);
  1218.                         }
  1219.                         $videoObject->save();
  1220.                         $assetImage['videos'][] = $videoObject;
  1221.                     }
  1222.                 } else if( in_array('full'$this->options) || $isPrimary || (isset($availableImages[$image->id]) && (int)$availableImages[$image->id]->getMetadata('last-modified') < $date->timestamp) ){ // wenn voll import oder Primary image
  1223.                     $imageUrl 'http://img.oastatic.com/img/2048/2048/' $image->id '/.jpg';
  1224.                     try {
  1225.                         $loadedImage \Pimcore\Tool::getHttpData$imageUrl, [], [], ['timeout' => 60] );
  1226.                     } catch (\Throwable $exception) {
  1227.                         $this->dump('################### ERROR ###################');
  1228.                         $this->dump($exception->getMessage());
  1229.                         $this->dump($exception->getTraceAsString());
  1230.                         $this->dump('################### ERROR ###################');
  1231.                     }
  1232.                     if( $loadedImage ){
  1233.                         $name \Pimcore\Tool\Transliteration::toASCII($image->title ?? '');
  1234.                         if (strlen($name ?: '') > 50) {
  1235.                             $name substr($name050);
  1236.                         }
  1237.                         $filename \Pimcore\File::getValidFilename$name '-' $image->id ) . '.jpg';
  1238.                         if ($this->doRemoveICCProfile) {
  1239.                             $loadedImage \Elements\Bundle\AlpsteinBundle\Tools\Image::removeICCProfile($loadedImage$filename);
  1240.                         }
  1241.                         if( array_key_exists(trim($image->id), $availableImages) ){
  1242.                             $this->dump('------------> Override Image' );
  1243.                             /**
  1244.                              * @var $editImage Image
  1245.                              */
  1246.                             $newImage $availableImages[$image->id];
  1247.                             $newImage->setData$loadedImage );
  1248.                         }else{
  1249.                             $this->dump('------------> new Image' );
  1250.                             $newImage Image::getByPath$folder->getFullPath() . '/' $filename );
  1251.                             if( !$newImage instanceof Image ) {
  1252.                                 $newImage = new Image();
  1253.                             }
  1254.                             $newImage->addMetadata'alpstein-img-id''input'trim($image->id) );
  1255.                             $newImage->setData($loadedImage);
  1256.                             $newImage->setFilename$filename );
  1257.                             $newImage->setParent$folder );
  1258.                             $availableImages[trim($image->id)] = $newImage;
  1259.                         }
  1260.                         $newImage $this->addImageMetadata($newImage$image$language$date$inGallery$isPrimary$license);
  1261.                         $saveImageSuccess false;
  1262.                         try {
  1263.                             $newImage->save();
  1264.                             $saveImageSuccess true;
  1265.                             $this->dump('Image ID:' $image->id ' Image: ' . ($image->title ?? 'no-title' ));
  1266.                         } catch (\Throwable $exception) {
  1267.                             if (strpos($exception->getMessage(), 'Duplicate full path') === false) {
  1268.                                 $this->dump('Delete and retry Image ID:' $image->id ' Image: ' . ($image->title ?? 'no-title' ) . PHP_EOL $exception->getMessage());
  1269.                                 try {
  1270.                                     $newImage->delete();
  1271.                                 } catch (\Throwable $e) {
  1272.                                     $this->applicationLogger->info("Could not delete image after error occured\nrecreating image...:\n\n{$exception->getMessage()}\n{$exception->getTraceAsString()}", ['component' => 'AlpsteinImportCommand''relatedObject' => $relatedObject]);
  1273.                                 }
  1274.                                 $newImage = new Image();
  1275.                                 $newImage->addMetadata'alpstein-img-id''input'trim($image->id) );
  1276.                                 $newImage->setData($loadedImage);
  1277.                                 $newImage->setFilename$filename );
  1278.                                 $newImage->setParent$folder );
  1279.                                 $availableImages[trim($image->id)] = $newImage;
  1280.                                 $newImage $this->addImageMetadata($newImage$image$language$date$inGallery$isPrimary$license);
  1281.                                 $newImage->save();
  1282.                                 $saveImageSuccess true;
  1283.                                 $this->dump('Image ID:' $image->id ' Image: ' . ($image->title ?? 'no-title'));
  1284.                             } else {
  1285.                                 $saveImageSuccess false;
  1286.                                 $this->applicationLogger->error('Could not save Image: ' PHP_EOL 'Foldername: ' $folderName PHP_EOL 'Image URL: ' $imageUrl PHP_EOL $exception->getMessage() . PHP_EOL $exception->getTraceAsString(), ['component' => 'AlpsteinImportCommand''relatedObject' => $relatedObject]);
  1287.                             }
  1288.                         }
  1289.                         if( $saveImageSuccess && $isPrimary ){
  1290.                             $assetImage['primaryImage'] = $newImage;
  1291.                         }
  1292.                         if( $saveImageSuccess && $inGallery ){
  1293.                             $assetImage['allImages'][] = $newImage;
  1294.                         }
  1295.                     }
  1296.                 }
  1297.             } catch (\Exception $e) {
  1298.                 $this->applicationLogger->info("Could not import image " .
  1299.                     ($image && $image->id $image->id null) .
  1300.                     "\n{$e->getMessage()}\n\n{$e->getTraceAsString()}",
  1301.                     ['component' => 'AlpsteinImportCommand''relatedObject' => $relatedObject]);
  1302.             }
  1303.         }
  1304.         if(empty($assetImage['primaryImage']) && $primaryId) {
  1305.             $newPrimaryImage =  $this->createNewPrimaryImage($primaryId$folder$primaryTitle);
  1306.             $assetImage['primaryImage'] = $newPrimaryImage;
  1307.         }
  1308.         foreach( $imagesArray as $img ){
  1309.             if( array_key_exists($img->id$availableImages) ){
  1310.                 unset($availableImages[$img->id]);
  1311.             }
  1312.         }
  1313.         foreach( $availableImages as $deleteImage ){
  1314.             $deleteImage->delete();
  1315.         }
  1316.         return $assetImage;
  1317.     }
  1318.     private function addImageMetadata($newImage$image$language$date$inGallery$isPrimary$license ) {
  1319.         $imageAuthorName '';
  1320.         if (isset($image->author)) {
  1321.             $imageAuthorName $image->author;
  1322.         } else if (isset($image->meta->authorFull)) {
  1323.             $imageAuthorName $image->meta->authorfull->name;
  1324.         } else if (isset($image->meta->author)) {
  1325.             $imageAuthorName $image->meta->author;
  1326.         }
  1327.         $imageSourceName '';
  1328.         $imageSourceUrl '';
  1329.         if (isset($image->source)) {
  1330.             $imageSourceName = (string)$image->source ?? '';
  1331.         }
  1332.         if ($image->meta->source) {
  1333.             $imageSourceName = (string)$image->meta->source->name ?? '';
  1334.             $imageSourceUrl =  isset($image->meta->source->url) ? (string)$image->meta->source->url '';
  1335.         }
  1336.         if ($imageAuthorName) {
  1337.             $newImage->addMetadata'author''input'$imageAuthorName);
  1338.         }
  1339.         if ($imageSourceName) {
  1340.             $newImage->addMetadata('source''input'$imageSourceName);
  1341.         }
  1342.         if ($imageSourceUrl) {
  1343.             $newImage->addMetadata('sourceUrl''input'$imageSourceUrl);
  1344.         }
  1345.         $newImage->addMetadata'copyright''input'$image->meta->source->name);
  1346.         $newImage->addMetadata'title''input'$image->title ?? ''$language );
  1347.         $newImage->addMetadata'last-modified''input'$date->timestamp );
  1348.         $newImage->addMetadata'gallery''checkbox'$inGallery );
  1349.         $newImage->addMetadata'primary''checkbox'$isPrimary );
  1350.         if ($license) {
  1351.             $newImage->setProperty('ulicense_object''object'$license);
  1352.         } else {
  1353.             $newImage->setProperty('ulicense_object''object'$this->defaultLicense);
  1354.         }
  1355.         return $newImage;
  1356.     }
  1357.     /**
  1358.      * @param $properties
  1359.      * @param string $language
  1360.      * @return array
  1361.      * @throws \Exception
  1362.      */
  1363.     private function tourProperties$properties$language 'de' ){
  1364.         $tourPropertiesArray = [];
  1365.         $properties $properties ?? [];
  1366.         foreach( $properties as $prop ){
  1367.             $propObj AlpsteinProperty::getByTag$prop->tag, [ 'limit' => 1'unpublished' => true ] );
  1368.             if( !$propObj instanceof AlpsteinProperty ){
  1369.                 $propObj = new AlpsteinProperty();
  1370.                 $propObj->setKey\Pimcore\File::getValidFilename($prop->tag '-' time() ) );
  1371.                 $propObj->setParent$this->objectPropertiesFolder );
  1372.                 $propObj->setPublishedtrue );
  1373.                 $propObj->setTag$prop->tag );
  1374.             }
  1375.             if (isset($prop->text) && $prop->text != '') {
  1376.                 $propObj->setName$prop->text$language );
  1377.             }
  1378.             if(isset($prop->hasIcon)){
  1379.                 $filename \Pimcore\File::getValidFilename$prop->text );
  1380.                 $icon Image::getByPath$this->assetPropertiesFolder->getFullPath() . '/' $filename '.svg' );
  1381.                 //$this->assetPropertiesFolder;
  1382.                 if( !$icon instanceof Image && isset($prop->iconURL) ){
  1383.                     try {
  1384.                         $imageUrl \Pimcore\Tool::getHttpData($prop->iconURL, [], [], ['timeout' => 60]);
  1385.                     } catch (\Throwable $exception) {
  1386.                         $this->dump('################### ERROR ###################');
  1387.                         $this->dump($exception->getMessage());
  1388.                         $this->dump($exception->getTraceAsString());
  1389.                         $this->dump('################### ERROR ###################');
  1390.                     }
  1391.                     if( $imageUrl ){
  1392.                         $icon = new Image();
  1393.                         $icon->setFilename$filename '.svg' );
  1394.                         $icon->setParent$this->assetPropertiesFolder );
  1395.                         $icon->setData($imageUrl);
  1396.                         $icon->save();
  1397.                         $propObj->setIcon$icon );
  1398.                     }
  1399.                 }
  1400.             }
  1401.             $propObj->save();
  1402.             $tourPropertiesArray[] = $propObj;
  1403.         }
  1404.         return $tourPropertiesArray;
  1405.     }
  1406.     protected $licenses = [];
  1407.     protected function getLicenseByFid($fid) {
  1408.         if (!array_key_exists($fid$this->licenses) || !$this->licenses[$fid]) {
  1409.             $db Db::get();
  1410.             $id $db->fetchOne('SELECT o_id FROM object_collection_ULicenseFid_ULicense WHERE systemIdentifier = "Alpstein" AND fid = ' $db->quote($fid));
  1411.             if ($id) {
  1412.                 $this->licenses[$fid] = ULicense::getById($id);
  1413.             }
  1414.         }
  1415.         if (!array_key_exists($fid$this->licenses) || !$this->licenses[$fid]) {
  1416.             ApplicationLogger::getInstance()->warning('Could not find License for FID ' $fid, ['component' => 'Alpstein Importer']);
  1417.         }
  1418.         return $this->licenses[$fid];
  1419.     }
  1420.     private function createNewPrimaryImage($primaryId$folder$primaryTitle)
  1421.     {
  1422.         $this->dump('------------> new Primary Image' );
  1423.         $imageUrl 'http://img.oastatic.com/img/2048/2048/' $primaryId '/.jpg';
  1424.         try {
  1425.             $loadedImage \Pimcore\Tool::getHttpData$imageUrl, [], [], ['timeout' => 60] );
  1426.         } catch (\Throwable $exception) {
  1427.             $this->dump('################### ERROR ###################');
  1428.             $this->dump($exception->getMessage());
  1429.             $this->dump($exception->getTraceAsString());
  1430.             $this->dump('################### ERROR ###################');
  1431.             return null;
  1432.         }
  1433.         if ($loadedImage) {
  1434.             if (strlen($primaryTitle ?: '') > 50) {
  1435.                 $primaryTitle substr($primaryTitle050);
  1436.             }
  1437.             $filename \Pimcore\File::getValidFilename$primaryTitle '-' $primaryId ) . '.jpg';
  1438.             if ($this->doRemoveICCProfile) {
  1439.                 $loadedImage \Elements\Bundle\AlpsteinBundle\Tools\Image::removeICCProfile($loadedImage$filename);
  1440.             }
  1441.             $newImage Image::getByPath$folder->getFullPath() . '/' $filename );
  1442.             if( !$newImage instanceof Image ) {
  1443.                 $newImage = new Image();
  1444.             }
  1445.             $newImage->addMetadata'alpstein-img-id''input'trim($primaryId) );
  1446.             $newImage->setData($loadedImage);
  1447.             $newImage->setFilename$filename );
  1448.             $newImage->setParent$folder );
  1449.             //no metadata in primary Image
  1450. //        $newImage = $this->addImageMetadata($newImage, $image);
  1451.             $newImage->save();
  1452.             return $newImage;
  1453.         }
  1454.         return null;
  1455.     }
  1456.     protected function createTourIdPackages(array $tourIds) {
  1457.         if($this->numToursPerImportIteration) {
  1458.             return array_chunk($tourIds$this->numToursPerImportIteration);
  1459.         }
  1460.         return [$tourIds]; //single package
  1461.     }
  1462.     /**
  1463.      * @param AlpsteinRegion $region
  1464.      * @param ObjectMetadata[] $objectMetadata
  1465.      */
  1466.     private function getStartingRegionFromOldRegionsArray(AlpsteinRegion $region, array $objectMetadata) {
  1467.         foreach ($objectMetadata as $metadata) {
  1468.             $metadataElement $metadata->getElement();
  1469.             if($metadataElement instanceof AlpsteinRegion && $metadataElement->getId() === $region->getId()){
  1470.                 return $metadata->getData()['isStartingRegion'];
  1471.             }
  1472.         }
  1473.         return null//region does not exists => new region
  1474.     }
  1475. }