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

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