src/Elements/Bundle/DemiFrontendBundle/Controller/AccommodationController.php line 185

Open in your IDE?
  1. <?php
  2. /**
  3.  * Elements DeMI
  4.  *
  5.  * This source file is available under the elements DeMI license version 1
  6.  *
  7.  *  @copyright  Copyright (c) elements.at New Media Solutions GmbH (https://www.elements.at/)
  8.  *  @license    elements DeMI Lizenz Version 1 (https://www.elements.at/de/demi-lizenz)
  9.  */
  10. namespace Elements\Bundle\DemiFrontendBundle\Controller;
  11. use Carbon\Carbon;
  12. use DateInterval;
  13. use Elements\Bundle\CmsToolsBundle\Tool\Helper\ElementsCustomDateFormat;
  14. use Elements\Bundle\DemiFrontendBundle\Event\DemiDirectInquiryEvent;
  15. use Elements\Bundle\SeoHelperBundle\Templating\Helper\Robots;
  16. use Elements\Demi\Frontend\Service\Configuration;
  17. use Elements\Bundle\DemiFrontendBundle\Service\ConnectedEntryProvider;
  18. use Elements\Bundle\DemiFrontendBundle\Service\Customer;
  19. use Elements\Bundle\DemiFrontendBundle\Service\DemiUrl;
  20. use Elements\Bundle\DemiFrontendBundle\Service\Newsletter;
  21. use Elements\Bundle\DemiFrontendBundle\Service\Payment;
  22. use Elements\Bundle\DemiFrontendBundle\Service\SearchFrontend;
  23. use Elements\Bundle\DemiFrontendBundle\Service\VacancyFrontend;
  24. use Elements\Demi\AbstractObject;
  25. use Elements\Demi\Accommodation\Search\Listing\VacancyLocal;
  26. use Elements\Demi\Accommodation\Search\Parameter;
  27. use Elements\Demi\Accommodation\Search\ResultSet;
  28. use Elements\Demi\Accommodation\Search\Service\Vacancies\Corridor;
  29. use Elements\Demi\CustomerFactory;
  30. use Elements\Demi\Deskline\Accommodation\Search\Service\Vacancies\Live;
  31. use Elements\Demi\Deskline\Config;
  32. use Elements\Demi\Deskline\Constant\FacilityGroupTypeInterface;
  33. use Elements\Demi\Deskline\Constant\LinkTypeInterface;
  34. use Elements\Demi\Deskline\DSI\Request\AbstractService;
  35. use Elements\Demi\Deskline\DSI\Request\BasicData\AvailabilityCalendar;
  36. use Elements\Demi\Deskline\DSI\Service\ClickCount;
  37. use Elements\Demi\Deskline\Exception;
  38. use Elements\Demi\Deskline\Service\Search\PaymentInformation;
  39. use Elements\Demi\Model\AccommodationProduct;
  40. use Elements\Demi\Model\AccommodationServiceProvider;
  41. use Elements\Demi\Model\Facility;
  42. use Elements\Demi\Model\HousePackageContainer;
  43. use Elements\Demi\Model\HousePackageMaster;
  44. use Elements\Demi\Model\MealType;
  45. use Elements\Demi\Model\Package;
  46. use Error;
  47. use Jaybizzle\CrawlerDetect\CrawlerDetect;
  48. use Pimcore;
  49. use Pimcore\Db;
  50. use Pimcore\Mail;
  51. use Pimcore\Model\DataObject;
  52. use Pimcore\Templating\Renderer\IncludeRenderer;
  53. use Pimcore\Tool;
  54. use Pimcore\Tool\Authentication;
  55. use Pimcore\Tool\DeviceDetector;
  56. use Pimcore\Tool\Serialize;
  57. use Pimcore\Translation\Translator;
  58. use Symfony\Component\DependencyInjection\ContainerInterface;
  59. use Symfony\Component\HttpFoundation\JsonResponse;
  60. use Symfony\Component\HttpFoundation\RedirectResponse;
  61. use Symfony\Component\HttpFoundation\Request;
  62. use Symfony\Component\HttpFoundation\Response;
  63. use Symfony\Component\HttpKernel\Exception\GoneHttpException;
  64. use Symfony\Component\Security\Core\User\UserInterface;
  65. use Symfony\Component\Validator\Constraints\Email;
  66. use Symfony\Component\Validator\Validator\ValidatorInterface;
  67. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  68. class AccommodationController extends AbstractDemiController
  69. {
  70.     protected array $enabledFeatures = [];
  71.     protected string $trustYouApiKey;
  72.     public function __construct(Configuration            $demiConfigurationHelper, protected SearchFrontend $searchParams, protected VacancyFrontend $vacancyFrontend,
  73.                                 protected VacancyLocal             $vacancyLocal, protected IncludeRenderer $renderer, protected ClickCount $clickCount, protected Corridor $corridor,
  74.                                 protected VacancyLocal             $searchListCorridor, protected Live $vacancyLive, protected DemiUrl $demiUrl, protected PaymentInformation $paymentInfo, protected Customer $customerHelper,
  75.                                 protected CustomerFactory          $customerFactory, protected Payment $paymentHelper, protected ConnectedEntryProvider $connectedEntryProvider, protected Translator $translator,
  76.                                 ElementsCustomDateFormat $elementsCustomDateFormat, protected Newsletter $newsletterHandlerContainerInterface $container, protected SavingRequestController $savingRequestController, protected ?ValidatorInterface $emailValidator null, array $enabledFeatures null)
  77.     {
  78.         $this->setContainer($container);
  79.         $this->setConfiguration($demiConfigurationHelper);
  80.         $this->setElementsCustomDateFormat($elementsCustomDateFormat);
  81.         if (!is_array($enabledFeatures)) {
  82.             $this->enabledFeatures $this->container->hasParameter("elements_demi.enabled_features") ? $this->container->getParameter("elements_demi.enabled_features") : [];
  83.         } else {
  84.             $this->enabledFeatures $enabledFeatures;
  85.         }
  86.         if ($emailValidator === null) {
  87.             $this->emailValidator $this->container->get('validator.email');
  88.         }
  89.         if ($this->container->hasParameter('elements_demi.trustYou.apiKey')) {
  90.             $this->trustYouApiKey $this->container->getParameter('elements_demi.trustYou.apiKey');
  91.         }
  92.     }
  93.     public function fidDirectLinkAction(Request $requeststring $guiid): RedirectResponse
  94.     {
  95.         if (preg_match('/[a-f0-9\-]+/'$guiid)) {
  96.             $serviceProvider AccommodationServiceProvider::getByFid($guiid);
  97.             $redirect $this->needsRedirectToCorrectUrl($request$serviceProvider);
  98.             if ($redirect !== '') {
  99.                 return $this->redirect($redirect301);
  100.             }
  101.         }
  102.         throw new GoneHttpException('Not found');
  103.     }
  104.     protected function redirectAccos(Request $request) :  Response|bool
  105.     {
  106.         $accoId $request->get('id');
  107.         $packageId $request->get('pid');
  108.         if(\Pimcore\Model\Site::isSiteRequest()) {
  109.             $site \Pimcore\Model\Site::getCurrentSite();
  110.             $targetDoc $site->getRootDocument()->getProperty('demi_redirectTargetDocId');
  111.             if($targetDoc){
  112.                 if(!empty($packageId)){
  113.                     //set portal start document to get correct demi config later
  114.                     $this->configuration->setDocument($targetDoc);
  115.                     $mappingArrayPackages $this->getMappingArray('HousePackageMasterMapping_Site_'.$site->getId());
  116.                     $mappingArrayAccos $this->getMappingArray('AccommodationServiceProviderMapping_Site_'.$site->getId());
  117.                     $fid $mappingArrayAccos[$accoId];
  118.                     $pfid $mappingArrayPackages[$packageId];
  119.                     if (!empty($fid) && !empty($pfid)) {
  120.                         $housePackageMaster HousePackageMaster::getByFid($pfid,1);
  121.                         $acco AccommodationServiceProvider::getByFid($fid,1);
  122.                         if ($acco && $housePackageMaster) {
  123.                             $urlHelper $this->demiUrl;
  124.                             $params = [
  125.                                 'accommodation' => $acco,
  126.                                 'package' => $housePackageMaster
  127.                             ];
  128.                             $correctPath $urlHelper->__invoke($params'demi_acco_detail_package_page'true);
  129.                             return $this->redirect($correctPath301);
  130.                         }
  131.                     }
  132.                     return $this->redirect($this->configuration->getAccoParentDocument()->getFullPath(), 301);
  133.                 } else {
  134.                     //set portal start document to get correct demi config later
  135.                     $this->configuration->setDocument($targetDoc);
  136.                     $mappingArray $this->getMappingArray('AccommodationServiceProviderMapping_Site_'.$site->getId());
  137.                     if ($fid $mappingArray[$accoId]) {
  138.                         $acco AccommodationServiceProvider::getByFid($fid,1);
  139.                         if ($acco) {
  140.                             $urlHelper $this->demiUrl;
  141.                             $params = [
  142.                                 'accommodation' => $acco,
  143.                             ];
  144.                             $route 'demi_acco_detail_page';
  145.                             $reset true;
  146.                             $correctPath $urlHelper->__invoke($params$route$reset);
  147.                             return $this->redirect($correctPath301);
  148.                         }
  149.                     }
  150.                     return $this->redirect($this->configuration->getAccoParentDocument()->getFullPath(), 301);
  151.                 }
  152.             }
  153.         }
  154.         return false;
  155.     }
  156.     public function detailAction(Request $requestint $idRobots $robotsUserInterface $user null)
  157.     {
  158.         //portal redirects for domains moving to steiermark.com
  159.         $redirectAccos $this->redirectAccos($request);
  160.         if ($redirectAccos) {
  161.             return $redirectAccos;
  162.         }
  163.         $this->configuration->setDocument($this->document);
  164.         try {
  165.             $accommodation DataObject\DemiAccommodationServiceProvider::getById($id);
  166.             $viewParams $this->getAccommodationData($request$id);
  167.         } catch (GoneHttpException $exception) {
  168.             //before running in to unavailable we could implement redirects for same domain
  169.             //not needed currently; use RedirectController::redirectAccoAction if required again
  170.             if($accommodation && !$accommodation->isPublished() && $accommodation->getProperty('feratelId')){
  171.                 return $this->forward(self::class. '::redirectViaFeratelIdProperty', [
  172.                     'request' => $request,
  173.                     'object' => $accommodation,
  174.                     'demiUrl' => $this->demiUrl
  175.                 ]);
  176.             }
  177.             return $this->forward(self::class. '::unavailableAction', [
  178.                 'request' => $request
  179.             ]);
  180.         }
  181.         $crawlerDetect = new CrawlerDetect();
  182.         if($crawlerDetect->isCrawler()){
  183.             $params $request->query->all();
  184.             if(!empty($params)){
  185.                 $canonical $this->demiUrl->invoke(['accommodation'=>$accommodation],'demi_acco_detail_page',true);
  186.                 file_put_contents(DEMI_LOG_PATH '/blocked_crawler_request.log'"[" Carbon::now()->format("Y-m-d H:i:s") . "] " .  $crawlerDetect->getMatches(). ": " $request->getRequestUri() .PHP_EOLFILE_APPEND LOCK_EX);
  187.                 //bye, bye crawler
  188.                 $this->redirect($canonical,303);
  189.             }
  190.         }
  191.         $redirect $this->needsRedirectToCorrectUrl($request$accommodation);
  192.         if ($redirect !== '') {
  193.             return $this->redirect($redirect301);
  194.         }
  195.         if ($redirect $this->needsTimeStringRedirect($request)) {
  196.             return $this->redirect($redirect301);
  197.         }
  198.         if($accommodation->getProperty('seo_noindex')) {
  199.             $robots->setNoIndex(true);
  200.         }
  201.         $viewParams['isAffiliate'] = $this->setIsAffiliate();
  202.         $viewParams['trustYouApiKey'] = $this->trustYouApiKey;
  203.         /**
  204.          * @var $searchParams Parameter
  205.          */
  206.         $ignorePrice $request->get('enquire') || $request->get('enquirySuccess');
  207.         $searchParams $this->searchParams->getSearchParam(null$ignorePrice);
  208.         $viewParams['user'] = $user;
  209.         $viewParams['searchParam'] = $searchParams;
  210.         $searchParams->setAccoIds([$id]);
  211.         if ($request->get('enquire')) {
  212.             $success $this->sendEnquire($request$id$searchParams$user);
  213.             if ($success) {
  214.                 $queryString $request->getQueryString();
  215.                 $queryString str_replace('enquire''enquirySuccess'$queryString);
  216.                 $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  217.                 return $this->redirect($request->getPathInfo() . '/enquire?'.$queryString);
  218.             }
  219.             throw new Exception('can not sent enquiry - dsi saving request was not successful');
  220.         }
  221.         if ($request->get('enquirySuccess')) {
  222.             $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  223.             $document $this->configuration->getEnquireThankYouPage();
  224.             $request->query->remove('enquirySuccess');
  225.             return new Response($this->renderer->render($document, ['id' => $id]));
  226.         }
  227.         $viewParams['accommodationResultSet'] = $this->vacancyFrontend->getAccommodationResultSetLocal($searchParams,
  228.             $id);
  229.         $productSearchParams = clone $searchParams;
  230.         $productSearchParams->setSearchType(Parameter::SEARCH_TYPE_EACH_PRODUCT_SEPARATED);
  231.         $productSearchParams->setProductType('Accommodation');
  232.         $viewParams['productResultSet'] = $this->vacancyFrontend->getAccommodationResultSetLocal($productSearchParams,
  233.             $id);
  234.         if (!$searchParams->getDateFrom()) {
  235.             $viewParams['packages'] = $this->vacancyFrontend->preparePackageMasters($accommodation,
  236.                 $this->vacancyLocal);
  237.         }
  238.         if ($this->enabledFeatures['destinationPackages'] ?? false) {
  239.             $packages \Elements\Demi\Accommodation\Search\Listing\Package::getPackagesOfferedByAccommodation($accommodation$searchParams);
  240.             if (count($packages)) {
  241.                 //get rid of published packages in the past
  242.                 $packages $this->searchParams->filterDestinationPackagesDate($packagesCarbon::now(), null);
  243.             }
  244.             $viewParams['destinationPackages'] = $packages;
  245.         } else {
  246.             $viewParams['destinationPackages'] = null;
  247.         }
  248.         $housePackageMasters $accommodation->getHousePackageMasters();
  249.         //check if Accommodation has packages
  250.         $hasPackageValidDates false;
  251.         foreach ($housePackageMasters as $housePackageMaster) {
  252.             $validDates $housePackageMaster->getValidDates();
  253.             if ($validDates instanceof DataObject\Fieldcollection) {
  254.                 $dates $validDates->getItems();
  255.                 if (is_array($dates) && count($dates) > 0) {
  256.                     foreach ($dates as $item) {
  257.                         if ($item->getTo()->getTimestamp() > time()) {
  258.                             $hasPackageValidDates true;
  259.                             break 2;
  260.                         }
  261.                     }
  262.                 }
  263.             }
  264.         }
  265.         $viewParams['connectedEntries'] = $this->connectedEntryProvider->getConnectedEntriesForAcco($accommodation$request);
  266.         $viewParams['hasHousePackages'] = $hasPackageValidDates;
  267.         $viewParams['availabilityIsChecked'] = !$searchParams->isNoDate();
  268.         $viewParams['activeTab'] = $request->get('activeTab');
  269.         $viewParams['googleMapsKey'] = Pimcore\Config::getSystemConfiguration()['services']['google']['browser_api_key'] ?? Pimcore\Config::getSystemConfiguration()['services']['google']['simple_api_key'];
  270.         $viewParams['showJsonLd'] = count($_GET) == 0;
  271.         $deviceDetector Tool\DeviceDetector ::getInstance();
  272.         $device $deviceDetector->isPhone() ? 'mobile' 'desktop';
  273.         $path $viewParams['isAffiliate'] ? 'Affiliate' 'Accommodation';
  274.         return $this->renderTemplate('@ElementsDemiFrontend/'.$path.'/detail.'.$device.'.html.twig'$viewParams);
  275.     }
  276.     public function destinationpackageAction(Request $requestint $idint $pidRobots $robotsUserInterface $user null): Response
  277.     {
  278.         $object Package::getById($pid);
  279.         $accommodation AccommodationServiceProvider::getById($request->get('id'));
  280.         if (!$object || !$object->isPublished() || !$accommodation->isPublished()) {
  281.             return $this->forward(self::class. '::unavailableAction', [
  282.                 'request' => $request
  283.             ]);
  284.         }
  285.         $redirect $this->needsRedirectToCorrectUrl($request$accommodation$object);
  286.         if ($redirect !== '') {
  287.             return $this->redirect($redirect);
  288.         }
  289.         if($accommodation->getProperty('seo_noindex') || $object->getProperty('seo_noindex')) {
  290.             $robots->setNoIndex();
  291.         }
  292.         if ($object->getValidDates()) {
  293.             $endDate null;
  294.             foreach ($object->getValidDates()->getItems() as $item) {
  295.                 if ($endDate == null) {
  296.                     $endDate $item->getDateTo();
  297.                 } elseif ($endDate->lt($item->getDateTo())) {
  298.                     $endDate $item->getDateTo();
  299.                 }
  300.             }
  301.             if ($endDate != null) {
  302.                 $endDate $endDate->endOfDay()->toRfc850String();
  303.                 $this->addResponseHeader('X-Robots-Tag''unavailable_after: ' $endDate);
  304.             }
  305.         }
  306.         /**
  307.          * @var $searchParams Parameter
  308.          */
  309.         $searchParams $this->searchParams->getPackageSearchParam(null$this->editmode);
  310.         if ($request->get('enquire')) {
  311.             //TODO Enquiry is in AccommodationController ... maybe move detail to Accommodation Controller? @msteyer: Is irrelevant?
  312.             $this->sendEnquire($request$id$searchParams$user);
  313.             return $this->redirect($request->getPathInfo() . '/enquire?enquirySuccess=true');
  314.         }
  315.         if ($request->get('enquirySuccess')) {
  316.             $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  317.             $document $this->configuration->getEnquireThankYouPage();
  318.             $request->query->remove('enquirySuccess');
  319.             return new Response($this->renderer->render($document, ['id' => $id'pid' => $pid]));
  320.         }
  321.         $viewParams $this->getAccommodationData($request$id$pid);
  322.         if (!isset($_COOKIE['pimcore_admin_sid']) && !$object->isPublished() && !$this->editmode && !$request->get('pimcore_object_preview')) {
  323.             throw new GoneHttpException('Not found');
  324.         }
  325.         $viewParams['trustYouApiKey'] = $this->trustYouApiKey;
  326.         $viewParams['searchParam'] = $searchParams;
  327.         $viewParams['user'] = $user;
  328.         $viewParams['tvbPackage'] = $object;
  329.         $viewParams['availabilityIsChecked'] = ($searchParams->getDateFrom() != null);
  330.         $viewParams['activeTab'] = $request->get('activeTab');
  331.         $viewParams['googleMapsKey'] = Pimcore\Config::getSystemConfiguration()['services']['google']['browser_api_key'] ?? Pimcore\Config::getSystemConfiguration()['services']['google']['simple_api_key'];
  332.         $viewParams['showJsonLd'] = count($_GET) === 0;
  333.         $deviceDetector Tool\DeviceDetector ::getInstance();
  334.         $device $deviceDetector->isPhone() ? 'mobile' 'desktop';
  335.         return $this->renderTemplate('@ElementsDemiFrontend/Accommodation/destinationpackage.'.$device.'.html.twig'$viewParams);
  336.     }
  337.     public function packageAction(Request $requestint $idint $pidRobots $robotsUserInterface $user null): Response
  338.     {
  339.         //portal redirects for domains moving to steiermark.com
  340.         $redirectAccos $this->redirectAccos($request);
  341.         if ($redirectAccos) {
  342.             return $redirectAccos;
  343.         }
  344.         $object HousePackageMaster::getById($pid);
  345.         $accommodation AccommodationServiceProvider::getById($request->get('id'));
  346.         if ($accommodation === null || $object === null || !$object->isPublished() || !$accommodation->isPublished()) {
  347.             //before running in to unavailable we could implement redirects for same domain
  348.             //not needed currently; use RedirectController::redirectHousepackageAction if required again
  349.             return $this->forward(self::class. '::unavailableAction', [
  350.                 'request' => $request
  351.             ]);
  352.         }
  353.         $queryBuilder Db::getConnection()->createQueryBuilder();
  354.         $queryBuilder->select('o_id from object_' DataObject\DemiHousePackageContainer::classId())
  355.             ->where('o_id in (select src_id from object_relations_' DataObject\DemiHousePackageContainer::classId() . " where
  356.             dest_id in (select dest_id from object_relations_{$object::classId()} where src_id = $pid) and fieldname = 'products')");
  357.         $res $queryBuilder->execute()->fetchAll();
  358.         if (!empty($res)) {
  359.             $hpc HousePackageContainer::getById(reset($res)['o_id']);
  360.             if ($hpc instanceof HousePackageContainer) {
  361.                 $viewParams['housePackageContainer'] = $hpc;
  362.             }
  363.         }
  364.         $redirect $this->needsRedirectToCorrectUrl($request$accommodation$object);
  365.         if ($redirect !== '') {
  366.             return $this->redirect($redirect301);
  367.         }
  368.         if($accommodation->getProperty('seo_noindex') || $object->getProperty('seo_noindex')) {
  369.             $robots->setNoIndex();
  370.         }
  371.         if ($redirect $this->needsTimeStringRedirect($request)) {
  372.             return $this->redirect($redirect301);
  373.         }
  374.         if ($object->getValidDates()) {
  375.             $endDate null;
  376.             foreach ($object->getValidDates()->getItems() as $item) {
  377.                 if ($endDate === null) {
  378.                     $endDate $item->getTo();
  379.                 } elseif ($endDate->lt($item->getTo())) {
  380.                     $endDate $item->getTo();
  381.                 }
  382.             }
  383.             if ($endDate !== null) {
  384.                 $endDate $endDate->endOfDay()->toRfc850String();
  385.                 $this->addResponseHeader('X-Robots-Tag''unavailable_after: ' $endDate);
  386.             }
  387.         }
  388.         $ignorePrice $request->get('enquire') || $request->get('enquirySuccess');
  389.         $searchParams $this->searchParams->getSearchParam(null$ignorePrice);
  390.         if ($request->get('enquire')) {
  391.             $this->sendEnquire($request$id$searchParams$user);
  392.             return $this->redirect($request->getPathInfo() . '/enquire?enquirySuccess=true');
  393.         }
  394.         if ($request->get('enquirySuccess')) {
  395.             $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  396.             $document $this->configuration->getEnquireThankYouPage();
  397.             $request->query->remove('enquirySuccess');
  398.             return new Response($this->renderer->render($document, ['id' => $id'pid' => $pid]));
  399.         }
  400.         $viewParams $this->getAccommodationData($request$id);
  401.         if ((!isset($_COOKIE['pimcore_admin_sid']) && !$this->editmode && !$object->isPublished() && !$request->get('pimcore_object_preview'))) {
  402.             throw new GoneHttpException('Not found');
  403.         }
  404.         $viewParams['trustYouApiKey'] = $this->trustYouApiKey;
  405.         $viewParams['isAffiliate'] = $this->setIsAffiliate();
  406.         $viewParams['searchParam'] = $searchParams;
  407.         $viewParams['user'] = $user;
  408.         $viewParams['housePackageMaster'] = $object;
  409.         $viewParams['availabilityIsChecked'] = !$searchParams->isNoDate();
  410.         $this->vacancyLocal->setParameter($searchParams);
  411.         $resultSet $this->vacancyFrontend->preparePackageMasters($accommodation,
  412.             $this->vacancyLocal$object);
  413.         $viewParams['housePackageResultSet'] = $resultSet;
  414.         $viewParams['activeTab'] = $request->get('activeTab');
  415.         $viewParams['useLts'] = false;
  416.         $viewParams['googleMapsKey'] = Pimcore\Config::getSystemConfiguration()['services']['google']['browser_api_key'] ?? Pimcore\Config::getSystemConfiguration()['services']['google']['browser_api_key'] ?? null;
  417.         $viewParams['showJsonLd'] = empty($request->query->all());
  418.         $deviceDetector Tool\DeviceDetector ::getInstance();
  419.         $device $deviceDetector?->isPhone() ? 'mobile' 'desktop';
  420.         $path $viewParams['isAffiliate'] ? 'Affiliate' 'Accommodation';
  421.         return $this->renderTemplate('@ElementsDemiFrontend/'.$path.'/package.'.$device.'.html.twig'$viewParams);
  422.     }
  423.     private function getAccommodationData(Request $requestint $idint $pid null): array
  424.     {
  425.         $object AccommodationServiceProvider::getById($id);
  426.         if ($object === null || (!isset($_COOKIE['pimcore_admin_sid']) && !$object->isPublished() && !$this->editmode && !$request->get('pimcore_object_preview'))) {
  427.             throw new GoneHttpException('Not found');
  428.         }
  429.         $user Authentication::authenticateSession();
  430.         $activePimcoreUser false;
  431.         if ($user) {
  432.             $activePimcoreUser true;
  433.         }
  434.         if ($object instanceof AccommodationServiceProvider) {
  435.             if ($object->getIsTesthotel() && !(Pimcore::inDebugMode() || $activePimcoreUser)) {
  436.                 throw new GoneHttpException('Not found');
  437.             }
  438.             Config::setClientKeyOverrideFromObject($object);
  439.         }
  440.         $viewParams['countingClicksUriJs'] = $this->clickCount->getClickCountUrl($object);
  441.         $viewParams['accommodation'] = $object;
  442.         // Marketinggroups
  443.         $marketingGroups $object->getMarketingGroups();
  444.         uasort($marketingGroups, function ($a$b) {
  445.             return $a->getOrder() < $b->getOrder();
  446.         });
  447.         foreach ($marketingGroups as $i => $iValue) {
  448.             //remove MarketingGroups which should not be shown on web
  449.             if (!$iValue->getShowOnWeb()) {
  450.                 unset($marketingGroups[$i]);
  451.             }
  452.         }
  453.         $viewParams['sortedMarketingGroups'] = $marketingGroups;
  454.         // Facilities
  455.         $facilities $object->getFacilities();
  456.         $sortedFacilities = [];
  457.         $sortedHealthMeasures = [];
  458.         if ($facilities !== null) {
  459.             foreach ($facilities as $facilityItem) {
  460.                 $facility $facilityItem->getFacility();
  461.                 if ($facility instanceof Facility) {
  462.                     $facGroup $facility->getFacilityGroup();
  463.                     if ($facGroup) {
  464.                         if ($facGroup->getFacilityGroupType() == FacilityGroupTypeInterface::FACILITY_GROUP_TYPE_HEALTH) {
  465.                             if (!isset($sortedHealthMeasures[$facGroup->getId()])) {
  466.                                 $data = [
  467.                                     'name' => $facGroup->getName(),
  468.                                     'keyname' => $facGroup->getDemiIconClass(),
  469.                                     'facilities' => []
  470.                                 ];
  471.                                 $sortedHealthMeasures[$facGroup->getId()] = $data;
  472.                             }
  473.                         } elseif (!isset($sortedFacilities[$facGroup->getId()])) {
  474.                             $data = [
  475.                                 'name' => $facGroup->getName(),
  476.                                 'keyname' => $facGroup->getDemiIconClass(),
  477.                                 'facilities' => []
  478.                             ];
  479.                             $sortedFacilities[$facGroup->getId()] = $data;
  480.                         }
  481.                         $facName $facility->getName();
  482.                         if ($facility->getValueType() === 'IntDigit') {
  483.                             $facName .= ': ' $facilityItem->getFacilityValue();
  484.                         }
  485.                         if ($facGroup->getFacilityGroupType() == FacilityGroupTypeInterface::FACILITY_GROUP_TYPE_HEALTH) {
  486.                             $sortedHealthMeasures[$facGroup->getId()]['facilities'][] = $facName;
  487.                         } else {
  488.                             $sortedFacilities[$facGroup->getId()]['facilities'][] = $facName;
  489.                         }
  490.                     }
  491.                 }
  492.             }
  493.         }
  494.         //sort alphabetically within facility group - tosc does the same
  495.         if (!empty($sortedFacilities)) {
  496.             foreach ($sortedFacilities as $facilities) {
  497.                 sort($facilities['facilities']);
  498.             }
  499.         }
  500.         $viewParams['sortedHealthMeasures'] = $sortedHealthMeasures;
  501.         $viewParams['sortedFacilities'] = $sortedFacilities;
  502.         // Impressionen
  503.         $panoLinks $object->demiGetLinks([LinkTypeInterface::DESKLINE_LINK_PANO]);
  504.         $types = [LinkTypeInterface::DESKLINE_LINK_VIDEO_EMBEDLinkTypeInterface::DESKLINE_LINK_VIDEOLinkTypeInterface::DESKLINE_LINK_VIDEO_VIMEO];
  505.         $videoLinks $object->demiGetLinks($types);
  506.         if ($pid && $package Package::getById($pid)) {
  507.             $panoLinks array_merge($panoLinks$package->demiGetLinks([LinkTypeInterface::DESKLINE_LINK_PANO]));
  508.             $videoLinks array_merge($videoLinks$package->demiGetLinks($types));
  509.         }
  510.         $linkGroups = [
  511.             'video' => $videoLinks,
  512.             'pano' => $panoLinks
  513.         ];
  514.         $viewParams['impressionLinks'] = $linkGroups;
  515.         // Links
  516.         $accoLinks $object->demiGetLinks([
  517.             LinkTypeInterface::DESKLINE_LINK_URL,
  518.             LinkTypeInterface::DESKLINE_LINK_NOTDEFINED
  519.         ]);
  520.         $viewParams['accoLinks'] = $accoLinks;
  521.         $viewParams['showAvailablityCalendar'] = $this->enabledFeatures['availabilityCalenderRooms'] ?? false;
  522.         return $viewParams;
  523.     }
  524.     private function sendEnquire(Request $requestint $idParameter $searchParamsUserInterface $user null): bool
  525.     {
  526.         if ($this->configuration->getDisableEnquiry()) {
  527.             return false// Just return since detailEnquireAction is called from detailAction
  528.         }
  529.         $this->configuration->getEnquireMailServiceProvider();
  530.         $housePackageId $request->get('pid');
  531.         /** @var AccommodationServiceProvider $acco */
  532.         $acco AccommodationServiceProvider::getById($id);
  533.         if ($housePackageId) {
  534.             $package HousePackageMaster::getById($housePackageId);
  535.         } else {
  536.             $package null;
  537.         }
  538.         // date
  539.         $defaultFrom $request->get('from-no-date') ? $this->elementsCustomDateFormat->stringToCarbon($request->get('from-no-date')) : new Carbon();
  540.         $defaultTo $request->get('to-no-date') ? $this->elementsCustomDateFormat->stringToCarbon($request->get('to-no-date')) : new Carbon();
  541.         $fromDate = empty($request->get('from')) ? $defaultFrom $this->elementsCustomDateFormat->stringToCarbon($request->get('from'));
  542.         $toDate = empty($request->get('to')) ? $defaultTo $this->elementsCustomDateFormat->stringToCarbon($request->get('to'));
  543.         //nights
  544.         $nightsFromParam $searchParams->getNights();
  545.         if (count($nightsFromParam) > 1) {
  546.             $nights $nightsFromParam[0].'-'.$nightsFromParam[count($nightsFromParam) - 1];
  547.             $nightsForDSI $nightsFromParam[0];
  548.         } else {
  549.             $nights $toDate->diff($fromDate)->days;
  550.             $nightsForDSI $toDate->diff($fromDate)->days;
  551.         }
  552.         $request->query->set('nights'$nights);
  553.         $request->query->set('from'ElementsCustomDateFormat::toString($fromDate));
  554.         $request->query->set('to'ElementsCustomDateFormat::toString($toDate));
  555.         $mailParams $request->request->all();
  556.         $mailParams['salutation'] = AbstractObject::getById($mailParams['salutation']) ? AbstractObject::getById($mailParams['salutation'])->getTitle() : '';
  557.         $mailParams['from'] = $request->get('from');
  558.         $mailParams['to'] = $request->get('to');
  559.         $filteredKeys array_filter(array_keys($mailParams), function ($key) {
  560.             return !str_starts_with($key'_');
  561.         });
  562.         $mailParams array_intersect_key($mailParamsarray_flip($filteredKeys));
  563.         $this->emailValidator->validate($mailParams['email'], new Email());
  564.         $roomRows $request->get('roomRow');
  565.         $products = [];
  566.         if ($roomRows) {
  567.             foreach ($roomRows as $key => $roomRow) {
  568.                 $pid $request->get('productId')[$key];
  569.                 $mealCode $request->get('mealCode')[$key];
  570.                 $units $request->get('count') ? $request->get('count')[$key] : 1;
  571.                 $products[] = ['id' => $pid'mealCode' => $mealCode'units' => $units'roomRowKey' => $roomRow];
  572.             }
  573.         }
  574.         //privacy policy is no longer a checkbox
  575.         /*
  576.         if (!empty($this->configuration->getPrivacyPolicyEnquiry())) {
  577.             $ppAccepted = (bool)$request->get('pp');
  578.         } else {
  579.             $ppAccepted = true;
  580.         }
  581.         */
  582.         //just assume ppAccepted, it no longer exists
  583.         $ppAccepted=true;
  584.         if ($this->configuration->getUseDSI()) {
  585.             $request->query->set('nights'$nightsForDSI);
  586.             $request->query->set('accoIds', [$id]);
  587.             $request->query->set('pp'$ppAccepted);
  588.             $request->query->set('price'null);
  589.             $productIds $request->get('productId');
  590.             $request->query->set('comment'$this->getCommentForDSIEnquiry($request$package$products$nightsFromParam));
  591.             if ($productIds == null || (count($productIds) > 1)) {
  592.                 $request->query->set('productId', []);
  593.             } else {
  594.                 $request->query->set('productId'reset($productIds));
  595.             }
  596.             $mailSent $this->savingRequestController->serviceProviderRequestAction($request$user);
  597.         } else {
  598.             $mailSent $this->sendEnquireMail($mailParams$searchParams$request$acco$package$products$ppAcceptedfalse);
  599.             if ($mailSent && $this->newsletterHandler && $request->get('newsletter') === 'on') {
  600.                 try {
  601.                     //handle newsletter subscripton
  602.                     $emptyCustomer null;
  603.                     $this->newsletterHandler->addRecipient($request->get('email'), $request->get('firstname'), $request->get('surname'), ['request' => $request], $emptyCustomer$emptyCustomer);
  604.                 } catch (\Exception $exception) {
  605.                     if (Pimcore::inDebugMode()) {
  606.                         //silently ignore newsletter subscription errors in live mode, show them in debug
  607.                         throw $exception;
  608.                     }
  609.                 }
  610.             }
  611.         }
  612.         try {
  613.             $eventDispatcher Pimcore::getEventDispatcher();
  614.             $eventDispatcher->dispatch(new DemiDirectInquiryEvent($searchParams$request, []), DemiDirectInquiryEvent::POST_DIRECTINQUIRY_EVENT);
  615.         } catch (Error $e) {
  616.         }
  617.         return $mailSent;
  618.     }
  619.     /**
  620.      * @param Request $request
  621.      * @param $package
  622.      * @param $products
  623.      * @param $nights
  624.      *
  625.      * @return mixed|string
  626.      */
  627.     private function getCommentForDSIEnquiry(Request $requestnull|HousePackageMaster|HousePackageContainer|Package $package, array $productsint|array $nights): mixed
  628.     {
  629.         $comment $request->get('comment');
  630.         if ($package !== null) {
  631.             $comment .= ', Package ' $package->getName() . ':';
  632.         }
  633.         foreach ($products as $productInfo) {
  634.             $product AbstractObject::getById($productInfo['id']);
  635.             $mealType MealType::getById($productInfo['mealCode']);
  636.             $units $productInfo['units'];
  637.             if ($product) {
  638.                 $prodStr '';
  639.                 if ($units) {
  640.                     $prodStr .= $units 'x ';
  641.                 }
  642.                 $prodStr .= $product->getName();
  643.                 if ($mealType) {
  644.                     $prodStr .= ' | ' $mealType->getText();
  645.                 }
  646.                 $comment .= $prodStr;
  647.             }
  648.         }
  649.         if ($request->get('tel')) {
  650.             $comment .= ', Tel.: '.$request->get('tel');
  651.         }
  652.         if (is_array($nights) && count($nights) > 1) {
  653.             $comment .= ", {$this->translator->trans('demi.nights')}{$nights[0]} - {$nights[count($nights) - 1]}";
  654.         }
  655.         return $comment;
  656.     }
  657.     private function sendEnquireMail($mailParams$searchParams$request$acco$package$products$ppAccepted$mailSent)
  658.     {
  659.         try {
  660.             $spamBotAddress $request->get('address');
  661.             $mailParams['nights'] = $request->get('nights');
  662.             if (empty($spamBotAddress) && $ppAccepted) {
  663.                 if ($this->configuration->getEnquireMailServiceProvider() != '') {
  664.                     $fallback $this->configuration->getEnquiryServiceProviderFallback();
  665.                     $alwaysUseFallback $this->configuration->getUseEnquiryServiceProviderFallbackAsOverride();
  666.                     //sending the email
  667.                     $mail = new Mail(nullnullnull);
  668.                     if (Tool::getClientIp() == $this->testIp) {
  669.                         if ($request->get('email')) {
  670.                             $mail->addTo($request->get('email'));
  671.                         } else {
  672.                             $mail->addTo('anna.huber.elements@gmail.com');
  673.                         }
  674.                         $mail->disableLogging();
  675.                     } elseif (!$alwaysUseFallback && $acco->getAddress() && $acco->getAddress()->getEmail()) {
  676.                         $mail->addTo(trim($acco->getAddress()->getEmail()));
  677.                     } elseif ($fallback != '') {
  678.                         $fallbacks explode(','$fallback);
  679.                         foreach ($fallbacks as $fallback) {
  680.                             $mail->addTo($fallback);
  681.                         }
  682.                     } else {
  683.                         throw new Exception("can not sent enquiry - no email on acco {$acco->getId()} and no property demi_enquiryServiceProviderFallback !");
  684.                     }
  685.                     $mail->addReplyTo($request->get('email'));
  686.                     $mail->setDocument($this->configuration->getEnquireMailServiceProvider());
  687.                     $mail->setParams($mailParams);
  688.                     $mail->setParam('searchParam'$searchParams);
  689.                     $mail->setParam('acco'$acco);
  690.                     $mail->setParam('package'$package);
  691.                     $mail->setParam('products'$products);
  692.                     // $this->logEnquire($acco);
  693.                     $mail->send();
  694.                     $mailSent true;
  695.                 } else {
  696.                     throw new Exception("missing property on enquiryServiceProvider {$this->document}!");
  697.                 }
  698.                 if ($this->configuration->getEnquireCustomer() != '') {
  699.                     //sending the email
  700.                     $customerMail = new Mail(nullnullnull'utf-8');
  701.                     $customerMail->addTo($request->get('email'));
  702.                     $customerMail->setDocument($this->configuration->getEnquireCustomer());
  703.                     $customerMail->setParams($mailParams);
  704.                     $customerMail->setParam('searchParam'$searchParams);
  705.                     $customerMail->setParam('acco'$acco);
  706.                     $customerMail->setParam('package'$package);
  707.                     $customerMail->setParam('products'$products);
  708.                     if (Tool::getClientIp() == $this->testIp) {
  709.                         $customerMail->disableLogging();
  710.                     }
  711.                     $customerMail->send();
  712.                 }
  713.             }
  714.         } catch (\Exception $e) {
  715.             $mail = new Mail(nullnullnull);
  716.             $mail->setSubject('Fehler direktanfrage ');
  717.             $mail->addTo('deskline@elements.at');
  718.             $mail->text($e->getMessage());
  719.             $mail->send();
  720.         }
  721.         return $mailSent;
  722.     }
  723.     public function accommodationTeaserCompactAction(Request $request): Response
  724.     {
  725.         $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  726.         $accoId $request->get('id');
  727.         if (!$accoId) {
  728.             return new Response('no id given');
  729.         }
  730.         $searchParams $this->searchParams->getSearchParam(nullfalse,  false);
  731.         $searchParams->setAccoIds([$accoId]);
  732.         $productType Parameter::PRODUCT_TYPE_ACCOMMODATION;
  733.         if ($searchParams->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE) {
  734.             $productType Parameter::PRODUCT_TYPE_PACKAGE;
  735.         }
  736.         if ($searchParams->getIsCorridor()) {
  737.             $this->searchListCorridor->setParameter($searchParams);
  738.             $accommodations $this->searchListCorridor->getResultSet()->getAccommodations();
  739.             $resultSet reset($accommodations);
  740.         } else {
  741.             $resultSet $this->vacancyFrontend->getAccommodationResultSetLocal($searchParams,
  742.                 $accoId$productType);
  743.         }
  744.         $viewParams['accommodationResult'] = $resultSet;
  745.         $viewParams['accommodation'] = AccommodationServiceProvider::getById($accoId);
  746.         $viewParams['searchParam'] = $searchParams;
  747.         $viewParams['noDate'] = $searchParams->isNoDate();
  748.         $viewParams['freeCancellationInList'] = $this->enabledFeatures['freeCancellationInList'];
  749.         $device DeviceDetector::getInstance()->isPhone() ? 'mobile' 'desktop';
  750.         return $this->renderTemplate('@ElementsDemiFrontend/Accommodation/accommodationTeaserCompact.'.$device.'.html.twig'$viewParams);
  751.     }
  752.     public function lightboxTopbarContentAction(Request $request): Response
  753.     {
  754.         if ($request->get('tid')) {
  755.             return $this->forward(DestinationPackageController::class . '::lightboxTopbarContentAction', [
  756.                 'request' => $request
  757.             ]);
  758.         }
  759.         $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  760.         $accoId trim($request->get('id'));
  761.         $accommodation AccommodationServiceProvider::getById($accoId);
  762.         $searchParams $this->searchParams->getSearchParam(nullfalse);
  763.         $searchParams->setAccoIds([$accoId]);
  764.         $productType Parameter::PRODUCT_TYPE_ACCOMMODATION;
  765.         if ($searchParams->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE) {
  766.             $productType Parameter::PRODUCT_TYPE_PACKAGE;
  767.         }
  768.         if ($searchParams->getIsCorridor()) {
  769.             $this->searchListCorridor->setParameter($searchParams);
  770.             $accommodationsCorridor $this->searchListCorridor->getResultSet()->getAccommodations();
  771.             $resultSet reset($accommodationsCorridor);
  772.         } elseif ($searchParams->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE || !empty($request->get('pid'))) {
  773.             $housePackageMaster HousePackageMaster::getById((int)$request->get('pid'));
  774.             if ($searchParams->getDateFrom() && $searchParams->getDateTo()) {
  775.                 $this->vacancyLive->setSearchParameter($searchParams);
  776.                 $resultSet ResultSet\Accommodation::getById($accoId$this->vacancyLive);
  777.             } else {
  778.                 $this->vacancyLocal->setParameter($searchParams);
  779.                 $resultSet $this->vacancyFrontend->preparePackageMasters($accommodation,
  780.                     $this->vacancyLocal$housePackageMasterfalse);
  781.             }
  782.         } elseif ($searchParams->getDateFrom() && $searchParams->getDateTo()) {
  783.             $adapter $this->vacancyLive;
  784.             $adapter->setSearchParameter($searchParams);
  785.             $resultSet ResultSet\Accommodation::getById($accoId$adapter);
  786.         } elseif ($accommodation) {
  787.             $resultSet $this->vacancyFrontend->getAccommodationResultSetLocal($searchParams,
  788.                 $accoId$productType);
  789.         }
  790.         $viewParams['accommodationResult'] = is_array($resultSet) ? reset($resultSet) : $resultSet;
  791.         $viewParams['accommodation'] = $accommodation;
  792.         $viewParams['housePackageMaster'] = $housePackageMaster ?? null;
  793.         $viewParams['searchParam'] = $searchParams;
  794.         $viewParams['noDate'] = $searchParams->isNoDate();
  795.         $viewParams['availabilityIsChecked'] = !$searchParams->isNoDate();
  796.         $viewParams['isList'] = $request->get('viewType') === 'list';
  797.         $viewParams['isPackageDetailPage'] = $request->get('viewType') === 'package';
  798.         return $this->renderTemplate('@ElementsDemiFrontend/Accommodation/lightboxTopbarContent.html.twig'$viewParams);
  799.     }
  800.     public function ajaxAction(Request $request): Response
  801.     {
  802.         $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  803.         $accoId $request->get('id');
  804.         $accommodation AccommodationServiceProvider::getById((int)$accoId);
  805.         if (!$request->isXmlHttpRequest() || $accommodation === null || (!isset($_COOKIE['pimcore_admin_sid']) && !$accommodation->isPublished() && !$this->editmode && !$request->get('pimcore_object_preview'))) {
  806.             throw new GoneHttpException('Not found');
  807.         }
  808.         $packageId $request->get('pid');
  809.         if ($packageId && Package::getById($packageId)) {
  810.             //we are dealing with TVB Package -> go to DestinationPackageController
  811.             return $this->forward(DestinationPackageController::class . '::ajaxAction', [
  812.                 'request' => $request
  813.             ]);
  814.         }
  815.         $requestType $request->get('ajaxRequestType');
  816.         return match ($requestType) {
  817.             'getProductByIdAndMealcode' => $this->forward(self::class . '::ajaxGetProductByIdAndMealcodeAction', [
  818.                 'request' => $request
  819.             ]),
  820.             'getPaymentInformation' => $this->forward(self::class . '::ajaxGetPaymentInformationAction', [
  821.                 'request' => $request
  822.             ]),
  823.             'getLiveDetailProducts' => $this->forward(self::class . '::ajaxGetLiveDetailProductsAction', [
  824.                 'request' => $request,
  825.                 'isAffiliate' => $this->setIsAffiliate()
  826.             ]),
  827.             'getCorridorMatrix' => $this->forward(self::class . '::ajaxGetCorridorMatrixAction', [
  828.                 'request' => $request
  829.             ]),
  830.             'getAvailabilityCalendar' => $this->forward(self::class . '::ajaxGetAvailabilityCalendarAction', [
  831.                 'request' => $request
  832.             ]),
  833.             default => throw new GoneHttpException('Not found - no matching request type'),
  834.         };
  835.     }
  836.     public function ajaxGetAvailabilityCalendarAction(Request $request): JsonResponse
  837.     {
  838.         $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  839.         $months $request->get('months') ? (int) $request->get('months') : 3;
  840.         $product null;
  841.         if ($request->get('productId')) {
  842.             $product AccommodationProduct::getById($request->get('productId'));
  843.         }
  844.         $accoId $request->get('id');
  845.         $accommodation AccommodationServiceProvider::getById($accoId);
  846.         if ($accommodation === null || (!isset($_COOKIE['pimcore_admin_sid']) && !$accommodation->isPublished() && !$this->editmode && !$request->get('pimcore_object_preview'))) {
  847.             throw new GoneHttpException('Not found');
  848.         }
  849.         Config::setClientKeyOverrideFromObject($accommodation);
  850.         $startDate = new Carbon();
  851.         if ($request->get('startDate')) {
  852.             $dateFormatHelper = new ElementsCustomDateFormat();
  853.             $startDate $dateFormatHelper->stringToCarbon($request->get('startDate'));
  854.         } else {
  855.             $startDate->setDate($startDate->year$startDate->month1);
  856.         }
  857.         try {
  858.             $dsiRequest = new AvailabilityCalendar(Config::getInstance(), Config::getInstance()->getSalesChannel(), $startDate$months$accommodation$product);
  859.             $requestService AbstractService::getService();
  860.             $responseXml $requestService->makeRequest($dsiRequestAbstractService::REQUEST_TYPE_AVAILABILITY_CALENDARfalse);
  861.             $data = [];
  862.             foreach ($responseXml->AvailabilityCalendar->AV as $dateEntry) {
  863.                 $lineData explode(','$dateEntry);
  864.                 $availablity = (int)$lineData[1];
  865.                 $data[] = [
  866.                     'date' => $lineData[0] . 'T00:00:00',
  867.                     'availability' => max($availablity0),
  868.                     'minStay' => $lineData[2],
  869.                     'maxStay' => $lineData[3],
  870.                     'intervall' => $lineData[4],
  871.                     'arrival' => $lineData[5],
  872.                     'departure' => $lineData[6]
  873.                 ];
  874.             }
  875.         } catch (Exception $desklineException) {
  876.             //no availabilities results in Exception. That is ok - return array with empty availabilities
  877.             $data = [];
  878.             $from $startDate;
  879.             $to = clone $startDate;
  880.             $to->addMonths(3);
  881.             while($from <= $to) {
  882.                 $data[] = [
  883.                     'date' => $from->format('Y-m-d') . 'T00:00:00',
  884.                     'availability' => 0,
  885.                     'minStay' => 0,
  886.                     'maxStay' => 0,
  887.                     'intervall' => 0,
  888.                     'arrival' => 0,
  889.                     'departure' => 0
  890.                 ];
  891.                 $from->addDay();
  892.             }
  893.             return $this->json($data);
  894.         }
  895.         return $this->json($data);
  896.     }
  897.     public function ajaxGetLiveDetailProductsAction(Request $requestbool $isAffiliate false): JsonResponse
  898.     {
  899.         $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  900.         $productArray = [];
  901.         $packageArray = [];
  902.         $isPackage false;
  903.         $accoId $request->get('id');
  904.         $accommodation AccommodationServiceProvider::getById((int)$accoId);
  905.         $pId $request->get('pid');
  906.         $housePackageMaster HousePackageMaster::getById($pId);
  907.         $useCancellationInfo = isset($this->enabledFeatures['freeCancellationInList']);
  908.         $searchParams $this->searchParams->getSearchParam(nullfalse);
  909.         $orderKey $searchParams->getOrderKey();
  910.         $searchParams->setOrderKey(array_diff(is_string($orderKey) ? [$orderKey] : $orderKey, ['custom''nearbySearchAsc']));
  911.         $searchParams->setAccoIds([$accoId]);
  912.         if ($housePackageMaster) {
  913.             $packageProductIds = [];
  914.             foreach ($housePackageMaster->getProducts() as $accommodationProduct) {
  915.                 $packageProductIds[] = $accommodationProduct->getId();
  916.             }
  917.             $searchParams->setProductIds($packageProductIds);
  918.             $isPackage true;
  919.         }
  920.         $adapter $this->vacancyLive;
  921.         $adapter->setSearchParameter($searchParams);
  922.         $accommodationResultSet ResultSet\Accommodation::getById($accoId$adapter);
  923.         if (($accommodationResultSet && count($accommodationResultSet->getProducts()) > 0)) {
  924.             $splitData $this->vacancyFrontend->splitProductsAndPackages($accommodationResultSet);
  925.             $productArray $splitData['products'];
  926.             $packageArray $splitData['packages'];
  927.         }
  928.         $fromCorridorSearch $request->get('corridor') == 1;
  929.         $nightsMappings $this->configuration->getVariableNightMappings();
  930.         //only do alternative search detail page if there ist a nights mapping present
  931.         if ($nightsMappings && !$fromCorridorSearch && (!$accommodationResultSet || count($accommodationResultSet->getProducts()) < 1)) {
  932.             //no rooms found - initiate corridor search
  933.             $alternativeSeachParams $this->getAlternativeSearchParams($searchParams77);
  934.             $dayDiff $alternativeSeachParams->getDateFrom()->diff($alternativeSeachParams->getDateTo());
  935.             $period $dayDiff->format('%a');
  936.             $alternativeSeachParams->setPeriod($period);
  937.             $this->corridor->setSearchParameter($alternativeSeachParams);
  938.             $alternativeAccommodationResultSet ResultSet\Accommodation::getById($accoId$this->corridor);
  939.             if ($housePackageMaster && $alternativeAccommodationResultSet) {
  940.                 $alternativeAccommodationResultSet = new ResultSet\HousePackageMaster($accoId$alternativeAccommodationResultSet->getRoomRows(), $pId$alternativeAccommodationResultSet);
  941.             }
  942.         }
  943.         if ($housePackageMaster) {
  944.             if (array_key_exists($housePackageMaster->getId(), $packageArray) && is_array($packageArray[$housePackageMaster->getId()]) && count($packageArray[$housePackageMaster->getId()]) == 0) {
  945.                 $error $this->searchParams->getSearchError($searchParams);
  946.             }
  947.             $renderedProductsData $this->renderTemplate('@ElementsDemiFrontend/Accommodation/accommodationProductList.html.twig',
  948.                 [
  949.                     'accommodationResultSet' => $accommodationResultSet,
  950.                     'alternativeResultSet' => $alternativeAccommodationResultSet ?? null,
  951.                     'alternativeSearchParameter' => $alternativeSeachParams ?? null,
  952.                     'accommodation' => $accommodation,
  953.                     'searchParam' => $searchParams,
  954.                     'productArray' => array_key_exists($housePackageMaster->getId(), $packageArray) ? $packageArray[$housePackageMaster->getId()] : [],
  955.                     'error' => $error ?? null,
  956.                     'isAffiliate' => $isAffiliate,
  957.                     'tvbPackage' => null,
  958.                     'fromCorridor' => $fromCorridorSearch,
  959.                     'useCancellationInfo' => $useCancellationInfo
  960.                 ]);
  961.         } else {
  962.             if (count($productArray) == 0) {
  963.                 $error $this->searchParams->getSearchError($searchParams);
  964.             }
  965.             $renderedProductsData $this->renderTemplate('@ElementsDemiFrontend/Accommodation/accommodationProductList.html.twig',
  966.                 [
  967.                     'accommodationResultSet' => $accommodationResultSet,
  968.                     'alternativeResultSet' => $alternativeAccommodationResultSet ?? null,
  969.                     'alternativeSearchParameter' => $alternativeSeachParams ?? null,
  970.                     'accommodation' => $accommodation,
  971.                     'searchParam' => $searchParams,
  972.                     'productArray' => $productArray,
  973.                     'error' => $error ?? null,
  974.                     'isAffiliate' => $isAffiliate,
  975.                     'fromCorridor' => $fromCorridorSearch,
  976.                     'useCancellationInfo' => $useCancellationInfo,
  977.                     'tvbPackage' => null
  978.                 ]);
  979.             if (count($packageArray) == 0) {
  980.                 $error $this->searchParams->getSearchError($searchParams);
  981.             }
  982.             if ($this->enabledFeatures['destinationPackages'] && $request->get('corridor') != 1) {
  983.                 $destinationPackages \Elements\Demi\Accommodation\Search\Listing\Package::getPackagesOfferedByAccommodation($accommodation$searchParams);
  984.             } else {
  985.                 $destinationPackages null;
  986.             }
  987.             $renderedPackagesData $this->renderTemplate('@ElementsDemiFrontend/Accommodation/accommodationPackagesList.html.twig',
  988.                 [
  989.                     'accommodationResultSet' => $accommodationResultSet,
  990.                     'accommodation' => $accommodation,
  991.                     'searchParam' => $searchParams,
  992.                     'packageArray' => $packageArray,
  993.                     'error' => $error ?? null,
  994.                     'isAffiliate' => $isAffiliate,
  995.                     'destinationPackages' => $destinationPackages
  996.                 ]);
  997.         }
  998.         $deviceDetector Tool\DeviceDetector ::getInstance();
  999.         $template '@ElementsDemiFrontend/Accommodation/searchInfoBox.desktop.html.twig';
  1000.         if ($deviceDetector->isPhone()) {
  1001.             $template '@ElementsDemiFrontend/Accommodation/searchInfoBox.mobile.html.twig';
  1002.         } else {
  1003.             $renderedSearchInfoCompact $this->renderTemplate('@ElementsDemiFrontend/Accommodation/searchInfoBoxCompact.desktop.html.twig',
  1004.                 [
  1005.                     'accommodationResultSet' => $accommodationResultSet,
  1006.                     'accommodation' => $accommodation,
  1007.                     'isPackage' => $isPackage,
  1008.                     'availabilityIsChecked' => !$searchParams->isNoDate(),
  1009.                     'useCancellationInfo' => $useCancellationInfo
  1010.                 ]);
  1011.         }
  1012.         $renderedSearchInfoBox $this->renderTemplate($template,
  1013.             [
  1014.                 'accommodationResultSet' => $accommodationResultSet,
  1015.                 'alternativeResultSet' => $alternativeAccommodationResultSet ?? null,
  1016.                 'alternativeSearchParameter' => $alternativeSeachParams ?? null,
  1017.                 'accommodation' => $accommodation,
  1018.                 'isPackage' => $isPackage,
  1019.                 'availabilityIsChecked' => !$searchParams->isNoDate(),
  1020.                 'useCancellationInfo' => $useCancellationInfo
  1021.             ]);
  1022.         //TODO @msteyrer minprice hast to be minprice rooms !
  1023.         $priceObject $this->paymentHelper->getPriceObject($accommodationResultSet $accommodationResultSet->getMinPrice() : 0$accommodation);
  1024.         $matches = [];
  1025.         preg_match('@[0-9.|,]+@', (string) $priceObject$matches);
  1026.         $minPriceString $matches[0];
  1027.         $data = [
  1028.             'status' => 'success',
  1029.             'availabilityUnits' => isset($splitData['jsonArray']) ? $splitData['jsonArray'] : '',
  1030.             'html_rooms' => $renderedProductsData->getContent(),
  1031.             'html_packages' => isset($renderedPackagesData) ? $renderedPackagesData->getContent() : '',
  1032.             'html_searchInfo' => $renderedSearchInfoBox->getContent(),
  1033.             'html_searchInfoCompact' => isset($renderedSearchInfoCompact) ? $renderedSearchInfoCompact->getContent() : '',
  1034.             'minPriceRooms' => $minPriceString
  1035.         ];
  1036.         return new JsonResponse($data);
  1037.     }
  1038.     public function ajaxGetCorridorMatrixAction(Request $request): JsonResponse
  1039.     {
  1040.         $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  1041.         $isPackage false;
  1042.         $accoId $request->get('id');
  1043.         $accommodation AccommodationServiceProvider::getById((int)$accoId);
  1044.         $pId $request->get('pid');
  1045.         $searchParams $this->searchParams->getSearchParam();
  1046.         $searchParams->setAccoIds([$accoId]);
  1047.         $dateFrom $searchParams->getDateFrom();
  1048.         $minDate $request->get('minDate');
  1049.         $data = [];
  1050.         $corridor $this->corridor;
  1051.         $nrOfDays $this->configuration->getCorridorMatrixDays();
  1052.         $nights $this->searchParams->getNightsFromRequest($request);
  1053.         if ($minDate) {
  1054.             //user is navigating in corridor Matrix
  1055.             $dateFrom ElementsCustomDateFormat::parseDateString($request->get('minDate'));
  1056.             $minDateRooms $dateFrom;
  1057.             $newSearchParams = clone $searchParams;
  1058.             $newSearchParams->setDateFrom($minDateRooms);
  1059.             $dateTo = clone $dateFrom;
  1060.             if ($nights[count($nights) - 1] > $nrOfDays) {
  1061.                 $dateTo->add(new DateInterval('P'.$nights[count($nights) - 1].'D'));
  1062.             } else {
  1063.                 $dateTo->add(new DateInterval('P'.$nrOfDays.'D'));
  1064.             }
  1065.             $newSearchParams->setDateTo($dateTo);
  1066.             $searchParams $newSearchParams;
  1067.         }
  1068.         $corridor->setSearchParameter($searchParams);
  1069.         $accommodationResultSet ResultSet\Accommodation::getById($accoId$corridor);
  1070.         if (!empty($pId) && $accommodationResultSet) {
  1071.             $accommodationResultSet = new ResultSet\HousePackageMaster($accoId$accommodationResultSet->getRoomRows(), $pId$accommodationResultSet);
  1072.         }
  1073.         $roomsFound false;
  1074.         $packagesFound false;
  1075.         if ($accommodationResultSet) {
  1076.             $minDateRooms $accommodationResultSet->getMinDateFrom($dateFromParameter::PRODUCT_TYPE_ACCOMMODATION);
  1077.             $minDatePackages $accommodationResultSet->getMinDateFrom($dateFromParameter::PRODUCT_TYPE_PACKAGE);
  1078.             foreach ($accommodationResultSet->getProducts() as $resultSetProduct) {
  1079.                 if ($resultSetProduct->getProductType() == Parameter::PRODUCT_TYPE_ACCOMMODATION) {
  1080.                     $roomsFound true;
  1081.                 } elseif ($resultSetProduct->getProductType() == Parameter::PRODUCT_TYPE_PACKAGE) {
  1082.                     $packagesFound true;
  1083.                 }
  1084.             }
  1085.         } else {
  1086.             $minDateRooms $minDatePackages $dateFrom;
  1087.         }
  1088.         if (!$roomsFound) {
  1089.             $alternativeSeachParams $this->getAlternativeSearchParams($searchParams77);
  1090.         }
  1091.         if ($pId) {
  1092.             $isPackage true;
  1093.             if (!$minDateRooms) {
  1094.                 $minDateRooms $minDatePackages;
  1095.             }
  1096.         }
  1097.         $corridorMatrixEmpty $this->vacancyFrontend->getCorridorMatrix($accoId$minDateRooms$nrOfDays$searchParams,
  1098.             $nights'Accommodation'true);
  1099.         $corridorMatrixSetRooms $corridorMatrixEmpty;
  1100.         if ($minDateRooms) {
  1101.             if ($request->get('activeTab') === 'package') {
  1102.                 //Packages Tab is open, use mindate from packages for both
  1103.                 $minDateRooms $minDatePackages;
  1104.             }
  1105.             if ($roomsFound) {
  1106.                 $corridorMatrixSetRooms $this->vacancyFrontend->getCorridorMatrix($accoId$minDateRooms$nrOfDays$searchParams,
  1107.                     $nights);
  1108.             }
  1109.         }
  1110.         $corridorMatrixSetPackages $corridorMatrixEmpty;
  1111.         if ($minDatePackages) {
  1112.             if ($request->get('activeTab') === 'rooms') {
  1113.                 //Packages Tab is open, use mindate from rooms for both
  1114.                 $minDatePackages $minDateRooms;
  1115.             }
  1116.             if ($packagesFound) {
  1117.                 $corridorMatrixSetPackages $this->vacancyFrontend->getCorridorMatrix($accoId$minDatePackages$nrOfDays,
  1118.                     $searchParams,
  1119.                     $nights'Package');
  1120.             }
  1121.         }
  1122.         $data['status'] = 'success';
  1123.         $data['rooms'] = $corridorMatrixSetRooms;
  1124.         if ($isPackage) {
  1125.             $data['rooms'] = $corridorMatrixSetPackages;
  1126.         }
  1127.         $data['packages'] = $corridorMatrixSetPackages;
  1128.         if (!$minDate && !$roomsFound && !$packagesFound) {
  1129.             $this->corridor->setSearchParameter($alternativeSeachParams);
  1130.             $alternativeResultSet ResultSet\Accommodation::getById($accoId$this->corridor);
  1131.             if (!empty($pId) && $alternativeResultSet) {
  1132.                 $alternativeResultSet = new ResultSet\HousePackageMaster($accoId$alternativeResultSet->getRoomRows(), $pId$alternativeResultSet);
  1133.             }
  1134.             if ($alternativeResultSet && $alternativeResultSet->getMinPrice()) {
  1135.                 $deviceDetector Tool\DeviceDetector ::getInstance();
  1136.                 $template '@ElementsDemiFrontend/Accommodation/includes/alternative-room-results.desktop.html.twig';
  1137.                 if ($deviceDetector->isPhone()) {
  1138.                     $template '@ElementsDemiFrontend/Accommodation/includes/alternative-room-results.mobile.html.twig';
  1139.                 }
  1140.                 $renderedHtml $this->renderView($template,
  1141.                     [
  1142.                         'alternativeResultSet' => $alternativeResultSet,
  1143.                         'alternativeSearchParameter' => $alternativeSeachParams,
  1144.                         'accommodation' => $accommodation,
  1145.                     ]);
  1146.             } else {
  1147.                 $inAffiliate str_contains($request->attributes->get('_route'), 'affiliate');
  1148.                 $renderedHtml $this->renderView('@ElementsDemiFrontend/Accommodation/includes/nothing-found.html.twig',
  1149.                     ['accommodation' => $accommodation'showAllRoomsLink' => true'isAffiliate' => $inAffiliate]);
  1150.             }
  1151.             $data['html_noRooms'] = $data['html_noPackages'] = $renderedHtml;
  1152.         } elseif (!$minDate && !$roomsFound && $packagesFound) {
  1153.             $data['html_noRooms'] = $this->renderView('@ElementsDemiFrontend/Accommodation/includes/hint-other-tab.html.twig',
  1154.                 [
  1155.                     'targetTab' => 'packages',
  1156.                     'textTk' => 'demi.detail.hint-to-package-tab-text',
  1157.                     'buttonTk' => 'demi.detail.hint-to-package-tab-button'
  1158.                 ]);
  1159.         } elseif (!$minDate && $roomsFound && !$packagesFound) {
  1160.             $data['html_noPackages'] = $this->renderView('@ElementsDemiFrontend/Accommodation/includes/hint-other-tab.html.twig',
  1161.                 [
  1162.                     'targetTab' => 'rooms',
  1163.                     'textTk' => 'demi.detail.hint-to-room-tab-text',
  1164.                     'buttonTk' => 'demi.detail.hint-to-room-tab-button'
  1165.                 ]);
  1166.         }
  1167.         $deviceDetector Tool\DeviceDetector ::getInstance();
  1168.         $template '@ElementsDemiFrontend/Accommodation/searchInfoBox.desktop.html.twig';
  1169.         if ($deviceDetector->isPhone()) {
  1170.             $template '@ElementsDemiFrontend/Accommodation/searchInfoBox.mobile.html.twig';
  1171.         } else {
  1172.             $renderedSearchInfoCompact $this->renderView('@ElementsDemiFrontend/Accommodation/searchInfoBoxCompact.desktop.html.twig',
  1173.                 [
  1174.                     'accommodationResultSet' => $accommodationResultSet,
  1175.                     'alternativeResultSet' => $alternativeResultSet ?? null,
  1176.                     'alternativeSearchParameter' => $alternativeSeachParams ?? null,
  1177.                     'accommodation' => $accommodation,
  1178.                     'isPackage' => $isPackage,
  1179.                     'isCorridor' => true
  1180.                 ]);
  1181.             $data['html_searchInfoCompact'] = $renderedSearchInfoCompact;
  1182.         }
  1183.         $renderedSearchInfoBox $this->renderView($template,
  1184.             [
  1185.                 'accommodationResultSet' => $accommodationResultSet,
  1186.                 'alternativeResultSet' => $alternativeResultSet ?? null,
  1187.                 'alternativeSearchParameter' => $alternativeSeachParams ?? null,
  1188.                 'accommodation' => $accommodation,
  1189.                 'isPackage' => $isPackage,
  1190.                 'isCorridor' => true
  1191.             ]);
  1192.         $data['html_searchInfo'] = $renderedSearchInfoBox;
  1193.         return $this->json($data);
  1194.     }
  1195.     public function ajaxGetProductByIdAndMealcodeAction(Request $request): JsonResponse
  1196.     {
  1197.         $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  1198.         $productId $request->get('productId');
  1199.         $mealcode $request->get('mealcode');
  1200.         $roomindex $request->get('roomindex');
  1201.         $searchParams $this->searchParams->getSearchParam($roomindex);
  1202.         $searchParams->setProductIds([$productId]);
  1203.         $searchParams->setMealType($mealcode);
  1204.         $searchParams->setClassifications([]);
  1205.         $searchParams->setStars([]);
  1206.         $p AccommodationProduct::getById($productId);
  1207.         if ($p && $p->getParent()) {
  1208.             $acco $p->getParent()->getParent();
  1209.         }
  1210.         if ($acco instanceof AccommodationServiceProvider) {
  1211.             Config::setClientKeyOverrideFromObject($acco);
  1212.         }
  1213.         $this->vacancyLive->setSearchParameter($searchParams);
  1214.         /**
  1215.          * @var  ResultSet\Product $productSet
  1216.          */
  1217.         try {
  1218.             $productSet ResultSet\Product::getByIdAndMealcode($productId$mealcode$this->vacancyLive);
  1219.         } catch (\Exception $exception) {
  1220.             if (Pimcore::inDebugMode()) {
  1221.                 throw $exception;
  1222.             }
  1223.             throw new GoneHttpException('ajax request failed');
  1224.         }
  1225.         if ($productSet) {
  1226.             $roomRows $searchParams->getRoomrows();
  1227.             $renderedPriceDetail $this->renderView('@ElementsDemiFrontend/Includes/productPriceInfo.html.twig',
  1228.                 [
  1229.                     'productSet' => $productSet,
  1230.                     'roomRow' => $roomRows[0],
  1231.                     'searchParam' => $searchParams
  1232.                 ]);
  1233.             $priceBeforeSpecial '';
  1234.             if ($productSet->getPriceData() && $productSet->getPriceData()->getPriceBeforeSpecial() > $productSet->getPrice()) {
  1235.                 $priceBeforeSpecial $productSet->getPriceData()->getPriceBeforeSpecial();
  1236.             }
  1237.             $jsonArray =
  1238.                 [
  1239.                     'status' => 'success',
  1240.                     'price' => $productSet->getPrice(),
  1241.                     'priceBeforeSpecial' => $priceBeforeSpecial,
  1242.                     'priceDetailHtml' => $renderedPriceDetail
  1243.                 ];
  1244.         } else {
  1245.             $jsonArray = ['status' => 'failed'];
  1246.         }
  1247.         return $this->json($jsonArray);
  1248.     }
  1249.     public function ajaxGetPaymentInformationAction(Request $request): Response
  1250.     {
  1251.         $this->addResponseHeader('X-Robots-Tag''noindex,nofollow');
  1252.         $accoId $request->get('id');
  1253.         $acco AccommodationServiceProvider::getById($accoId);
  1254.         $productId $request->get('productId');
  1255.         $product AccommodationProduct::getById($productId);
  1256.         $roomRowIndex $request->get('roomindex');
  1257.         $viewParams['bookOnRequest'] = $request->get('bookonrequest') == '1';
  1258.         $params $this->searchParams->getSearchParam($roomRowIndex);
  1259.         $roomRows $params->getRoomrows();
  1260.         $roomRow reset($roomRows);
  1261.         if (!$roomRow) {
  1262.             throw new GoneHttpException('incomplete ajax request - roomrow not found');
  1263.         }
  1264.         $viewParams['paymentInfo'] = $this->paymentInfo->getPaymentInformation($acco,
  1265.             $product$acco->getDbcode(), null$params->getDateFrom(), $params->getDateTo(), $roomRow->getAdults(),
  1266.             $roomRow->getChildAges());
  1267.         return $this->renderTemplate('@ElementsDemiFrontend/Accommodation/ajaxGetPaymentInformation.html.twig'$viewParams);
  1268.     }
  1269.     protected function needsRedirectToCorrectUrl(Request $requestAccommodationServiceProvider $accommodationPackage|HousePackageMaster|HousePackageContainer $package null) : string
  1270.     {
  1271.         $urlHelper $this->demiUrl;
  1272.         $currentPath $request->getPathInfo();
  1273.         if ($package instanceof Package) {
  1274.             $routeName 'demi_acco_detail_tvbpackage_page';
  1275.         } else {
  1276.             $routeName $package 'demi_acco_detail_package_page' 'demi_acco_detail_page';
  1277.         }
  1278.         $params = [
  1279.             'accommodation' => $accommodation,
  1280.         ];
  1281.         if ($package) {
  1282.             $params['package'] = $package;
  1283.         }
  1284.         $reset true;
  1285.         if ($request->get('keepParams')) {
  1286.             $reset false;
  1287.             $request->query->remove('keepParams');
  1288.             $request->query->remove('guiid');
  1289.             $request->query->set('ref'$request->getPathInfo());
  1290.         }
  1291.         $correctPath $urlHelper->invoke($params$routeName$reset);
  1292.         if ($request->get('enquirySuccess')) {
  1293.             return '';
  1294.         }
  1295.         if ($currentPath != $correctPath) {
  1296.             return $correctPath;
  1297.         }
  1298.         return '';
  1299.     }
  1300.     protected function setIsAffiliate() : bool
  1301.     {
  1302.         return false;
  1303.     }
  1304.     protected function getMappingArray(string $type): array
  1305.     {
  1306.         $mappingFile __DIR__ .'/../Resources/RedirectIdMapping' '/'.$type;
  1307.         if (!file_exists($mappingFile)) {
  1308.             throw new NotFoundHttpException('DeMI Redirect failed - mapping file '.$mappingFile.'  missing');
  1309.         }
  1310.         return Serialize::unserialize(file_get_contents($mappingFile));
  1311.     }
  1312. }