├── Api ├── ContainerInterface.php ├── DataLayer │ ├── BeginCheckoutInterface.php │ ├── Cart │ │ └── ItemInterface.php │ ├── Order │ │ └── ItemInterface.php │ ├── Product │ │ └── ItemInterface.php │ ├── PurchaseInterface.php │ ├── ViewCartInterface.php │ └── ViewItemInterface.php └── Transaction │ └── LogInterface.php ├── Block ├── AbstractDataLayer.php ├── Adminhtml │ └── System │ │ └── Config │ │ └── Form │ │ ├── Attention.php │ │ ├── EventList.php │ │ ├── ExportServerContainerButton.php │ │ ├── ExportWebContainerButton.php │ │ ├── Info.php │ │ ├── InfoMeasurementProtocol.php │ │ ├── InfoPlan.php │ │ ├── InfoPlanExtra.php │ │ ├── InfoPlanPlus.php │ │ └── ProtectCustomerData.php ├── DataLayer │ ├── BeginCheckout.php │ ├── Other.php │ ├── Purchase.php │ ├── PurchaseGetOrderFromRequest.php │ ├── ViewCart.php │ └── ViewItem.php └── GtmCode.php ├── Controller ├── Adminhtml │ ├── ContainerGenerate.php │ └── WebContainer │ │ └── Generate.php └── LastOrder │ └── DataLayer.php ├── LICENSE.txt ├── Model ├── AbstractDataLayer.php ├── Config.php ├── Config │ └── Source │ │ ├── BrandAttribute.php │ │ ├── InstallGtmOptions.php │ │ ├── OrderStatuses.php │ │ └── ProductAttribute.php ├── DataLayer │ ├── AbstractOrder.php │ ├── BeginCheckout.php │ ├── Cart │ │ └── Item.php │ ├── Order │ │ └── Item.php │ ├── Product │ │ └── Item.php │ ├── Purchase.php │ ├── ViewCart.php │ └── ViewItem.php ├── ResourceModel │ ├── Transaction.php │ └── Transaction │ │ └── Collection.php ├── Transaction.php ├── Transaction │ └── Log.php ├── TransactionRepository.php └── WebContainer.php ├── Plugin ├── Magefan │ └── GoogleTagManager │ │ └── Api │ │ └── DataLayer │ │ └── PurchaseInterface.php └── Magento │ ├── Customer │ └── CustomerData │ │ └── Customer.php │ └── Framework │ └── App │ └── Config │ └── ScopeConfig.php ├── README.md ├── Setup └── Patch │ └── Schema │ └── ChangePath.php ├── composer.json ├── etc ├── acl.xml ├── adminhtml │ ├── routes.xml │ └── system.xml ├── config.xml ├── csp_whitelist.xml ├── db_schema.xml ├── di.xml ├── frontend │ ├── di.xml │ └── routes.xml └── module.xml ├── registration.php └── view ├── adminhtml └── templates │ └── system │ └── config │ ├── button │ └── export-container-button.phtml │ └── event │ └── list.phtml └── frontend ├── layout ├── catalog_product_view.xml ├── checkout_cart_index.xml ├── checkout_index_index.xml ├── checkout_onepage_success.xml ├── default.xml ├── hyva_checkout_index_index.xml ├── p2c2p_payment_success.xml └── redsys_checkout_success.xml └── templates ├── js_code.phtml └── no_js_code.phtml /Api/ContainerInterface.php: -------------------------------------------------------------------------------- 1 | config = $config; 41 | $this->mfSecureRenderer = $mfSecureRenderer ?: \Magento\Framework\App\ObjectManager::getInstance() 42 | ->get(SecureHtmlRendererInterface::class); 43 | parent::__construct($context, $data); 44 | } 45 | 46 | /** 47 | * Get GTM datalayer 48 | * 49 | * @return array 50 | */ 51 | abstract protected function getDataLayer(): array; 52 | 53 | /** 54 | * Init GTM datalayer 55 | * 56 | * @return string 57 | */ 58 | protected function _toHtml(): string 59 | { 60 | if ($this->config->isEnabled()) { 61 | $dataLayer = $this->getDataLayer(); 62 | if ($dataLayer) { 63 | $json = json_encode($dataLayer); 64 | $json = str_replace('"getMfGtmCustomerIdentifier()"', 'getMfGtmCustomerIdentifier()', $json); 65 | $script = ' 66 | window.dataLayer = window.dataLayer || []; 67 | window.dataLayer.push(' . $json . '); 68 | '; 69 | return $this->mfSecureRenderer->renderTag('script', ['style' => 'display:none'], $script, false); 70 | } 71 | } 72 | 73 | return ''; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/Attention.php: -------------------------------------------------------------------------------- 1 | 28 | Attention! Once you change and save the "Web/Server Container", "Google Analytics 4" or "Google Ads" settings, 29 | please don\'t forget to scroll down to the "Export Container" section 30 | and click the "Generate JSON Container & Download File" button to export container data. 31 | After you save the file, 32 | import it to your Google Tag Manager container. 33 | '; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/EventList.php: -------------------------------------------------------------------------------- 1 | moduleManager = $moduleManager; 38 | parent::__construct($context, $data); 39 | } 40 | 41 | /** 42 | * Set template to itself 43 | * 44 | * @return $this 45 | */ 46 | protected function _prepareLayout(): EventList 47 | { 48 | parent::_prepareLayout(); 49 | if (!$this->getTemplate()) { 50 | $this->setTemplate(static::EVENT_LIST_TEMPLATE); 51 | } 52 | return $this; 53 | } 54 | 55 | /** 56 | * Render event list 57 | * 58 | * @param AbstractElement $element 59 | * @return string 60 | */ 61 | public function render(AbstractElement $element): string 62 | { 63 | // Remove scope label 64 | $element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue(); 65 | return parent::render($element); 66 | } 67 | 68 | /** 69 | * Get the event list and scripts contents 70 | * 71 | * @param AbstractElement $element 72 | * @return string 73 | */ 74 | protected function _getElementHtml(AbstractElement $element): string 75 | { 76 | return $this->_toHtml(); 77 | } 78 | 79 | /** 80 | * Retrieve true if GTM Plus is enabled 81 | * 82 | * @return bool 83 | */ 84 | public function isPlusEnabled(): bool 85 | { 86 | return (bool)$this->moduleManager->isEnabled('Magefan_GoogleTagManagerPlus'); 87 | } 88 | 89 | /** 90 | * Retrieve true if GTM Extra is enabled 91 | * 92 | * @return bool 93 | */ 94 | public function isExtraEnabled(): bool 95 | { 96 | return (bool)$this->moduleManager->isEnabled('Magefan_GoogleTagManagerExtra'); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/ExportServerContainerButton.php: -------------------------------------------------------------------------------- 1 | getUrl( 24 | 'mfgoogletagmanagerextra/serverContainer/generate', 25 | [ 26 | 'store_id' => (int)$this->getRequest()->getParam('store') ?: null, 27 | 'website_id' => (int)$this->getRequest()->getParam('website') ?: null 28 | ] 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/ExportWebContainerButton.php: -------------------------------------------------------------------------------- 1 | unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue(); 38 | return parent::render($element); 39 | } 40 | 41 | /** 42 | * Get the button and scripts contents 43 | * 44 | * @param AbstractElement $element 45 | * @return string 46 | */ 47 | protected function _getElementHtml(AbstractElement $element): string 48 | { 49 | return $this->_toHtml(); 50 | } 51 | 52 | /** 53 | * @return string 54 | */ 55 | public function getConteinderType() 56 | { 57 | return $this->conteinerType; 58 | } 59 | 60 | /** 61 | * @return string 62 | */ 63 | public function getContainerGenerateUrl() 64 | { 65 | return $this->getUrl( 66 | 'mfgoogletagmanager/webContainer/generate', 67 | [ 68 | 'store_id' => (int)$this->getRequest()->getParam('store') ?: null, 69 | 'website_id' => (int)$this->getRequest()->getParam('website') ?: null 70 | ] 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/Info.php: -------------------------------------------------------------------------------- 1 | '; 24 | $html .= 'GA4 Measurement Protocol is not available while GTM Server Container is enabled.'; 25 | $html .= ''; 26 | 27 | $script = ' 28 | require(["jquery", "Magento_Ui/js/modal/alert", "domReady!"], function($, alert){ 29 | setInterval(function(){ 30 | if (parseInt($("#mfgoogletagmanager_server_container_enabled").val())) { 31 | $("#ga4mp-disabled").show(); 32 | } else { 33 | $("#ga4mp-disabled").hide(); 34 | } 35 | }, 1000); 36 | }); 37 | '; 38 | 39 | $html .= $this->mfSecureRenderer->renderTag('script', [], $script, false); 40 | 41 | return $html; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/InfoPlan.php: -------------------------------------------------------------------------------- 1 | getModuleVersion->execute($this->getModuleName() . $this->getMinPlan())) { 39 | return ''; 40 | } 41 | 42 | $html = ''; 43 | 44 | if ($text = $this->getText()) { 45 | $textHtml = '
'; 46 | $textHtml .= $text . ' Read more.'; 47 | $textHtml .= '
'; 48 | } 49 | 50 | $optionAvailableInText = ($this->getMinPlan() == 'Extra') 51 | ? 'This option is available in Extra plan only.' 52 | : 'This option is available in Plus or Extra plans only.'; 53 | 54 | $script = ' 55 | require(["jquery", "Magento_Ui/js/modal/alert", "domReady!"], function($, alert){ 56 | setInterval(function(){ 57 | var sections = ' . $this->getSectionsJson() . '; 58 | 59 | sections.forEach(function(sectionId) { 60 | var $section = $("#" + sectionId + "-state").parent(".section-config"); 61 | if (!$section.length) { 62 | $section = $("#" + sectionId).parents("tr:first"); 63 | } else { 64 | var $fieldset = $section.find("fieldset:first"); 65 | if (!$fieldset.data("mfftext")) { 66 | $fieldset.data("mfftext", 1); 67 | $fieldset.prepend(\'' . $textHtml . '\'); 68 | } 69 | } 70 | 71 | $section.find(".use-default").css("visibility", "hidden"); 72 | $section.find("input,select").each(function(){ 73 | $(this).attr("readonly", "readonly"); 74 | $(this).removeAttr("disabled"); 75 | if ($(this).data("mffdisabled")) return; 76 | $(this).data("mffdisabled", 1); 77 | $(this).click(function(){ 78 | $(this).val($(this).data("mfOldValue")).trigger("change"); 79 | alert({ 80 | title: "You cannot use this option.", 81 | content: "' . $optionAvailableInText . '", 82 | buttons: [{ 83 | text: "Upgrade Plan Now", 84 | class: "action primary accept", 85 | click: function () { 86 | window.open("https://magefan.com/magento-2-google-tag-manager/pricing?utm_source=gtm_config&utm_medium=link&utm_campaign=regular"); 87 | } 88 | }] 89 | }); 90 | }).on("focus", function() { 91 | $(this).data("mfOldValue", $(this).val()); 92 | }); 93 | }); 94 | }); 95 | }, 1000); 96 | }); 97 | '; 98 | 99 | $html .= $this->mfSecureRenderer->renderTag('script', [], $script, false); 100 | 101 | return $html; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/InfoPlanExtra.php: -------------------------------------------------------------------------------- 1 | %1 plan only.", $this->getMinPlan()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/InfoPlanPlus.php: -------------------------------------------------------------------------------- 1 | Plus or Extra plans only."); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/ProtectCustomerData.php: -------------------------------------------------------------------------------- 1 | getUrl('*/*/*/section/web'); 25 | $comment = ' 26 | Note: this option works only when default Magento Cookie Restriction Mode is enabled at 27 | Stores > Configuration > General > Web > Default Cookie Settings. 28 | If Magento Cookie Restriction Mode is disabled, then GTM JavaScript will be loaded before consent ignoring the "Load GTM Script Before Consent" option.

29 | Even if GTM JavaScript is loaded before customer`s consent, 30 | GTM still waits for consent to send user data related to advertising and analytics.'; 31 | 32 | $element->setComment($comment); 33 | return parent::render($element); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Block/DataLayer/BeginCheckout.php: -------------------------------------------------------------------------------- 1 | checkoutSession = $checkoutSession; 48 | $this->beginCheckout = $beginCheckout; 49 | parent::__construct($context, $config, $data); 50 | } 51 | 52 | /** 53 | * Get GTM datalayer for checkout page 54 | * 55 | * @return array 56 | * @throws LocalizedException 57 | * @throws NoSuchEntityException 58 | */ 59 | protected function getDataLayer(): array 60 | { 61 | $quote = $this->checkoutSession->getQuote(); 62 | return $this->beginCheckout->get($quote); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Block/DataLayer/Other.php: -------------------------------------------------------------------------------- 1 | checkoutSession = $checkoutSession; 47 | $this->purchase = $purchase; 48 | parent::__construct($context, $config, $data); 49 | } 50 | 51 | /** 52 | * Get GTM datalayer for checkout success page 53 | * 54 | * @return array 55 | * @throws NoSuchEntityException 56 | */ 57 | protected function getDataLayer(): array 58 | { 59 | $order = $this->getOrder(); 60 | return $this->purchase->get($order); 61 | } 62 | 63 | /** 64 | * @return \Magento\Sales\Model\Order 65 | */ 66 | protected function getOrder() 67 | { 68 | return $this->checkoutSession->getLastRealOrder(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Block/DataLayer/PurchaseGetOrderFromRequest.php: -------------------------------------------------------------------------------- 1 | getOrderFactory()->create()->loadByIncrementId($this->getOrderId()); 24 | 25 | if (!$order->getId()) { 26 | return null; 27 | } 28 | return $order; 29 | } 30 | 31 | /** 32 | * @return mixed 33 | */ 34 | protected function getOrderFactory() 35 | { 36 | $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); 37 | return $objectManager->get(\Magento\Sales\Model\OrderFactory::class); 38 | } 39 | 40 | /** 41 | * @return string 42 | */ 43 | protected function getOrderId() 44 | { 45 | $request = $this->getRequest(); 46 | 47 | if ($request) { 48 | return (string)$request->getParam('order_id'); 49 | } 50 | 51 | return 0; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Block/DataLayer/ViewCart.php: -------------------------------------------------------------------------------- 1 | checkoutSession = $checkoutSession; 48 | $this->viewCart = $viewCart; 49 | parent::__construct($context, $config, $data); 50 | } 51 | 52 | /** 53 | * Get GTM datalayer for shopping cart page 54 | * 55 | * @return array 56 | * @throws LocalizedException 57 | * @throws NoSuchEntityException 58 | */ 59 | protected function getDataLayer(): array 60 | { 61 | $quote = $this->checkoutSession->getQuote(); 62 | return $this->viewCart->get($quote); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Block/DataLayer/ViewItem.php: -------------------------------------------------------------------------------- 1 | registry = $registry; 48 | $this->viewItem = $viewItem; 49 | parent::__construct($context, $config, $data); 50 | } 51 | 52 | /** 53 | * Get GTM datalayer for product page 54 | * 55 | * @return array 56 | * @throws NoSuchEntityException 57 | */ 58 | protected function getDataLayer(): array 59 | { 60 | return $this->viewItem->get($this->getCurrentProduct()); 61 | } 62 | 63 | /** 64 | * Get current product 65 | * 66 | * @return Product 67 | */ 68 | private function getCurrentProduct(): Product 69 | { 70 | return $this->registry->registry('current_product'); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Block/GtmCode.php: -------------------------------------------------------------------------------- 1 | config = $config; 34 | parent::__construct($context, $data); 35 | } 36 | 37 | /** 38 | * @return string 39 | */ 40 | public function getGtmScript(): string 41 | { 42 | $partsForRemove = [ 43 | '', 44 | '', 45 | '' 47 | ]; 48 | $gtmScript = $this->config->getGtmScript(); 49 | if ($gtmScript) { 50 | foreach ($partsForRemove as $part) { 51 | $gtmScript = str_replace($part, '', $gtmScript); 52 | } 53 | 54 | $gtmScript = str_replace($this->getGtmJsUrl(true), $this->getGtmJsUrl(), $gtmScript); 55 | 56 | return $gtmScript; 57 | } 58 | 59 | return ''; 60 | } 61 | 62 | /** 63 | * @return string 64 | */ 65 | public function getGtmNoScript(): string 66 | { 67 | return $this->config->getGtmNoScript(); 68 | } 69 | 70 | /** 71 | * @return string 72 | */ 73 | public function getPublicId(): string 74 | { 75 | return $this->config->getPublicId(); 76 | } 77 | 78 | /** 79 | * Check if protect customer data is enabled 80 | * 81 | * @return bool 82 | */ 83 | public function isProtectCustomerDataEnabled(): bool 84 | { 85 | return $this->config->isProtectCustomerDataEnabled(); 86 | } 87 | 88 | 89 | /** 90 | * Retrieve true if gtm script should be loaded before customer provice consent. 91 | * 92 | * @return bool 93 | */ 94 | public function isLoadBeforeConsent(): bool 95 | { 96 | return $this->config->isLoadBeforeConsent(); 97 | } 98 | 99 | /** 100 | * Retrieve true if cookie restriction mode enabled 101 | * 102 | * @return bool 103 | */ 104 | public function isCookieRestrictionModeEnabled() 105 | { 106 | return $this->config->isCookieRestrictionModeEnabled(); 107 | } 108 | 109 | /** 110 | * Retrieve true if mf cookie consent extension is enabled 111 | * 112 | * @return bool 113 | */ 114 | public function isMfCookieConsentExtensionEnabled() 115 | { 116 | return $this->config->isMfCookieConsentExtensionEnabled(); 117 | } 118 | 119 | /** 120 | * Get current website ID 121 | * 122 | * @return int 123 | * @throws NoSuchEntityException 124 | */ 125 | public function getWebsiteId(): int 126 | { 127 | return (int)$this->_storeManager->getStore()->getWebsiteId(); 128 | } 129 | 130 | /** 131 | * @return string 132 | */ 133 | protected function _toHtml(): string 134 | { 135 | 136 | if ($this->config->isEnabled() /* && $this->getPublicId() */) { 137 | return parent::_toHtml(); 138 | } 139 | 140 | return ''; 141 | } 142 | 143 | /** 144 | * @return Config 145 | */ 146 | public function getConfig() 147 | { 148 | return $this->config; 149 | } 150 | 151 | /** 152 | * Retrieve true if speed optimization is enabled 153 | * 154 | * @return bool 155 | */ 156 | public function isSpeedOptimizationEnabled(): bool 157 | { 158 | return (bool)$this->config->isSpeedOptimizationEnabled(); 159 | } 160 | 161 | 162 | /** 163 | * @param $origin 164 | * @return string 165 | * @throws NoSuchEntityException 166 | */ 167 | public function getGtmJsUrl($origin = false): string 168 | { 169 | if (!$origin && $this->getConfig()->isGoogleTagGatewayEnabled()) { 170 | return $this->_storeManager->getStore()->getBaseUrl() . '/mfgtmproxy/'; 171 | } else { 172 | return 'https://www.googletagmanager.com/gtm.js'; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Controller/Adminhtml/ContainerGenerate.php: -------------------------------------------------------------------------------- 1 | resultRawFactory = $resultRawFactory; 100 | $this->fileFactory = $fileFactory; 101 | $this->dateTime = $dateTime; 102 | $this->logger = $logger; 103 | $this->redirect = $redirect; 104 | $this->config = $config; 105 | $this->container = $container; 106 | $this->storeManager = $storeManager; 107 | parent::__construct($context); 108 | } 109 | 110 | /** 111 | * Generate and download JSON container 112 | */ 113 | public function execute() 114 | { 115 | //clear echo current buffer 116 | if (function_exists('ob_get_clean')) { 117 | ob_get_clean(); 118 | } 119 | 120 | /** @var Redirect $resultRedirect */ 121 | $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); 122 | 123 | /* Ability to generate JSON file even if the module is disabled. 124 | if (!$this->config->isEnabled()) { 125 | $this->messageManager 126 | ->addErrorMessage(__('To generate a JSON container, please enable the extension first.')); 127 | return $resultRedirect->setPath($this->redirect->getRefererUrl()); 128 | } 129 | */ 130 | 131 | try { 132 | $storeId = (string)$this->getRequest()->getParam('store_id') ?: null; 133 | if (!$storeId && ($websiteId = ((string)$this->getRequest()->getParam('website_id') ?: null))) { 134 | $storeId = $this->storeManager->getWebsite($websiteId)->getDefaultStore()->getId(); 135 | } 136 | 137 | $container = $this->container->generate($storeId); 138 | 139 | $fileContent = [ 140 | 'type' => 'string', 141 | 'value' => json_encode($container, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), 142 | 'rm' => true 143 | ]; 144 | return $this->fileFactory->create( 145 | sprintf('GTM' . '_%s.json', $this->dateTime->date('Y-m-d_H-i-s')), 146 | $fileContent, 147 | DirectoryList::MEDIA, 148 | 'application/json' 149 | ); 150 | } catch (\Exception $e) { 151 | $this->logger->critical('Error message', ['exception' => $e]); 152 | $this->messageManager->addErrorMessage(__('Something went wrong while generating the file.')); 153 | return $resultRedirect->setPath($this->redirect->getRefererUrl()); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /Controller/Adminhtml/WebContainer/Generate.php: -------------------------------------------------------------------------------- 1 | config = $config; 64 | $this->purchaseDataLayer = $purchaseDataLayer; 65 | $this->orderRepository = $orderRepository; 66 | $this->checkoutSession = $checkoutSession; 67 | $this->jsonResultFactory = $jsonResultFactory; 68 | } 69 | 70 | public function execute() 71 | { 72 | $result = $this->jsonResultFactory->create(); 73 | if ($this->config->isEnabled()) { 74 | $orderId = $this->checkoutSession->getLastOrderId(); 75 | try { 76 | $order = $this->orderRepository->get($orderId); 77 | } catch (\NoSuchElementException $e) { 78 | $order = null; 79 | } 80 | if ($order && $order->getEntityId()) { 81 | $dataLayer = $this->purchaseDataLayer->get($order); 82 | if ($dataLayer) { 83 | $result->setData($dataLayer); 84 | } 85 | } 86 | } 87 | return $result; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Please visit Magefan.com for license details (https://magefan.com/end-user-license-agreement) -------------------------------------------------------------------------------- /Model/AbstractDataLayer.php: -------------------------------------------------------------------------------- 1 | config = $config; 87 | $this->storeManager = $storeManager; 88 | $this->categoryRepository = $categoryRepository; 89 | $this->request = $request ?: ObjectManager::getInstance()->get( 90 | RequestInterface::class 91 | ); 92 | $this->registry = $registry ?: ObjectManager::getInstance()->get( 93 | Registry::class 94 | ); 95 | $this->session = $session ?: ObjectManager::getInstance()->get( 96 | Session::class 97 | ); 98 | $this->groupRepository = $groupRepository ?: ObjectManager::getInstance()->get( 99 | GroupRepository::class 100 | ); 101 | } 102 | 103 | 104 | /** 105 | * @return string 106 | */ 107 | public function getEcommPageType(): string 108 | { 109 | if ('other' === $this->ecommPageType) { 110 | $fullActionName = $this->request->getFullActionName(); 111 | switch ($fullActionName) { 112 | case 'cms_index_index': 113 | $this->ecommPageType = 'home'; 114 | break; 115 | case 'catalog_category_view': 116 | $this->ecommPageType = 'category'; 117 | break; 118 | case 'catalog_product_view': 119 | $this->ecommPageType = 'product'; 120 | break; 121 | case 'checkout_cart_index': 122 | $this->ecommPageType = 'cart'; 123 | break; 124 | case 'checkout_index_index': 125 | $this->ecommPageType = 'checkout'; 126 | break; 127 | case 'contact_index_index': 128 | $this->ecommPageType = 'contact'; 129 | break; 130 | case 'catalogsearch_result_index': 131 | $this->ecommPageType = 'searchresults'; 132 | break; 133 | case 'cms_page_view': 134 | $this->ecommPageType = 'cmspage'; 135 | break; 136 | } 137 | } 138 | 139 | return $this->ecommPageType; 140 | } 141 | 142 | /** 143 | * @param string $ecommPageType 144 | * @return void 145 | */ 146 | public function setEcommPageType(string $ecommPageType): void 147 | { 148 | $this->ecommPageType = $ecommPageType; 149 | } 150 | 151 | /** 152 | * Get category names 153 | * 154 | * @param Product $product 155 | * @return array 156 | * @throws NoSuchEntityException 157 | */ 158 | protected function getCategoryNames(Product $product): array 159 | { 160 | $result = []; 161 | 162 | if (!$this->config->getCategoriesAttribute()) { 163 | return $result; 164 | } 165 | 166 | if ($productCategory = $this->getCategoryByProduct($product)) { 167 | $categoryIds = $productCategory->getPathIds(); 168 | $number = 1; 169 | $categoryNames = []; 170 | foreach ($categoryIds as $categoryId) { 171 | $category = $this->categoryRepository->get($categoryId, $this->storeManager->getStore()->getId()); 172 | if ($category->getLevel() < 2) { 173 | continue; 174 | } 175 | 176 | $result['item_category' . (($number == 1) ? '' : $number)] = $category->getName(); 177 | $categoryNames[] = $category->getName(); 178 | $number++; 179 | } 180 | $result['category'] = implode(',', $categoryNames); 181 | 182 | } 183 | 184 | return $result; 185 | } 186 | 187 | /** 188 | * Get product category 189 | * 190 | * @param Product $product 191 | * @return CategoryInterface|null 192 | * @throws NoSuchEntityException 193 | */ 194 | private function getCategoryByProduct(Product $product): ?CategoryInterface 195 | { 196 | if ('catalog_category_product' == $this->request->getFullActionName()) { 197 | if ($category = $this->registry->registry('current_category')) { 198 | return $category; 199 | } 200 | } 201 | 202 | $productCategory = null; 203 | $categoryIds = $product->getCategoryIds(); 204 | 205 | if ($categoryIds) { 206 | $level = -1; 207 | $store = $this->storeManager->getStore(); 208 | $rootCategoryId = $store->getRootCategoryId(); 209 | 210 | foreach ($categoryIds as $categoryId) { 211 | try { 212 | $category = $this->categoryRepository->get($categoryId, $store->getId()); 213 | if ($category->getIsActive() 214 | && $category->getLevel() > $level 215 | && in_array($rootCategoryId, $category->getPathIds()) 216 | ) { 217 | $level = $category->getLevel(); 218 | $productCategory = $category; 219 | } 220 | } catch (Exception $e) { // phpcs:ignore 221 | /* Do nothing */ 222 | } 223 | } 224 | } 225 | 226 | return $productCategory; 227 | } 228 | 229 | /** 230 | * Get current currency code 231 | * 232 | * @return string 233 | * @throws NoSuchEntityException 234 | */ 235 | protected function getCurrentCurrencyCode(): string 236 | { 237 | return $this->storeManager->getStore()->getCurrentCurrencyCode(); 238 | } 239 | 240 | /** 241 | * Format price 242 | * 243 | * @param float $price 244 | * @return float 245 | */ 246 | protected function formatPrice(float $price): float 247 | { 248 | return (float)number_format($price, 2, '.', ''); 249 | } 250 | 251 | /** 252 | * Get product price 253 | * @deprecated 254 | * @param Product $product 255 | * @return float 256 | */ 257 | protected function getPrice(Product $product): float 258 | { 259 | $priceInfo = $product->getPriceInfo()->getPrice('final_price')->getAmount(); 260 | $price = $priceInfo->getValue(); 261 | return $this->formatPrice((float)$price); 262 | } 263 | 264 | /** 265 | * @param $product 266 | * @return float 267 | */ 268 | protected function getProductValue($product): float 269 | { 270 | $priceInfo = $product->getPriceInfo()->getPrice('final_price')->getAmount(); 271 | if (!$this->config->isPurchaseTaxEnabled()) { 272 | $value = $priceInfo->getValue('tax'); 273 | } else { 274 | $value = $priceInfo->getValue(); 275 | } 276 | 277 | return $this->formatPrice((float)$value); 278 | } 279 | 280 | /** 281 | * @param Product $product 282 | * @param string $attributeCode 283 | * @return string 284 | */ 285 | protected function getProductAttributeValue(Product $product, ?string $attributeCode): string 286 | { 287 | if ($attributeCode) { 288 | $result = $product->getData($attributeCode); 289 | if (is_numeric($result) && !in_array($attributeCode, ['sku', 'entity_id'])) { 290 | $result = $product->getResource()->getAttribute($attributeCode)->getFrontend()->getValue($product); 291 | } 292 | 293 | if (is_array($result)) { 294 | $result = implode(', ', $result); 295 | } 296 | 297 | if ($result) { 298 | return (string)$result; 299 | } 300 | } 301 | 302 | return ''; 303 | } 304 | 305 | /** 306 | * @return string 307 | * @throws NoSuchEntityException 308 | * @throws \Magento\Framework\Exception\LocalizedException 309 | */ 310 | protected function getCustomerGroupCode(): string 311 | { 312 | if (null === $this->customerGroupCode) { 313 | $this->customerGroupCode = 'Guest'; 314 | $customerGroupId = $this->session->getCustomerGroupId(); 315 | if ($customerGroupId) { 316 | try { 317 | $group = $this->groupRepository->getById($customerGroupId); 318 | $this->customerGroupCode = (string)$group->getCode(); 319 | } catch (NoSuchEntityException $e) { 320 | /* Do nothing */ 321 | } 322 | } 323 | } 324 | 325 | return $this->customerGroupCode; 326 | } 327 | 328 | /** 329 | * @param array $data 330 | * @return array 331 | */ 332 | protected function eventWrap(array $data): array 333 | { 334 | if (empty($data)) { 335 | return $data; 336 | } 337 | 338 | $data = $this->addCustomerGroup($data); 339 | $data = $this->addMfUniqueEventId($data); 340 | $data = $this->addEcommPageType($data); 341 | $data = $this->addCustomerIdentifier($data); 342 | 343 | return $data; 344 | } 345 | 346 | /** 347 | * @param array $data 348 | * @return array 349 | */ 350 | protected function addCustomerGroup(array $data): array 351 | { 352 | if (!isset($data['customerGroup'])) { 353 | $data['customerGroup'] = $this->getCustomerGroupCode(); 354 | } 355 | return $data; 356 | } 357 | 358 | /** 359 | * @param array $data 360 | * @return array 361 | */ 362 | protected function addMfUniqueEventId(array $data): array 363 | { 364 | 365 | $hash = md5(json_encode($data) . microtime()); 366 | $event = isset($data['event']) ? $data['event'] : 'event'; 367 | $eventId = $event . '_' . $hash; 368 | 369 | $data['magefanUniqueEventId'] = $eventId; 370 | 371 | return $data; 372 | } 373 | 374 | protected function addEcommPageType(array $data): array 375 | { 376 | if (!isset($data['ecomm_pagetype'])) { 377 | $data['ecomm_pagetype'] = $this->getEcommPageType(); 378 | } 379 | 380 | return $data; 381 | } 382 | 383 | /** 384 | * @param array $data 385 | * @return array 386 | */ 387 | protected function addCustomerIdentifier(array $data): array 388 | { 389 | if (empty($data['customer_identifier'])) { 390 | $data['customer_identifier'] = 'getMfGtmCustomerIdentifier()'; 391 | } 392 | return $data; 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /Model/Config.php: -------------------------------------------------------------------------------- 1 | scopeConfig = $scopeConfig; 92 | $this->moduleManager = $moduleManager; 93 | } 94 | 95 | /** 96 | * Retrieve true if module is enabled 97 | * 98 | * @param string|null $storeId 99 | * @return bool 100 | */ 101 | public function isEnabled(?string $storeId = null): bool 102 | { 103 | return (bool)$this->getConfig(self::XML_PATH_EXTENSION_ENABLED, $storeId); 104 | } 105 | 106 | /** 107 | * Retrieve true if web container enabled 108 | * 109 | * @param string|null $storeId 110 | * @return bool 111 | */ 112 | public function isWebContainerEnabled(?string $storeId = null): bool 113 | { 114 | return (bool)$this->getConfig(self::XML_PATH_WEB_CONTAINER_ENABLED, $storeId); 115 | } 116 | 117 | /** 118 | * Retrieve GTM account ID 119 | * 120 | * @param string|null $storeId 121 | * @return string 122 | */ 123 | public function getAccountId(?string $storeId = null): string 124 | { 125 | return trim((string)$this->getConfig(self::XML_PATH_ACCOUNT_ID, $storeId)); 126 | } 127 | 128 | /** 129 | * Retrieve GTM container ID 130 | * 131 | * @param string|null $storeId 132 | * @return string 133 | */ 134 | public function getContainerId(?string $storeId = null): string 135 | { 136 | return trim((string)$this->getConfig(self::XML_PATH_CONTAINER_ID, $storeId)); 137 | } 138 | 139 | /** 140 | * @param string|null $storeId 141 | * @return string 142 | */ 143 | public function getPublicId(?string $storeId = null): string 144 | { 145 | if ('use_public_id' === $this->getInstallGtm()) { 146 | return trim((string)$this->getConfig(self::XML_PATH_WEB_PUBLIC_ID, $storeId)); 147 | } else { 148 | if ($gtmScript = $this->getGtmScript($storeId)) { 149 | $pattern = '/GTM-[A-Z0-9]+/'; 150 | $matches = []; 151 | if (preg_match($pattern, $gtmScript, $matches)) { 152 | if (isset($matches[0])) { 153 | return trim((string)$matches[0]); 154 | } 155 | } 156 | } 157 | } 158 | 159 | return ''; 160 | } 161 | 162 | /** 163 | * @param string|null $storeId 164 | * @return string 165 | */ 166 | public function getGtmScript(?string $storeId = null): string 167 | { 168 | return trim((string)$this->getConfig(self::XML_PATH_SCRIPT_CONTENT, $storeId)); 169 | } 170 | 171 | /** 172 | * @param string|null $storeId 173 | * @return string 174 | */ 175 | public function getGtmNoScript(?string $storeId = null): string 176 | { 177 | return trim((string)$this->getConfig(self::XML_PATH_NO_SCRIPT_CONTENT, $storeId)); 178 | } 179 | 180 | /** 181 | * Retrieve true if analytics enabled 182 | * 183 | * @param string|null $storeId 184 | * @return bool 185 | */ 186 | public function isAnalyticsEnabled(?string $storeId = null): bool 187 | { 188 | return (bool)$this->getConfig(self::XML_PATH_ANALYTICS_ENABLE, $storeId); 189 | } 190 | 191 | /** 192 | * Retrieve Google Analytics measurement ID 193 | * 194 | * @param string|null $storeId 195 | * @return string 196 | */ 197 | public function getMeasurementId(?string $storeId = null): string 198 | { 199 | return trim((string)$this->getConfig(self::XML_PATH_ANALYTICS_MEASUREMENT_ID, $storeId)); 200 | } 201 | 202 | /** 203 | * @param string|null $storeId 204 | * @return bool 205 | */ 206 | public function isPurchaseTaxEnabled(?string $storeId = null): bool 207 | { 208 | return (bool)$this->getConfig(self::XML_PATH_EVENTS_PURCHASE_TAX_ENABLED, $storeId); 209 | } 210 | 211 | /** 212 | * @param string|null $storeId 213 | * @return bool 214 | */ 215 | public function isPurchaseShippingEnabled(?string $storeId = null): bool 216 | { 217 | return (bool)$this->getConfig(self::XML_PATH_EVENTS_PURCHASE_SHIPPING_ENABLED, $storeId); 218 | } 219 | 220 | /** 221 | * Retrieve Magento product attribute 222 | * 223 | * @param string|null $storeId 224 | * @return string 225 | */ 226 | public function getProductAttribute(?string $storeId = null): string 227 | { 228 | return trim((string)$this->getConfig(self::XML_PATH_ATTRIBUTES_PRODUCT, $storeId)); 229 | } 230 | 231 | /** 232 | * Retrieve Magento product brand attribute 233 | * 234 | * @param string|null $storeId 235 | * @return string 236 | */ 237 | public function getBrandAttribute(?string $storeId = null): string 238 | { 239 | return trim((string)$this->getConfig(self::XML_PATH_ATTRIBUTES_BRAND, $storeId)); 240 | } 241 | 242 | /** 243 | * Retrieve true if protect customer data is enabled 244 | * 245 | * @param string|null $storeId 246 | * @return bool 247 | */ 248 | public function isProtectCustomerDataEnabled(?string $storeId = null): bool 249 | { 250 | return (bool)$this->getConfig(self::XML_PATH_PROTECT_CUSTOMER_DATA, $storeId); 251 | } 252 | 253 | /** 254 | * Retrieve true if gtm script should be loaded before customer provice consent. 255 | * 256 | * @param string|null $storeId 257 | * @return bool 258 | */ 259 | public function isLoadBeforeConsent(?string $storeId = null): bool 260 | { 261 | return $this->getConfig(self::XML_PATH_LOAD_BEFORE_CONSENT, $storeId) || 262 | !$this->isCookieRestrictionModeEnabled($storeId); 263 | } 264 | 265 | 266 | /** 267 | * Retrieve true if cookie restriction mode enabled 268 | * 269 | * @param string|null $storeId 270 | * @return bool 271 | */ 272 | public function isCookieRestrictionModeEnabled(?string $storeId = null) 273 | { 274 | return (bool)$this->getConfig(Custom::XML_PATH_WEB_COOKIE_RESTRICTION, $storeId); 275 | } 276 | 277 | /** 278 | * Retrieve true if mf cookie consent extension is enabled 279 | * 280 | * @param string|null $storeId 281 | * @return bool 282 | */ 283 | public function isMfCookieConsentExtensionEnabled(?string $storeId = null) 284 | { 285 | return $this->moduleManager->isEnabled('Magefan_CookieConsent') 286 | && $this->getConfig(self::XML_PATH_MF_COOKIE_CONSENT_EXTENSION_ENABLED, $storeId); 287 | } 288 | 289 | /* 290 | * Retrieve Magento product categories 291 | * 292 | * @param string|null $storeId 293 | * @return string 294 | */ 295 | public function getCategoriesAttribute(?string $storeId = null): string 296 | { 297 | return trim((string)$this->getConfig(self::XML_PATH_ATTRIBUTES_CATEGORIES, $storeId)); 298 | } 299 | 300 | /** 301 | * @param string|null $storeId 302 | * @return int 303 | */ 304 | public function getInstallGtm(?string $storeId = null): string 305 | { 306 | return trim((string)$this->getConfig(self::XML_PATH_INSTALL_GTM, $storeId)); 307 | } 308 | 309 | /** 310 | * Retrieve store config value 311 | * 312 | * @param string $path 313 | * @param string|null $storeId 314 | * @return mixed 315 | */ 316 | public function getConfig(string $path, ?string $storeId = null) 317 | { 318 | return $this->scopeConfig->getValue($path, ScopeInterface::SCOPE_STORE, $storeId); 319 | } 320 | 321 | /** 322 | * Retrieve true if speed optimization is enabled 323 | * 324 | * @param string|null $storeId 325 | * @return bool 326 | */ 327 | public function isSpeedOptimizationEnabled(?string $storeId = null): bool 328 | { 329 | return (bool)$this->getConfig(self::XML_PATH_SPEED_OPTIMIZATION_ENABLED, $storeId); 330 | } 331 | 332 | /** 333 | * @param string|null $storeId 334 | * @return bool 335 | */ 336 | public function isThirdPartyGaEnabled(?string $storeId = null): bool 337 | { 338 | return (bool)$this->getConfig(self::XML_PATH_THIRD_PARTY_GA, $storeId); 339 | } 340 | 341 | /** 342 | * @param string|null $storeId 343 | * @return bool 344 | */ 345 | public function isGoogleTagGatewayEnabled(?string $storeId = null): bool 346 | { 347 | if ($this->moduleManager->isEnabled('Magefan_GoogleTagManagerExtra')) { 348 | return (bool)$this->getConfig(self::XML_PATH_GOOGLE_TAG_GATEWAY, $storeId); 349 | } 350 | 351 | return false; 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /Model/Config/Source/BrandAttribute.php: -------------------------------------------------------------------------------- 1 | attributeCollectionFactory = $attributeCollectionFactory; 35 | } 36 | 37 | /** 38 | * Return array of options as value-label pairs 39 | * 40 | * @return array Format: array(array('value' => '', 'label' => '