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

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