vendor/isotope/isotope-core/system/modules/isotope/library/Isotope/Frontend.php line 464

Open in your IDE?
  1. <?php
  2. /*
  3.  * Isotope eCommerce for Contao Open Source CMS
  4.  *
  5.  * Copyright (C) 2009 - 2019 terminal42 gmbh & Isotope eCommerce Workgroup
  6.  *
  7.  * @link       https://isotopeecommerce.org
  8.  * @license    https://opensource.org/licenses/lgpl-3.0.html
  9.  */
  10. namespace Isotope;
  11. use Contao\Controller;
  12. use Contao\CoreBundle\ContaoCoreBundle;
  13. use Contao\Database;
  14. use Contao\Database\Result;
  15. use Contao\Date;
  16. use Contao\Environment;
  17. use Contao\FrontendTemplate;
  18. use Contao\FrontendUser;
  19. use Contao\MemberModel;
  20. use Contao\PageModel;
  21. use Contao\StringUtil;
  22. use Contao\System;
  23. use Contao\Widget;
  24. use Haste\Input\Input;
  25. use Isotope\EventListener\ChangeLanguageListener;
  26. use Isotope\Frontend\ProductAction\CartAction;
  27. use Isotope\Frontend\ProductAction\FavoriteAction;
  28. use Isotope\Interfaces\IsotopeAttributeWithOptions;
  29. use Isotope\Interfaces\IsotopeOrderableCollection;
  30. use Isotope\Interfaces\IsotopePrice;
  31. use Isotope\Interfaces\IsotopeProduct;
  32. use Isotope\Interfaces\IsotopeProductCollection;
  33. use Isotope\Model\Attribute;
  34. use Isotope\Model\AttributeOption;
  35. use Isotope\Model\Product;
  36. use Isotope\Model\Product\Standard;
  37. use Isotope\Model\ProductCollection\Cart;
  38. use Isotope\Model\ProductCollection\Order;
  39. use Isotope\Model\ProductCollectionSurcharge;
  40. use Isotope\Model\TaxClass;
  41. use Symfony\Component\DependencyInjection\ContainerInterface;
  42. /**
  43.  * Provide methods to handle Isotope front end components.
  44.  */
  45. class Frontend extends \Contao\Frontend
  46. {
  47.     /**
  48.      * Cached reader page id's
  49.      * @var array
  50.      */
  51.     protected static $arrReaderPageIds = array();
  52.     /**
  53.      * Get shipping and payment surcharges for given collection
  54.      *
  55.      * @param IsotopeProductCollection $objCollection
  56.      *
  57.      * @return ProductCollectionSurcharge[]
  58.      */
  59.     public function findShippingAndPaymentSurcharges(IsotopeProductCollection $objCollection)
  60.     {
  61.         if (!$objCollection instanceof IsotopeOrderableCollection) {
  62.             System::log('Product collection ID "' $objCollection->getId() . '" is not orderable'__METHOD__TL_ERROR);
  63.             return false;
  64.         }
  65.         // Do not add shipping and payment surcharge to cart,
  66.         // they should only appear in the order review
  67.         if ($objCollection instanceof Cart) {
  68.             return array();
  69.         }
  70.         $arrSurcharges = array();
  71.         if (($objSurcharge $objCollection->getShippingSurcharge()) !== null) {
  72.             $arrSurcharges[] = $objSurcharge;
  73.         }
  74.         if (($objSurcharge $objCollection->getPaymentSurcharge()) !== null) {
  75.             $arrSurcharges[] = $objSurcharge;
  76.         }
  77.         return $arrSurcharges;
  78.     }
  79.     /**
  80.      * Replace the current page with a reader page if applicable
  81.      *
  82.      * @param array $arrFragments
  83.      *
  84.      * @return array
  85.      */
  86.     public function loadReaderPageFromUrl($arrFragments)
  87.     {
  88.         $strKey   'product';
  89.         $strAlias '';
  90.         // Find products alias. Can't use Input because they're not yet initialized
  91.         if ($GLOBALS['TL_CONFIG']['useAutoItem'] && \in_array($strKey$GLOBALS['TL_AUTO_ITEM'], true)) {
  92.             $strKey 'auto_item';
  93.         }
  94.         for ($i 1$c \count($arrFragments); $i $c$i += 2) {
  95.             if ($arrFragments[$i] == $strKey) {
  96.                 $strAlias $arrFragments[$i 1];
  97.             }
  98.         }
  99.         global $objIsotopeListPage;
  100.         $objIsotopeListPage null;
  101.         if ($strAlias != '' && ($objPage PageModel::findPublishedByIdOrAlias($arrFragments[0])) !== null) {
  102.             // Check the URL and language of each page if there are multiple results
  103.             // see Contao's index.php
  104.             if ($objPage !== null && $objPage->count() > 1) {
  105.                 $objNewPage null;
  106.                 $arrPages   = array();
  107.                 // Order by domain and language
  108.                 /** @var PageModel $objCurrentPage */
  109.                 foreach ($objPage as $objCurrentPage) {
  110.                     $objCurrentPage->loadDetails();
  111.                     $domain                                           $objCurrentPage->domain ? : '*';
  112.                     $arrPages[$domain][$objCurrentPage->rootLanguage] = $objCurrentPage;
  113.                     // Also store the fallback language
  114.                     if ($objCurrentPage->rootIsFallback) {
  115.                         $arrPages[$domain]['*'] = $objCurrentPage;
  116.                     }
  117.                 }
  118.                 $strHost Environment::get('host');
  119.                 // Look for a root page whose domain name matches the host name
  120.                 if (isset($arrPages[$strHost])) {
  121.                     $arrLangs $arrPages[$strHost];
  122.                 } elseif (isset($arrPages['*'])) {
  123.                     $arrLangs $arrPages['*']; // Empty domain
  124.                 } else {
  125.                     // No domain match (see #2347)
  126.                     return $arrFragments;
  127.                 }
  128.                 // Use the first result (see #4872)
  129.                 if (!$GLOBALS['TL_CONFIG']['addLanguageToUrl']) {
  130.                     $objNewPage current($arrLangs);
  131.                 } // Try to find a page matching the language parameter
  132.                 elseif (($lang Input::get('language')) != '' && isset($arrLangs[$lang])) {
  133.                     $objNewPage $arrLangs[$lang];
  134.                 }
  135.                 // Store the page object
  136.                 if (\is_object($objNewPage)) {
  137.                     $objPage $objNewPage;
  138.                 }
  139.             }
  140.             if ('page' === $objPage->iso_readerMode && ($objReader $objPage->getRelated('iso_readerJumpTo')) !== null) {
  141.                 /** @var PageModel $objIsotopeListPage */
  142.                 $objIsotopeListPage $objPage->current();
  143.                 $objIsotopeListPage->loadDetails();
  144.                 $arrFragments[0] = ($objReader->alias ?: $objReader->id);
  145.             }
  146.         }
  147.         return $arrFragments;
  148.     }
  149.     /**
  150.      * Overrides the reader page
  151.      *
  152.      * @param PageModel $objPage
  153.      */
  154.     public function overrideReaderPage($objPage)
  155.     {
  156.         /** @var PageModel $objIsotopeListPage */
  157.         global $objIsotopeListPage;
  158.         if (null !== $objIsotopeListPage) {
  159.             $originalPageId $objPage->id;
  160.             $arrTrail   $objIsotopeListPage->trail;
  161.             $arrTrail[] = $originalPageId;
  162.             $objPage->id $objIsotopeListPage->id;
  163.             $objPage->pid $objIsotopeListPage->pid;
  164.             $objPage->alias $objIsotopeListPage->alias;
  165.             $objPage->trail $arrTrail;
  166.             $objPage->languageMain $objIsotopeListPage->languageMain;
  167.             $objIsotopeListPage->pid $originalPageId;
  168.         }
  169.     }
  170.     public function overrideArticles($pageId$strColumn)
  171.     {
  172.         global $objPage;
  173.         global $objIsotopeListPage;
  174.         if (!$objIsotopeListPage || $pageId === $objIsotopeListPage->pid) {
  175.             return false;
  176.         }
  177.         $objPage->id $objIsotopeListPage->pid;
  178.         $articles Controller::getFrontendModule(0$strColumn);
  179.         $objPage->id $objIsotopeListPage->id;
  180.         return $articles;
  181.     }
  182.     /**
  183.      * Inject the necessary scripts here upon the "modifyFrontendPage" hook.
  184.      * We don't use the generatePage hook here anymore as modules added via
  185.      * InsertTags will not get those scripts added. We also don't use a combination
  186.      * of both (e.g. use generatePage hook by default and use the modifyFrontendPage
  187.      * hook only if there are still $GLOBALS['AJAX_PRODUCTS'] because a simple
  188.      * str_replace on </body> is really not a performance issue. So we chose
  189.      * simplicity here.
  190.      *
  191.      * @param string $buffer
  192.      * @param string $templateName
  193.      *
  194.      * @return string
  195.      */
  196.     public function injectScripts($buffer$templateName)
  197.     {
  198.         // Only add messages to the fe_page template (see isotope/core#2255)
  199.         if (!empty($templateName) && !== strncmp($templateName'fe_'3)) {
  200.             return $buffer;
  201.         }
  202.         $messages Message::generate();
  203.         $hasProducts = !empty($GLOBALS['AJAX_PRODUCTS']) && \is_array($GLOBALS['AJAX_PRODUCTS']);
  204.         if ($messages === '' && !$hasProducts) {
  205.             return $buffer;
  206.         }
  207.         $template = new FrontendTemplate('iso_scripts');
  208.         if ($hasProducts) {
  209.             $template->hasProducts true;
  210.             $template->loadMessage StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['loadingProductData']);
  211.             $template->products    json_encode($GLOBALS['AJAX_PRODUCTS']);
  212.         }
  213.         if ($messages !== '') {
  214.             $template->hasMessages true;
  215.             $template->messages str_replace(array("\n""\r""'"), array('''''\''), $messages);
  216.         }
  217.         return str_replace('</body>'$template->parse() . '</body>'$buffer);
  218.     }
  219.     /**
  220.      * Format surcharge prices
  221.      *
  222.      * @param ProductCollectionSurcharge[] $arrSurcharges
  223.      * @param string|null                  $currencyCode
  224.      *
  225.      * @return array
  226.      */
  227.     public static function formatSurcharges($arrSurcharges$currencyCode null)
  228.     {
  229.         $i         0;
  230.         $arrReturn = array();
  231.         foreach ($arrSurcharges as $k => $objSurcharge) {
  232.             $arrReturn[$k]                = $objSurcharge->row();
  233.             $arrReturn[$k]['price']       = Isotope::formatPriceWithCurrency($objSurcharge->pricetrue$currencyCode$objSurcharge->applyRoundingIncrement);
  234.             $arrReturn[$k]['total_price'] = Isotope::formatPriceWithCurrency($objSurcharge->total_pricetrue$currencyCode$objSurcharge->applyRoundingIncrement);
  235.             $arrReturn[$k]['tax_free_total_price'] = Isotope::formatPriceWithCurrency($objSurcharge->tax_free_total_pricetrue$currencyCode$objSurcharge->applyRoundingIncrement);
  236.             $arrReturn[$k]['rowClass']    = trim('foot_' . (++$i) . ' ' $objSurcharge->rowClass);
  237.             $arrReturn[$k]['tax_id']      = $objSurcharge->getTaxNumbers();
  238.             $arrReturn[$k]['raw']         = $objSurcharge->row();
  239.             $arrReturn[$k]['surcharge']   = $objSurcharge;
  240.         }
  241.         return $arrReturn;
  242.     }
  243.     /**
  244.      * Adds the product urls to the array so they get indexed when search index is rebuilt in the maintenance module
  245.      *
  246.      * @param array  $arrPages     Absolute page urls
  247.      * @param int    $intRoot      Root page id
  248.      * @param bool   $blnIsSitemap True if it's a sitemap module call (= treat differently when page is protected etc.)
  249.      *
  250.      * @return array   Extended array of absolute page urls
  251.      */
  252.     public function addProductsToSearchIndex($arrPages$intRoot 0$blnIsSitemap false)
  253.     {
  254.         $t         PageModel::getTable();
  255.         $time      Date::floorToMinute();
  256.         $arrValue  = array();
  257.         $arrColumn = array(
  258.             "$t.type='root'",
  259.             "$t.published='1'",
  260.             "($t.start='' OR $t.start<'$time')",
  261.             "($t.stop='' OR $t.stop>'" . ($time 60) . "')"
  262.         );
  263.         if ($intRoot 0) {
  264.             $arrColumn[] = "$t.id=?";
  265.             $arrValue[]  = $intRoot;
  266.         }
  267.         $objRoots PageModel::findBy($arrColumn$arrValue);
  268.         if (null !== $objRoots) {
  269.             foreach ($objRoots as $objRoot) {
  270.                 $arrPageIds   Database::getInstance()->getChildRecords($objRoot->id$tfalse);
  271.                 $arrPageIds[] = $intRoot;
  272.                 $objProducts Product::findPublishedByCategories($arrPageIds);
  273.                 if (null !== $objProducts) {
  274.                     foreach ($objProducts as $objProduct) {
  275.                         if (!$objProduct->isPublished()) {
  276.                             continue;
  277.                         }
  278.                         // Find the categories in the current root
  279.                         $arrCategories array_intersect($objProduct->getCategories(), $arrPageIds);
  280.                         $intRemaining  \count($arrCategories);
  281.                         $objPages PageModel::findMultipleByIds($arrCategories) ?: [];
  282.                         foreach ($objPages as $objPage) {
  283.                             --$intRemaining;
  284.                             // The target page does not exist
  285.                             if ($objPage === null || 'none' === $objPage->iso_readerMode) {
  286.                                 continue;
  287.                             }
  288.                             // The target page has not been published
  289.                             if (!$objPage->published
  290.                                 || ($objPage->start != '' && $objPage->start $time)
  291.                                 || ($objPage->stop != '' && $objPage->stop < ($time 60))
  292.                             ) {
  293.                                 continue;
  294.                             }
  295.                             // The target page is exempt from the sitemap
  296.                             if ($blnIsSitemap && 'map_never' === $objPage->sitemap) {
  297.                                 continue;
  298.                             }
  299.                             // Do not generate a reader for the index page, except if it is the only one
  300.                             if ($intRemaining && 'index' === $objPage->alias) {
  301.                                 continue;
  302.                             }
  303.                             // Pass root language to page object
  304.                             $objPage->language $objRoot->language;
  305.                             $arrPages[] = $objProduct->generateUrl($objPagetrue);
  306.                             // Only take the first category because this is our primary one
  307.                             // Having multiple reader pages in the sitemap XML would mean duplicate content
  308.                             break;
  309.                         }
  310.                     }
  311.                 }
  312.             }
  313.         }
  314.         return array_unique($arrPages);
  315.     }
  316.     /**
  317.      * save_callback for upload widget to store $_FILES data into the product
  318.      *
  319.      * @deprecated Deprecated since Isotope 2.4, to be removed in Isotope 3.0.
  320.      */
  321.     public function saveUpload($varValueIsotopeProduct $objProductWidget $objWidget)
  322.     {
  323.         if (\is_array($_SESSION['FILES'][$objWidget->name])
  324.             && $_SESSION['FILES'][$objWidget->name]['uploaded'] == '1'
  325.             && $_SESSION['FILES'][$objWidget->name]['error'] == 0
  326.         ) {
  327.             return $_SESSION['FILES'][$objWidget->name]['name'];
  328.         }
  329.         return $varValue;
  330.     }
  331.     /**
  332.      * Get postal codes from CSV and ranges
  333.      *
  334.      * @param string $strPostalCodes
  335.      *
  336.      * @return array
  337.      */
  338.     public static function parsePostalCodes($strPostalCodes)
  339.     {
  340.         $arrCodes = array();
  341.         foreach (StringUtil::trimsplit(','$strPostalCodes) as $strCode) {
  342.             $arrCode StringUtil::trimsplit('-'$strCode);
  343.             // Ignore codes with more than 1 range
  344.             switch (\count($arrCode)) {
  345.                 case 1:
  346.                     $arrCodes[] = $arrCode[0];
  347.                     break;
  348.                 case 2:
  349.                     $arrCodes array_merge($arrCodesrange($arrCode[0], $arrCode[1]));
  350.                     break;
  351.             }
  352.         }
  353.         return $arrCodes;
  354.     }
  355.     /**
  356.      * Store the current article ID so we know it for the product list
  357.      *
  358.      * @param Result $objRow
  359.      */
  360.     public function storeCurrentArticle($objRow)
  361.     {
  362.         $GLOBALS['ISO_CONFIG']['current_article']['id']  = $objRow->id;
  363.         $GLOBALS['ISO_CONFIG']['current_article']['pid'] = $objRow->pid;
  364.     }
  365.     /**
  366.      * Return pages in the current root available to the member
  367.      * Necessary to check if a product is allowed in the current site and cache the value
  368.      *
  369.      * @param array                      $arrPages
  370.      * @param MemberModel|FrontendUser $objMember
  371.      *
  372.      * @return array
  373.      */
  374.     public static function getPagesInCurrentRoot(array $arrPages$objMember null)
  375.     {
  376.         if (=== \count($arrPages)) {
  377.             return $arrPages;
  378.         }
  379.         /** @var PageModel $objPage */
  380.         global $objPage;
  381.         // $objPage not available, we don't know if the page is allowed
  382.         if (null === $objPage || $objPage == 0) {
  383.             return $arrPages;
  384.         }
  385.         static $arrAvailable = array();
  386.         static $arrUnavailable = array();
  387.         $intMember 0;
  388.         $arrGroups = [];
  389.         if (null !== $objMember) {
  390.             $intMember $objMember->id;
  391.             $arrGroups StringUtil::deserialize($objMember->groupstrue);
  392.         }
  393.         if (!isset($arrAvailable[$intMember])) {
  394.             $arrAvailable[$intMember] = array();
  395.         }
  396.         if (!isset($arrUnavailable[$intMember])) {
  397.             $arrUnavailable[$intMember] = array();
  398.         }
  399.         // Load remaining (not cached) pages.
  400.         foreach (array_diff($arrPages$arrAvailable[$intMember], $arrUnavailable[$intMember]) as $intPage) {
  401.             $objPageDetails PageModel::findWithDetails($intPage);
  402.             // Page is not in the current root
  403.             if ($objPageDetails->rootId != $objPage->rootId) {
  404.                 continue;
  405.             }
  406.             // Page is for guests only but we have a member
  407.             if ($objPageDetails->guests && $intMember && !$objPageDetails->protected) {
  408.                 $arrUnavailable[$intMember][] = $intPage;
  409.                 continue;
  410.             } elseif ($objPageDetails->protected) {
  411.                 // Page is protected but we have no member
  412.                 if ($intMember == 0) {
  413.                     $arrUnavailable[$intMember][] = $intPage;
  414.                     continue;
  415.                 }
  416.                 $arrPGroups StringUtil::deserialize($objPageDetails->groups);
  417.                 // Page is protected but has no groups
  418.                 if (!\is_array($arrPGroups)) {
  419.                     $arrUnavailable[$intMember][] = $intPage;
  420.                     continue;
  421.                 }
  422.                 // Page groups do not match with member groups
  423.                 if (\count(array_intersect($arrGroups$arrPGroups)) == 0) {
  424.                     $arrUnavailable[$intMember][] = $intPage;
  425.                     continue;
  426.                 }
  427.             }
  428.             $arrAvailable[$intMember][] = $intPage;
  429.         }
  430.         return array_intersect($arrPages$arrAvailable[$intMember]);
  431.     }
  432.     /**
  433.      * Show product name in breadcrumb
  434.      *
  435.      * @param array  $arrItems
  436.      *
  437.      * @return array
  438.      */
  439.     public function addProductToBreadcrumb($arrItems)
  440.     {
  441.         /** @var PageModel $objPage */
  442.         global $objPage;
  443.         if ($objPage->type === 'error_404'
  444.             || $objPage->type === 'error_403'
  445.             || !($alias Input::getAutoItem('product'falsetrue))
  446.             || ($objProduct Product::findAvailableByIdOrAlias($alias)) === null
  447.         ) {
  448.             return $arrItems;
  449.         }
  450.         global $objIsotopeListPage;
  451.         $last \count($arrItems) - 1;
  452.         // If we have a reader page that is a level below the list, rename the last item (the reader) to the product title
  453.         if (null !== $objIsotopeListPage && $objPage->pid == $objIsotopeListPage->id) {
  454.             $arrItems[$last]['title'] = $this->prepareMetaDescription($objProduct->meta_title ? : $objProduct->name);
  455.             $arrItems[$last]['link']  = $objProduct->name;
  456.         } else {
  457.             $listPage $objIsotopeListPage ?: $objPage;
  458.             $originalRow $listPage->originalRow();
  459.             // Replace the current page (if breadcrumb is insert tag, it would already be the product name)
  460.             $arrItems[$last] = array(
  461.                 'isRoot'   => (bool) $arrItems[$last]['isRoot'],
  462.                 'isActive' => false,
  463.                 'href'     => $listPage->getFrontendUrl(),
  464.                 'title'    => StringUtil::specialchars($originalRow['pageTitle'] ?: $originalRow['title']),
  465.                 'link'     => $originalRow['title'],
  466.                 'data'     => $originalRow,
  467.                 'class'    => ''
  468.             );
  469.             // Add a new item for the current product
  470.             $arrItems[] = array(
  471.                 'isRoot'   => false,
  472.                 'isActive' => true,
  473.                 'href'     => $objProduct->generateUrl($objPage),
  474.                 'title'    => StringUtil::specialchars($this->prepareMetaDescription($objProduct->meta_title ? : $objProduct->name)),
  475.                 'link'     => $objProduct->name,
  476.                 'data'     => $objPage->row(),
  477.             );
  478.         }
  479.         return $arrItems;
  480.     }
  481.     /**
  482.      * Initialize environment (language, objPage) for a given order
  483.      *
  484.      * @param Order  $objOrder
  485.      * @param string $strLanguage
  486.      */
  487.     public static function loadOrderEnvironment(Order $objOrder$strLanguage null)
  488.     {
  489.         global $objPage;
  490.         $strLanguage $strLanguage ?: $objOrder->language;
  491.         // Load page configuration
  492.         if ($objOrder->pageId && (null === $objPage || $objPage->id != $objOrder->pageId)) {
  493.             $objPage PageModel::findWithDetails($objOrder->pageId);
  494.             $objPage = static::loadPageConfig($objPage);
  495.         }
  496.         // Set the current system to the language when the user placed the order.
  497.         // This will result in correct e-mails and payment description.
  498.         self::setLanguage($strLanguage);
  499.         Isotope::setConfig($objOrder->getRelated('config_id'));
  500.         if (($objCart $objOrder->getRelated('source_collection_id')) !== null && $objCart instanceof Cart) {
  501.             Isotope::setCart($objCart);
  502.         }
  503.     }
  504.     /**
  505.      * Load system configuration into page object
  506.      *
  507.      * @param Result|PageModel $objPage
  508.      *
  509.      * @return Result
  510.      */
  511.     public static function loadPageConfig($objPage)
  512.     {
  513.         // Use the global date format if none is set
  514.         if ($objPage->dateFormat == '') {
  515.             $objPage->dateFormat $GLOBALS['TL_CONFIG']['dateFormat'];
  516.         }
  517.         if ($objPage->timeFormat == '') {
  518.             $objPage->timeFormat $GLOBALS['TL_CONFIG']['timeFormat'];
  519.         }
  520.         if ($objPage->datimFormat == '') {
  521.             $objPage->datimFormat $GLOBALS['TL_CONFIG']['datimFormat'];
  522.         }
  523.         // Set the admin e-mail address
  524.         if ($objPage->adminEmail != '') {
  525.             [$GLOBALS['TL_ADMIN_NAME'], $GLOBALS['TL_ADMIN_EMAIL']] = StringUtil::splitFriendlyEmail($objPage->adminEmail);
  526.         } else {
  527.             [$GLOBALS['TL_ADMIN_NAME'], $GLOBALS['TL_ADMIN_EMAIL']] = StringUtil::splitFriendlyEmail($GLOBALS['TL_CONFIG']['adminEmail']);
  528.         }
  529.         // Define the static URL constants
  530.         $isDebugMode System::getContainer()->getParameter('kernel.debug');
  531.         \define('TL_FILES_URL', ($objPage->staticFiles != '' && !$isDebugMode) ? $objPage->staticFiles TL_PATH '/' '');
  532.         \define('TL_ASSETS_URL', ($objPage->staticPlugins != '' && !$isDebugMode) ? $objPage->staticPlugins TL_PATH '/' '');
  533.         \define('TL_SCRIPT_URL'TL_ASSETS_URL);
  534.         \define('TL_PLUGINS_URL'TL_ASSETS_URL);
  535.         $objLayout Database::getInstance()->prepare("
  536.             SELECT l.*, t.templates
  537.             FROM tl_layout l
  538.             LEFT JOIN tl_theme t ON l.pid=t.id
  539.             WHERE l.id=?
  540.             ORDER BY l.id=? DESC
  541.         ")->limit(1)->execute($objPage->layout$objPage->layout);
  542.         if ($objLayout->numRows) {
  543.             // Get the page layout
  544.             $objPage->template      \strlen($objLayout->template) ? $objLayout->template 'fe_page';
  545.             $objPage->templateGroup $objLayout->templates;
  546.             // Store the output format
  547.             [$strFormat$strVariant] = explode('_'$objLayout->doctype);
  548.             $objPage->outputFormat  $strFormat;
  549.             $objPage->outputVariant $strVariant;
  550.         }
  551.         self::setLanguage($objPage->language);
  552.         return $objPage;
  553.     }
  554.     /**
  555.      * Adjust module and module id for certain payment and/or shipping modules
  556.      *
  557.      * @param PostSale $objPostsale
  558.      */
  559.     public function setPostsaleModuleSettings(PostSale $objPostsale)
  560.     {
  561.         // Payment method "Payone"
  562.         $strParam Input::post('param');
  563.         if (strpos($strParam'paymentMethodPayone') !== false) {
  564.             $intId = (int) str_replace('paymentMethodPayone'''$strParam);
  565.             $objPostsale->setModule('pay');
  566.             $objPostsale->setModuleId($intId);
  567.         }
  568.     }
  569.     /**
  570.      * Calculate price surcharge for attribute options
  571.      *
  572.      * @param float  $fltPrice
  573.      * @param object $objSource
  574.      * @param string $strField
  575.      * @param int    $intTaxClass
  576.      * @param array  $arrOptions
  577.      *
  578.      * @return float
  579.      * @throws \Exception
  580.      */
  581.     public function addOptionsPrice($fltPrice$objSource$strField$intTaxClass, array $arrOptions)
  582.     {
  583.         $fltAmount $fltPrice;
  584.         if ($objSource instanceof IsotopePrice
  585.             && ($objProduct $objSource->getRelated('pid')) instanceof IsotopeProduct
  586.             && $objProduct->getType() !== null) {
  587.             /** @var IsotopeProduct|Standard $objProduct */
  588.             $arrAttributes array_intersect(
  589.                 Attribute::getPricedFields(),
  590.                 array_merge(
  591.                     $objProduct->getType()->getAttributes(),
  592.                     $objProduct->getType()->getVariantAttributes()
  593.                 )
  594.             );
  595.             foreach ($arrAttributes as $field) {
  596.                 if (($objAttribute $GLOBALS['TL_DCA']['tl_iso_product']['attributes'][$field]) !== null
  597.                     && $objAttribute instanceof IsotopeAttributeWithOptions
  598.                     && $objAttribute->canHavePrices()
  599.                     && ($objOptions $objAttribute->getOptionsFromManager($objProduct)) !== null
  600.                 ) {
  601.                     $value $objAttribute->isCustomerDefined() ? ($arrOptions[$field] ?? null) : $objProduct->$field;
  602.                     $value StringUtil::deserialize($valuetrue);
  603.                     /** @var AttributeOption $objOption */
  604.                     foreach ($objOptions as $objOption) {
  605.                         if (\in_array($objOption->getLanguageId(), $value)) {
  606.                             $amount $objOption->getAmount($fltPrice0);
  607.                             $objTax $objSource->getRelated('tax_class');
  608.                             if ($objOption->isPercentage() || !$objTax instanceof TaxClass) {
  609.                                 $fltAmount += $amount;
  610.                                 continue;
  611.                             }
  612.                             if ('net_price' === $strField) {
  613.                                 $fltAmount += $objTax->calculateNetPrice($amount);
  614.                             } elseif ('gross_price' === $strField) {
  615.                                 $fltAmount += $objTax->calculateGrossPrice($amount);
  616.                             } else {
  617.                                 $fltAmount += $amount;
  618.                             }
  619.                         }
  620.                     }
  621.                 }
  622.             }
  623.         }
  624.         return $fltAmount;
  625.     }
  626.     /**
  627.      * Callback for add_to_cart button
  628.      *
  629.      * @param IsotopeProduct $objProduct
  630.      * @param array          $arrConfig
  631.      *
  632.      * @deprecated Deprecated since Isotope 2.5
  633.      */
  634.     public function addToCart(IsotopeProduct $objProduct, array $arrConfig = array())
  635.     {
  636.         $action = new CartAction();
  637.         $action->handleSubmit($objProduct$arrConfig);
  638.     }
  639.     /**
  640.      * Callback for add_to_cart button if a product is being edited.
  641.      *
  642.      * @param IsotopeProduct $objProduct
  643.      * @param array          $arrConfig
  644.      *
  645.      * @deprecated Deprecated since Isotope 2.5
  646.      */
  647.     public function updateCart(IsotopeProduct $objProduct, array $arrConfig = array())
  648.     {
  649.         $action = new CartAction();
  650.         $action->handleSubmit($objProduct$arrConfig);
  651.     }
  652.     /**
  653.      * Callback for toggle_favorites button
  654.      *
  655.      * @param IsotopeProduct $objProduct
  656.      * @param array          $arrConfig
  657.      *
  658.      * @deprecated Deprecated since Isotope 2.5
  659.      */
  660.     public function toggleFavorites(IsotopeProduct $objProduct, array $arrConfig = array())
  661.     {
  662.         $action = new FavoriteAction();
  663.         $action->handleSubmit($objProduct$arrConfig);
  664.     }
  665.     /**
  666.      * Replaces Isotope specific InsertTags in Frontend
  667.      *
  668.      * @param string $strTag
  669.      *
  670.      * @return mixed
  671.      *
  672.      * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use InsertTag::replace() instead.
  673.      */
  674.     public function replaceIsotopeTags($strTag)
  675.     {
  676.         $callback = new InsertTag();
  677.         return $callback->replace($strTag);
  678.     }
  679.     /**
  680.      * Hook callback for changelanguage extension to support language switching on product reader page
  681.      *
  682.      * @param array $arrGet
  683.      *
  684.      * @return array
  685.      *
  686.      * @deprecated Deprecated since Isotope 2.4. See ChangeLanguageListener
  687.      */
  688.     public function translateProductUrls($arrGet)
  689.     {
  690.         $listener = new ChangeLanguageListener();
  691.         return $listener->onTranslateUrlParameters($arrGet);
  692.     }
  693.     /**
  694.      * Return all error, confirmation and info messages as HTML string
  695.      *
  696.      * @return string
  697.      *
  698.      * @deprecated use Message::generate
  699.      */
  700.     public static function getIsotopeMessages()
  701.     {
  702.         return Message::generate();
  703.     }
  704.     /**
  705.      * Switches the environment to the given language.
  706.      *
  707.      * @param string $language
  708.      */
  709.     private static function setLanguage($language)
  710.     {
  711.         $GLOBALS['TL_LANGUAGE'] = $language;
  712.         if (class_exists(ContaoCoreBundle::class)) {
  713.             /** @var ContainerInterface $container */
  714.             $container System::getContainer();
  715.             if ($container->has('request_stack') && null !== ($request $container->get('request_stack')->getCurrentRequest())) {
  716.                 $request->setLocale($language);
  717.             }
  718.             if ($container->has('translator')) {
  719.                 $container->get('translator')->setLocale($language);
  720.             }
  721.         }
  722.         System::loadLanguageFile('default'$languagetrue);
  723.     }
  724. }