├── Api
├── Cart
│ └── ContentInterface.php
├── InitiateCheckoutInterface.php
├── Order
│ └── ContentInterface.php
├── Product
│ └── ContentInterface.php
├── PurchaseInterface.php
└── ViewProductContentInterface.php
├── Block
├── AbstractPixel.php
├── Adminhtml
│ └── System
│ │ └── Config
│ │ └── Form
│ │ ├── EventList.php
│ │ ├── Info.php
│ │ ├── InfoConversionApi.php
│ │ ├── InfoPlan.php
│ │ └── ProtectCustomerData.php
├── Pixel.php
└── Pixel
│ ├── InitiateCheckout.php
│ ├── Other.php
│ ├── Purchase.php
│ └── ViewProductContent.php
├── LICENSE.txt
├── Model
├── AbstractPixel.php
├── Config.php
├── Config
│ └── Source
│ │ └── ProductAttribute.php
└── Pixel
│ ├── Cart
│ └── Content.php
│ ├── InitiateCheckout.php
│ ├── Order
│ └── Content.php
│ ├── Product
│ └── Content.php
│ ├── Purchase.php
│ └── ViewProductContent.php
├── README.md
├── composer.json
├── etc
├── acl.xml
├── adminhtml
│ └── system.xml
├── config.xml
├── csp_whitelist.xml
├── di.xml
└── module.xml
├── registration.php
└── view
├── adminhtml
└── templates
│ └── system
│ └── config
│ └── event
│ └── list.phtml
└── frontend
├── layout
├── catalog_product_view.xml
├── checkout_index_index.xml
├── checkout_onepage_success.xml
├── default.xml
└── redsys_checkout_success.xml
└── templates
└── pixel.phtml
/Api/Cart/ContentInterface.php:
--------------------------------------------------------------------------------
1 | config = $config;
51 | $this->json = $json;
52 | $this->mfSecureRenderer = $mfSecureRenderer ?: \Magento\Framework\App\ObjectManager::getInstance()
53 | ->get(SecureHtmlRendererInterface::class);
54 | parent::__construct($context, $data);
55 | }
56 |
57 | /**
58 | * Get FB Pixel params
59 | *
60 | * @return array
61 | */
62 | abstract protected function getParameters(): array;
63 |
64 | /**
65 | * Get event name
66 | *
67 | * @return string
68 | */
69 | abstract protected function getEventName(): string;
70 |
71 | /**
72 | * Init FB pixel
73 | *
74 | * @return string
75 | */
76 | protected function _toHtml(): string
77 | {
78 | if ($this->config->isEnabled() && $this->config->getFbPixelId()) {
79 | $parameters = $this->getParameters();
80 | $eventName = $this->getEventName();
81 | if ($parameters && $eventName) {
82 | $script = '
83 | fbq("' . $this->getTrackMethod() . '", '
84 | . $this->json->serialize($eventName) . ', '
85 | . $this->json->serialize($parameters) . ', '
86 | . '{ "eventID": "' . $eventName . '" + "." + Math.floor(Math.random() * 1000000) + "." + Date.now() }'
87 | . ');
88 | ';
89 |
90 | return $this->mfSecureRenderer->renderTag('script', ['style' => 'display:none'], $script, false);
91 | }
92 | }
93 |
94 | return '';
95 | }
96 |
97 | /**
98 | * @return string
99 | */
100 | protected function getTrackMethod(): string
101 | {
102 | return "track";
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/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 FB Pixel Plus is enabled
81 | *
82 | * @return bool
83 | */
84 | public function isPlusEnabled()
85 | {
86 | return (bool)$this->moduleManager->isEnabled('Magefan_FacebookPixelPlus');
87 | }
88 |
89 | /**
90 | * Retrieve true if FB Pixel Extra is enabled
91 | *
92 | * @return bool
93 | */
94 | public function isExtraEnabled()
95 | {
96 | return (bool)$this->moduleManager->isEnabled('Magefan_FacebookPixelExtra');
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Block/Adminhtml/System/Config/Form/Info.php:
--------------------------------------------------------------------------------
1 | Extra plans only.';
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Block/Adminhtml/System/Config/Form/InfoPlan.php:
--------------------------------------------------------------------------------
1 | getModuleVersion->execute($this->getModuleName() . $this->getMinPlan())) {
37 | return '';
38 | }
39 |
40 | $html = '';
41 | $html .= '
';
42 | $html .= $this->getText() . '
Read more.';
43 | $html .= '
';
44 |
45 | $script = '
46 | require(["jquery", "Magento_Ui/js/modal/alert", "domReady!"], function($, alert){
47 | setInterval(function(){
48 | var $plusSection = $("#' . $this->getSectionId() . '-state").parent(".section-config");
49 | $plusSection.find(".use-default").css("visibility", "hidden");
50 | $plusSection.find("input,select").each(function(){
51 | $(this).attr("readonly", "readonly");
52 | $(this).removeAttr("disabled");
53 | if ($(this).data("fpdisabled")) return;
54 | $(this).data("fpdisabled", 1);
55 | $(this).click(function(){
56 | alert({
57 | title: "You cannot change this option.",
58 | content: "' .
59 | (
60 | ($this->getMinPlan() == 'Extra')
61 | ? 'This option is available in Extra plan only.'
62 | : 'This option is available in Plus or Extra plans only.'
63 | )
64 | . '",
65 | buttons: [{
66 | text: "Upgrade Plan Now",
67 | class: "action primary accept",
68 | click: function () {
69 | window.open("https://magefan.com/magento-2-google-tag-manager/pricing?utm_source=gtm_config&utm_medium=link&utm_campaign=regular");
70 | }
71 | }]
72 | });
73 | });
74 | });
75 | }, 1000);
76 | });
77 | ';
78 |
79 | $html .= $this->mfSecureRenderer->renderTag('script', [], $script, false);
80 |
81 | return $html;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Block/Adminhtml/System/Config/Form/ProtectCustomerData.php:
--------------------------------------------------------------------------------
1 | getUrl('*/*/*/section/web');
25 | $comment = 'When enabled, data won\'t be sent to Facebook, until the customer provides consent.'; /*
26 | Note, that this option will work only when Cookie Restriction Mode at
27 | Stores > Configuration > General > Web > Default Cookie Settings
28 | is enabled.'; */
29 |
30 | $element->setComment($comment);
31 | return parent::render($element);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Block/Pixel.php:
--------------------------------------------------------------------------------
1 | config = $config;
35 | parent::__construct($context, $data);
36 | }
37 |
38 | /**
39 | * Get FB pixel ID
40 | *
41 | * @return string
42 | */
43 | public function getFbPixelId(): string
44 | {
45 | return $this->config->getFbPixelId();
46 | }
47 |
48 | /**
49 | * Check if protect customer data is enabled
50 | *
51 | * @return bool
52 | */
53 | public function isProtectCustomerDataEnabled(): bool
54 | {
55 | return $this->config->isProtectCustomerDataEnabled();
56 | }
57 |
58 | /**
59 | * Retrieve true if speed optimization is enabled
60 | *
61 | * @return bool
62 | */
63 | public function isSpeedOptimizationEnabled(): bool
64 | {
65 | return (bool)$this->config->isSpeedOptimizationEnabled();
66 | }
67 |
68 | /**
69 | * Get current website ID
70 | *
71 | * @return int
72 | * @throws NoSuchEntityException
73 | */
74 | public function getWebsiteId(): int
75 | {
76 | return (int)$this->_storeManager->getStore()->getWebsiteId();
77 | }
78 |
79 | /**
80 | * Init FB pixel
81 | *
82 | * @return string
83 | */
84 | protected function _toHtml(): string
85 | {
86 | if ($this->config->isEnabled()) {
87 | return parent::_toHtml();
88 | }
89 |
90 | return '';
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Block/Pixel/InitiateCheckout.php:
--------------------------------------------------------------------------------
1 | checkoutSession = $checkoutSession;
53 | $this->initiateCheckout = $initiateCheckout;
54 | parent::__construct($context, $config, $json, $data);
55 | }
56 |
57 | /**
58 | * Get FB Pixel params
59 | *
60 | * @return array
61 | * @throws NoSuchEntityException
62 | * @throws LocalizedException
63 | */
64 | protected function getParameters(): array
65 | {
66 | $quote = $this->checkoutSession->getQuote();
67 | return $this->initiateCheckout->get($quote);
68 | }
69 |
70 | /**
71 | * Get event name
72 | *
73 | * @return string
74 | */
75 | protected function getEventName(): string
76 | {
77 | return self::INITIATE_CHECKOUT;
78 | }
79 |
80 | /**
81 | * @inheritDoc
82 | */
83 | protected function _toHtml(): string
84 | {
85 | $html = parent::_toHtml();
86 |
87 | $script = '
88 | window.mfFbPixelCheckout = ' . json_encode($this->getParameters()) . ';
89 | ';
90 |
91 | $html .= $this->mfSecureRenderer->renderTag('script', [], $script, false);
92 |
93 | return $html;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Block/Pixel/Other.php:
--------------------------------------------------------------------------------
1 | checkoutSession = $checkoutSession;
52 | $this->purchase = $purchase;
53 | parent::__construct($context, $config, $json, $data);
54 | }
55 |
56 | /**
57 | * Get FB Pixel params
58 | *
59 | * @return array
60 | * @throws NoSuchEntityException
61 | */
62 | protected function getParameters(): array
63 | {
64 | $order = $this->checkoutSession->getLastRealOrder();
65 | return $this->purchase->get($order);
66 | }
67 |
68 | /**
69 | * Get event name
70 | *
71 | * @return string
72 | */
73 | protected function getEventName(): string
74 | {
75 | return self::PURCHASE;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Block/Pixel/ViewProductContent.php:
--------------------------------------------------------------------------------
1 | registry = $registry;
53 | $this->viewProductContent = $viewProductContent;
54 | parent::__construct($context, $config, $json, $data);
55 | }
56 |
57 | /**
58 | * Get FB Pixel params
59 | *
60 | * @return array
61 | * @throws NoSuchEntityException
62 | */
63 | protected function getParameters(): array
64 | {
65 | return $this->viewProductContent->get($this->getCurrentProduct());
66 | }
67 |
68 | /**
69 | * Get event name
70 | *
71 | * @return string
72 | */
73 | protected function getEventName(): string
74 | {
75 | return self::VIEW_CONTENT;
76 | }
77 |
78 | /**
79 | * Get current product
80 | *
81 | * @return Product
82 | */
83 | private function getCurrentProduct(): Product
84 | {
85 | return $this->registry->registry('current_product');
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Please visit Magefan.com for license details (https://magefan.com/end-user-license-agreement)
--------------------------------------------------------------------------------
/Model/AbstractPixel.php:
--------------------------------------------------------------------------------
1 | config = $config;
63 | $this->storeManager = $storeManager;
64 | $this->categoryRepository = $categoryRepository;
65 | $this->request = $request ?: ObjectManager::getInstance()->get(
66 | RequestInterface::class
67 | );
68 | $this->registry = $registry ?: ObjectManager::getInstance()->get(
69 | Registry::class
70 | );
71 | }
72 |
73 | /**
74 | * Get category names
75 | *
76 | * @param Product $product
77 | * @return array
78 | * @throws NoSuchEntityException
79 | */
80 | protected function getCategoryNames(Product $product): array
81 | {
82 | $result = [];
83 |
84 | if (!$this->config->getCategoriesAttribute()) {
85 | return $result;
86 | }
87 |
88 | if ($productCategory = $this->getCategoryByProduct($product)) {
89 | $categoryIds = $productCategory->getPathIds();
90 | foreach ($categoryIds as $categoryId) {
91 | $category = $this->categoryRepository->get($categoryId, $this->storeManager->getStore()->getId());
92 | if ($category->getLevel() < 2) {
93 | continue;
94 | }
95 |
96 | $result[] = $category->getName();
97 | }
98 | }
99 |
100 | return $result;
101 | }
102 |
103 | /**
104 | * Get product category
105 | *
106 | * @param Product $product
107 | * @return CategoryInterface|null
108 | * @throws NoSuchEntityException
109 | */
110 | private function getCategoryByProduct(Product $product): ?CategoryInterface
111 | {
112 | if ('catalog_category_product' == $this->request->getFullActionName()) {
113 | if ($category = $this->registry->registry('current_category')) {
114 | return $category;
115 | }
116 | }
117 |
118 | $productCategory = null;
119 | $categoryIds = $product->getCategoryIds();
120 |
121 | if ($categoryIds) {
122 | $level = -1;
123 | $store = $this->storeManager->getStore();
124 | $rootCategoryId = $store->getRootCategoryId();
125 |
126 | foreach ($categoryIds as $categoryId) {
127 | try {
128 | $category = $this->categoryRepository->get($categoryId, $store->getId());
129 | if ($category->getIsActive()
130 | && $category->getLevel() > $level
131 | && in_array($rootCategoryId, $category->getPathIds())
132 | ) {
133 | $level = $category->getLevel();
134 | $productCategory = $category;
135 | }
136 | } catch (Exception $e) { // phpcs:ignore
137 | /* Do nothing */
138 | }
139 | }
140 | }
141 |
142 | return $productCategory;
143 | }
144 |
145 | /**
146 | * Get current currency code
147 | *
148 | * @return string
149 | * @throws NoSuchEntityException
150 | */
151 | protected function getCurrentCurrencyCode(): string
152 | {
153 | return $this->storeManager->getStore()->getCurrentCurrencyCode();
154 | }
155 |
156 | /**
157 | * Format price
158 | *
159 | * @param float $price
160 | * @return float
161 | */
162 | protected function formatPrice(float $price): float
163 | {
164 | return (float)number_format($price, 2, '.', '');
165 | }
166 |
167 | /**
168 | * Get product price
169 | *
170 | * @param Product $product
171 | * @return float
172 | */
173 | protected function getPrice(Product $product): float
174 | {
175 | $priceInfo = $product->getPriceInfo()->getPrice('final_price')->getAmount();
176 | $price = $priceInfo->getValue();
177 | return $this->formatPrice($price);
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/Model/Config.php:
--------------------------------------------------------------------------------
1 | scopeConfig = $scopeConfig;
53 | }
54 |
55 | /**
56 | * Retrieve true if module is enabled
57 | *
58 | * @param string|null $storeId
59 | * @return bool
60 | */
61 | public function isEnabled(?string $storeId = null): bool
62 | {
63 | return (bool)$this->getConfig(self::XML_PATH_EXTENSION_ENABLED, $storeId);
64 | }
65 |
66 | /**
67 | * Retrieve GTM account ID
68 | *
69 | * @param string|null $storeId
70 | * @return string
71 | */
72 | public function getFbPixelId(?string $storeId = null): string
73 | {
74 | return trim((string)$this->getConfig(self::XML_PATH_FB_PIXEL_ID, $storeId));
75 | }
76 |
77 | /**
78 | * Retrieve Magento product attribute
79 | *
80 | * @param string|null $storeId
81 | * @return string
82 | */
83 | public function getProductAttribute(?string $storeId = null): string
84 | {
85 | return trim((string)$this->getConfig(self::XML_PATH_ATTRIBUTES_PRODUCT, $storeId));
86 | }
87 |
88 | /*
89 | * Retrieve Magento product categories
90 | *
91 | * @param string|null $storeId
92 | * @return string
93 | */
94 | public function getCategoriesAttribute(?string $storeId = null): string
95 | {
96 | return trim((string)$this->getConfig(self::XML_PATH_ATTRIBUTES_CATEGORIES, $storeId));
97 | }
98 |
99 | /**
100 | * Retrieve true if speed optimization is enabled
101 | *
102 | * @param string|null $storeId
103 | * @return bool
104 | */
105 | public function isSpeedOptimizationEnabled(?string $storeId = null): bool
106 | {
107 | return (bool)$this->getConfig(self::XML_PATH_SPEED_OPTIMIZATION_ENABLED, $storeId);
108 | }
109 |
110 | /**
111 | * Retrieve true if protect customer data is enabled
112 | *
113 | * @param string|null $storeId
114 | * @return bool
115 | */
116 | public function isProtectCustomerDataEnabled(?string $storeId = null): bool
117 | {
118 | return (bool)$this->getConfig(self::XML_PATH_PROTECT_CUSTOMER_DATA, $storeId);
119 | }
120 |
121 | /**
122 | * Retrieve true if cookie restriction mode enabled
123 | *
124 | * @param string|null $storeId
125 | * @return bool
126 | */
127 | public function isCookieRestrictionModeEnabled(?string $storeId = null): bool
128 | {
129 | return (bool)$this->getConfig(Custom::XML_PATH_WEB_COOKIE_RESTRICTION, $storeId);
130 | }
131 |
132 | /**
133 | * Retrieve store config value
134 | *
135 | * @param string $path
136 | * @param string|null $storeId
137 | * @return mixed
138 | */
139 | public function getConfig(string $path, ?string $storeId = null)
140 | {
141 | return $this->scopeConfig->getValue($path, ScopeInterface::SCOPE_STORE, $storeId);
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/Model/Config/Source/ProductAttribute.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' => '