From 39411ae35d23d97bedff3b652d34e387dd8a6d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Ag=C3=BC=C3=AD?= Date: Thu, 23 Jan 2020 10:45:20 +0100 Subject: [PATCH 1/7] indents with spaces and beautifies code --- ganalytics.php | 1666 +++++++++++++++++++++++------------------------- 1 file changed, 809 insertions(+), 857 deletions(-) diff --git a/ganalytics.php b/ganalytics.php index 3470950..6209db8 100755 --- a/ganalytics.php +++ b/ganalytics.php @@ -18,870 +18,822 @@ * versions in the future. If you wish to customize PrestaShop for your * needs please refer to http://www.prestashop.com for more information. * - * @author PrestaShop SA - * @copyright 2007-2017 PrestaShop SA - * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * @author PrestaShop SA + * @copyright 2007-2017 PrestaShop SA + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ if (!defined('_PS_VERSION_')) - exit; + exit; class Ganalytics extends Module { - protected $js_state = 0; - protected $eligible = 0; - protected $filterable = 1; - protected static $products = array(); - protected $_debug = 0; - - public function __construct() - { - $this->name = 'ganalytics'; - $this->tab = 'analytics_stats'; - $this->version = '2.4.0'; - $this->author = 'PrestaShop'; - $this->module_key = 'fd2aaefea84ac1bb512e6f1878d990b8'; - $this->bootstrap = true; - - parent::__construct(); - - $this->displayName = $this->l('Google Analytics'); - $this->description = $this->l('Gain clear insights into important metrics about your customers, using Google Analytics'); - $this->confirmUninstall = $this->l('Are you sure you want to uninstall Google Analytics? You will lose all the data related to this module.'); - /* Backward compatibility */ - if (version_compare(_PS_VERSION_, '1.5', '<')) - require(_PS_MODULE_DIR_.$this->name.'/backward_compatibility/backward.php'); - - $this->checkForUpdates(); - } - - public function install() - { - if (version_compare(_PS_VERSION_, '1.5', '>=') && Shop::isFeatureActive()) - Shop::setContext(Shop::CONTEXT_ALL); - - if (!parent::install() || !$this->installTab() || !$this->registerHook('header') || !$this->registerHook('adminOrder') - || !$this->registerHook('footer') || !$this->registerHook('home') - || !$this->registerHook('productfooter') || !$this->registerHook('orderConfirmation') - || !$this->registerHook('backOfficeHeader') || !$this->registerHook('processCarrier')) - return false; - - if (version_compare(_PS_VERSION_, '1.5', '>=') - && (!$this->registerHook('actionProductCancel') || !$this->registerHook('actionCartSave'))) - return false; - - Db::getInstance()->Execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'ganalytics`'); - - if (!Db::getInstance()->Execute(' - CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'ganalytics` ( - `id_google_analytics` int(11) NOT NULL AUTO_INCREMENT, - `id_order` int(11) NOT NULL, - `id_customer` int(10) NOT NULL, - `id_shop` int(11) NOT NULL, - `sent` tinyint(1) DEFAULT NULL, - `date_add` datetime DEFAULT NULL, - PRIMARY KEY (`id_google_analytics`), - KEY `id_order` (`id_order`), - KEY `sent` (`sent`) - ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8 AUTO_INCREMENT=1')) - return $this->uninstall(); - - return true; - } - - public function uninstall() - { - if (!$this->uninstallTab() || !parent::uninstall()) - return false; - - return Db::getInstance()->Execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'ganalytics`'); - } - - public function installTab() - { - if (version_compare(_PS_VERSION_, '1.5', '<')) - return true; - - $tab = new Tab(); - $tab->active = 0; - $tab->class_name = 'AdminGanalyticsAjax'; - $tab->name = array(); - foreach (Language::getLanguages(true) as $lang) - $tab->name[$lang['id_lang']] = 'Google Analytics Ajax'; - $tab->id_parent = -1; //(int)Tab::getIdFromClassName('AdminAdmin'); - $tab->module = $this->name; - return $tab->add(); - } - - public function uninstallTab() - { - if (version_compare(_PS_VERSION_, '1.5', '<')) - return true; - - $id_tab = (int)Tab::getIdFromClassName('AdminGanalyticsAjax'); - if ($id_tab) - { - $tab = new Tab($id_tab); - return $tab->delete(); - } - - return true; - } - - public function displayForm() - { - // Get default language - $default_lang = (int)Configuration::get('PS_LANG_DEFAULT'); - - $helper = new HelperForm(); - - // Module, token and currentIndex - $helper->module = $this; - $helper->name_controller = $this->name; - $helper->token = Tools::getAdminTokenLite('AdminModules'); - $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name; - - // Language - $helper->default_form_language = $default_lang; - $helper->allow_employee_form_lang = $default_lang; - - // Title and toolbar - $helper->title = $this->displayName; - $helper->show_toolbar = true; // false -> remove toolbar - $helper->toolbar_scroll = true; // yes - > Toolbar is always visible on the top of the screen. - $helper->submit_action = 'submit'.$this->name; - $helper->toolbar_btn = array( - 'save' => - array( - 'desc' => $this->l('Save'), - 'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name. - '&token='.Tools::getAdminTokenLite('AdminModules'), - ), - 'back' => array( - 'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'), - 'desc' => $this->l('Back to list') - ) - ); - - $fields_form = array(); - // Init Fields form array - $fields_form[0]['form'] = array( - 'legend' => array( - 'title' => $this->l('Settings'), - ), - 'input' => array( - array( - 'type' => 'text', - 'label' => $this->l('Google Analytics Tracking ID'), - 'name' => 'GA_ACCOUNT_ID', - 'size' => 20, - 'required' => true, - 'hint' => $this->l('This information is available in your Google Analytics account') - ), - array( - 'type' => 'radio', - 'label' => $this->l('Enable User ID tracking'), - 'name' => 'GA_USERID_ENABLED', - 'hint' => $this->l('The User ID is set at the property level. To find a property, click Admin, then select an account and a property. From the Property column, click Tracking Info then User ID'), - 'values' => array( - array( - 'id' => 'ga_userid_enabled', - 'value' => 1, - 'label' => $this->l('Enabled') - ), - array( - 'id' => 'ga_userid_disabled', - 'value' => 0, - 'label' => $this->l('Disabled') - ), - ), - ), - ), - 'submit' => array( - 'title' => $this->l('Save'), - ) - ); - - // Load current value - $helper->fields_value['GA_ACCOUNT_ID'] = Configuration::get('GA_ACCOUNT_ID'); - $helper->fields_value['GA_USERID_ENABLED'] = Configuration::get('GA_USERID_ENABLED'); - - return $helper->generateForm($fields_form); - } - - /** - * back office module configuration page content - */ - public function getContent() - { - $output = ''; - if (Tools::isSubmit('submit'.$this->name)) - { - $ga_account_id = Tools::getValue('GA_ACCOUNT_ID'); - if (!empty($ga_account_id)) - { - Configuration::updateValue('GA_ACCOUNT_ID', $ga_account_id); - Configuration::updateValue('GANALYTICS_CONFIGURATION_OK', true); - $output .= $this->displayConfirmation($this->l('Account ID updated successfully')); - } - $ga_userid_enabled = Tools::getValue('GA_USERID_ENABLED'); - if (null !== $ga_userid_enabled) - { - Configuration::updateValue('GA_USERID_ENABLED', (bool)$ga_userid_enabled); - $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully')); - } - } - - if (version_compare(_PS_VERSION_, '1.5', '>=')) - $output .= $this->displayForm(); - else - { - $this->context->smarty->assign(array( - 'account_id' => Configuration::get('GA_ACCOUNT_ID'), - )); - $output .= $this->display(__FILE__, 'views/templates/admin/form-ps14.tpl'); - } - - return $this->display(__FILE__, 'views/templates/admin/configuration.tpl').$output; - } - - protected function _getGoogleAnalyticsTag($back_office = false) - { - $user_id = null; - if (Configuration::get('GA_USERID_ENABLED') && - $this->context->customer && $this->context->customer->isLogged() - ){ - $user_id = (int)$this->context->customer->id; - } - - return ' - '; - } - - public function hookHeader() - { - if (Configuration::get('GA_ACCOUNT_ID')) - { - $this->context->controller->addJs($this->_path.'views/js/GoogleAnalyticActionLib.js'); - - return $this->_getGoogleAnalyticsTag(); - } - } - - /** - * Return a detailed transaction for Google Analytics - */ - public function wrapOrder($id_order) - { - $order = new Order((int)$id_order); - - if (Validate::isLoadedObject($order)) - return array( - 'id' => $id_order, - 'affiliation' => Shop::isFeatureActive() ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), - 'revenue' => $order->total_paid, - 'shipping' => $order->total_shipping, - 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, - 'url' => $this->context->link->getAdminLink('AdminGanalyticsAjax'), - 'customer' => $order->id_customer); - } - - /** - * To track transactions - */ - public function hookOrderConfirmation($params) - { - $order = $params['objOrder']; - if (Validate::isLoadedObject($order) && $order->getCurrentState() != (int)Configuration::get('PS_OS_ERROR')) - { - $ga_order_sent = Db::getInstance()->getValue('SELECT id_order FROM `'._DB_PREFIX_.'ganalytics` WHERE id_order = '.(int)$order->id); - if ($ga_order_sent === false) - { - Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'ganalytics` (id_order, id_shop, sent, date_add) VALUES ('.(int)$order->id.', '.(int)$this->context->shop->id.', 0, NOW())'); - if ($order->id_customer == $this->context->cookie->id_customer) - { - $order_products = array(); - $cart = new Cart($order->id_cart); - foreach ($cart->getProducts() as $order_product) - $order_products[] = $this->wrapProduct($order_product, array(), 0, true); - - $ga_scripts = 'MBG.addCheckoutOption(3,\''.$order->payment.'\');'; - - $transaction = array( - 'id' => $order->id, - 'affiliation' => (version_compare(_PS_VERSION_, '1.5', '>=') && Shop::isFeatureActive()) ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), - 'revenue' => $order->total_paid, - 'shipping' => $order->total_shipping, - 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, - 'url' => $this->context->link->getModuleLink('ganalytics', 'ajax', array(), true), - 'customer' => $order->id_customer); - $ga_scripts .= $this->addTransaction($order_products, $transaction); - - $this->js_state = 1; - return $this->_runJs($ga_scripts); - } - } - } - } - - /** - * hook footer to load JS script for standards actions such as product clicks - */ - public function hookFooter() - { - $ga_scripts = ''; - $this->js_state = 0; - - if (isset($this->context->cookie->ga_cart)) - { - $this->filterable = 0; - - $gacarts = unserialize($this->context->cookie->ga_cart); - foreach ($gacarts as $gacart) - { - if ($gacart['quantity'] > 0) - $ga_scripts .= 'MBG.addToCart('.Tools::jsonEncode($gacart).');'; - elseif ($gacart['quantity'] < 0) - { - $gacart['quantity'] = abs($gacart['quantity']); - $ga_scripts .= 'MBG.removeFromCart('.Tools::jsonEncode($gacart).');'; - } - } - unset($this->context->cookie->ga_cart); - } - - $controller_name = Tools::getValue('controller'); - $products = $this->wrapProducts($this->context->smarty->getTemplateVars('products'), array(), true); - - if ($controller_name == 'order' || $controller_name == 'orderopc') - { - $this->js_state = 1; - $this->eligible = 1; - $step = Tools::getValue('step'); - if (empty($step)) - $step = 0; - $ga_scripts .= $this->addProductFromCheckout($products, $step); - $ga_scripts .= 'MBG.addCheckout(\''.(int)$step.'\');'; - } - - if (version_compare(_PS_VERSION_, '1.5', '<')) - { - if ($controller_name == 'orderconfirmation') - $this->eligible = 1; - } - else - { - $confirmation_hook_id = (int)Hook::getIdByName('orderConfirmation'); - if (isset(Hook::$executed_hooks[$confirmation_hook_id])) - $this->eligible = 1; - } - - if (isset($products) && count($products) && $controller_name != 'index') - { - if ($this->eligible == 0) - $ga_scripts .= $this->addProductImpression($products); - $ga_scripts .= $this->addProductClick($products); - } - - return $this->_runJs($ga_scripts); - } - - protected function filter($ga_scripts) - { - if ($this->filterable = 1) - return implode(';', array_unique(explode(';', $ga_scripts))); - - return $ga_scripts; - } - - /** - * hook home to display generate the product list associated to home featured, news products and best sellers Modules - */ - public function hookHome() - { - $ga_scripts = ''; - - // Home featured products - if ($this->isModuleEnabled('homefeatured')) - { - $category = new Category($this->context->shop->getCategory(), $this->context->language->id); - $home_featured_products = $this->wrapProducts($category->getProducts((int)Context::getContext()->language->id, 1, - (Configuration::get('HOME_FEATURED_NBR') ? (int)Configuration::get('HOME_FEATURED_NBR') : 8), 'position'), array(), true); - $ga_scripts .= $this->addProductImpression($home_featured_products).$this->addProductClick($home_featured_products); - } - - // New products - if ($this->isModuleEnabled('blocknewproducts') && (Configuration::get('PS_NB_DAYS_NEW_PRODUCT') - || Configuration::get('PS_BLOCK_NEWPRODUCTS_DISPLAY'))) - { - $new_products = Product::getNewProducts((int)$this->context->language->id, 0, (int)Configuration::get('NEW_PRODUCTS_NBR')); - $new_products_list = $this->wrapProducts($new_products, array(), true); - $ga_scripts .= $this->addProductImpression($new_products_list).$this->addProductClick($new_products_list); - } - - // Best Sellers - if ($this->isModuleEnabled('blockbestsellers') && (!Configuration::get('PS_CATALOG_MODE') - || Configuration::get('PS_BLOCK_BESTSELLERS_DISPLAY'))) - { - $ga_homebestsell_product_list = $this->wrapProducts(ProductSale::getBestSalesLight((int)$this->context->language->id, 0, 8), array(), true); - $ga_scripts .= $this->addProductImpression($ga_homebestsell_product_list).$this->addProductClick($ga_homebestsell_product_list); - } - - $this->js_state = 1; - return $this->_runJs($this->filter($ga_scripts)); - } - - /** - * hook home to display generate the product list associated to home featured, news products and best sellers Modules - */ - public function isModuleEnabled($name) - { - if (version_compare(_PS_VERSION_, '1.5', '>=')) - if(Module::isEnabled($name)) - { - $module = Module::getInstanceByName($name); - return $module->isRegisteredInHook('home'); - } - else - return false; - else - { - $module = Module::getInstanceByName($name); - return ($module && $module->active === true); - } - } - - /** - * wrap products to provide a standard products information for google analytics script - */ - public function wrapProducts($products, $extras = array(), $full = false) - { - $result_products = array(); - if (!is_array($products)) - return; - - $currency = new Currency($this->context->currency->id); - $usetax = (Product::getTaxCalculationMethod((int)$this->context->customer->id) != PS_TAX_EXC); - - if (count($products) > 20) - $full = false; - else - $full = true; - - foreach ($products as $index => $product) - { - if ($product instanceof Product) - $product = (array)$product; - - if (!isset($product['price'])) - $product['price'] = (float)Tools::displayPrice(Product::getPriceStatic((int)$product['id_product'], $usetax), $currency); - $result_products[] = $this->wrapProduct($product, $extras, $index, $full); - } - - return $result_products; - } - - /** - * wrap product to provide a standard product information for google analytics script - */ - public function wrapProduct($product, $extras, $index = 0, $full = false) - { - $ga_product = ''; - - $variant = null; - if (isset($product['attributes_small'])) - $variant = $product['attributes_small']; - elseif (isset($extras['attributes_small'])) - $variant = $extras['attributes_small']; - - $product_qty = 1; - if (isset($extras['qty'])) - $product_qty = $extras['qty']; - elseif (isset($product['cart_quantity'])) - $product_qty = $product['cart_quantity']; - - $product_id = 0; - if (!empty($product['id_product'])) - $product_id = $product['id_product']; - else if (!empty($product['id'])) - $product_id = $product['id']; - - if (!empty($product['id_product_attribute'])) - $product_id .= '-'. $product['id_product_attribute']; - - $product_type = 'typical'; - if (isset($product['pack']) && $product['pack'] == 1) - $product_type = 'pack'; - elseif (isset($product['virtual']) && $product['virtual'] == 1) - $product_type = 'virtual'; - - if ($full) - { - $ga_product = array( - 'id' => $product_id, - 'name' => Tools::str2url($product['name']), - 'category' => Tools::str2url($product['category']), - 'brand' => isset($product['manufacturer_name']) ? Tools::str2url($product['manufacturer_name']) : '', - 'variant' => Tools::str2url($variant), - 'type' => $product_type, - 'position' => $index ? $index : '0', - 'quantity' => $product_qty, - 'list' => Tools::getValue('controller'), - 'url' => isset($product['link']) ? urlencode($product['link']) : '', - 'price' => number_format($product['price'], 2, '.', '') - ); - } - else - { - $ga_product = array( - 'id' => $product_id, - 'name' => Tools::str2url($product['name']) - ); - } - return $ga_product; - } - - /** - * add order transaction - */ - public function addTransaction($products, $order) - { - if (!is_array($products)) - return; - - $js = ''; - foreach ($products as $product) - $js .= 'MBG.add('.Tools::jsonEncode($product).');'; - - return $js.'MBG.addTransaction('.Tools::jsonEncode($order).');'; - } - - /** - * add product impression js and product click js - */ - public function addProductImpression($products) - { - if (!is_array($products)) - return; - - $js = ''; - foreach ($products as $product) - $js .= 'MBG.add('.Tools::jsonEncode($product).",'',true);"; - - return $js; - } - - public function addProductClick($products) - { - if (!is_array($products)) - return; - - $js = ''; - foreach ($products as $product) - $js .= 'MBG.addProductClick('.Tools::jsonEncode($product).');'; - - return $js; - } - - public function addProductClickByHttpReferal($products) - { - if (!is_array($products)) - return; - - $js = ''; - foreach ($products as $product) - $js .= 'MBG.addProductClickByHttpReferal('.Tools::jsonEncode($product).');'; - - return $js; - } - - /** - * Add product checkout info - */ - public function addProductFromCheckout($products) - { - if (!is_array($products)) - return; - - $js = ''; - foreach ($products as $product) - $js .= 'MBG.add('.Tools::jsonEncode($product).');'; - - return $js; - } - - /** - * hook product page footer to load JS for product details view - */ - public function hookProductFooter($params) - { - $controller_name = Tools::getValue('controller'); - if ($controller_name == 'product') - { - // Add product view - $ga_product = $this->wrapProduct((array)$params['product'], null, 0, true); - $js = 'MBG.addProductDetailView('.Tools::jsonEncode($ga_product).');'; - - if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) > 0) - $js .= $this->addProductClickByHttpReferal(array($ga_product)); - - $this->js_state = 1; - return $this->_runJs($js); - } - } - - /** - * Generate Google Analytics js - */ - protected function _runJs($js_code, $backoffice = 0) - { - if (Configuration::get('GA_ACCOUNT_ID')) - { - $runjs_code = ''; - - if (!empty($js_code)){ - if($backoffice){ - $js_code .= 'MBG.setCampaign(\'backoffice-orders\',\'backoffice\',\'cms\');'; - } - - $runjs_code .= ' - '; - } - - if (($this->js_state) != 1 && ($backoffice == 0)) - $runjs_code .= ' - '; - - return $runjs_code; - } - } - - /** - * Hook admin order to send transactions and refunds details - */ - public function hookAdminOrder() - { - echo $this->_runJs($this->context->cookie->ga_admin_refund, 1); - unset($this->context->cookie->ga_admin_refund); - } - - /** - * admin office header to add google analytics js - */ - public function hookBackOfficeHeader() - { - $js = ''; - if (strcmp(Tools::getValue('configure'), $this->name) === 0) - { - if (version_compare(_PS_VERSION_, '1.5', '>') == true) - { - $this->context->controller->addCSS($this->_path.'views/css/ganalytics.css'); - if (version_compare(_PS_VERSION_, '1.6', '<') == true) - $this->context->controller->addCSS($this->_path.'views/css/ganalytics-nobootstrap.css'); - } - else - { - $js .= '\ - '; - } - } - - $ga_account_id = Configuration::get('GA_ACCOUNT_ID'); - - if (!empty($ga_account_id) && $this->active) - { - if (version_compare(_PS_VERSION_, '1.5', '>=') == true) - $this->context->controller->addJs($this->_path.'views/js/GoogleAnalyticActionLib.js'); - else - $js .= ''; - - $this->context->smarty->assign('GA_ACCOUNT_ID', $ga_account_id); - - $ga_scripts = ''; - if ($this->context->controller->controller_name == 'AdminOrders') - { - if (Tools::getValue('id_order')) - { - $order = new Order((int)Tools::getValue('id_order')); - if (Validate::isLoadedObject($order) && strtotime('+1 day', strtotime($order->date_add)) > time()) { - $ga_order_sent = Db::getInstance()->getValue('SELECT id_order FROM `'._DB_PREFIX_.'ganalytics` WHERE id_order = '.(int)Tools::getValue('id_order')); - if ($ga_order_sent === false) - Db::getInstance()->Execute('INSERT IGNORE INTO `'._DB_PREFIX_.'ganalytics` (id_order, id_shop, sent, date_add) VALUES ('.(int)Tools::getValue('id_order').', '.(int)$this->context->shop->id.', 0, NOW())'); - } - } - else - { - $ga_order_records = Db::getInstance()->ExecuteS('SELECT * FROM `'._DB_PREFIX_.'ganalytics` WHERE sent = 0 AND id_shop = \''.(int)$this->context->shop->id.'\' AND DATE_ADD(date_add, INTERVAL 30 minute) < NOW()'); - - if ($ga_order_records) - foreach ($ga_order_records as $row) - { - $transaction = $this->wrapOrder($row['id_order']); - if (!empty($transaction)) - { - Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'ganalytics` SET date_add = NOW(), sent = 1 WHERE id_order = '.(int)$row['id_order'].' AND id_shop = \''.(int)$this->context->shop->id.'\''); - $transaction = Tools::jsonEncode($transaction); - $ga_scripts .= 'MBG.addTransaction('.$transaction.');'; - } - } - - } - } - return $js.$this->_getGoogleAnalyticsTag(true).$this->_runJs($ga_scripts, 1); - } - else return $js; - } - - /** - * Hook admin office header to add google analytics js - */ - public function hookActionProductCancel($params) - { - $qty_refunded = Tools::getValue('cancelQuantity'); - $ga_scripts = ''; - foreach ($qty_refunded as $orderdetail_id => $qty) - { - // Display GA refund product - $order_detail = new OrderDetail($orderdetail_id); - $ga_scripts .= 'MBG.add('.Tools::jsonEncode( - array( - 'id' => empty($order_detail->product_attribute_id)?$order_detail->product_id:$order_detail->product_id.'-'.$order_detail->product_attribute_id, - 'quantity' => $qty) - ).');'; - } - $this->context->cookie->ga_admin_refund = $ga_scripts.'MBG.refundByProduct('.Tools::jsonEncode(array('id' => $params['order']->id)).');'; - } - - /** - * hook save cart event to implement addtocart and remove from cart functionality - */ - public function hookActionCartSave() - { - if (!isset($this->context->cart)) - return; - - if (!Tools::getIsset('id_product')) - return; - - $cart = array( - 'controller' => Tools::getValue('controller'), - 'addAction' => Tools::getValue('add') ? 'add' : '', - 'removeAction' => Tools::getValue('delete') ? 'delete' : '', - 'extraAction' => Tools::getValue('op'), - 'qty' => (int)Tools::getValue('qty', 1) - ); - - $cart_products = $this->context->cart->getProducts(); - if (isset($cart_products) && count($cart_products)) - foreach ($cart_products as $cart_product) - if ($cart_product['id_product'] == Tools::getValue('id_product')) - $add_product = $cart_product; - - if ($cart['removeAction'] == 'delete') - { - $add_product_object = new Product((int)Tools::getValue('id_product'), true, (int)Configuration::get('PS_LANG_DEFAULT')); - if (Validate::isLoadedObject($add_product_object)) - { - $add_product['name'] = $add_product_object->name; - $add_product['manufacturer_name'] = $add_product_object->manufacturer_name; - $add_product['category'] = $add_product_object->category; - $add_product['reference'] = $add_product_object->reference; - $add_product['link_rewrite'] = $add_product_object->link_rewrite; - $add_product['link'] = $add_product_object->link_rewrite; - $add_product['price'] = $add_product_object->price; - $add_product['ean13'] = $add_product_object->ean13; - $add_product['id_product'] = Tools::getValue('id_product'); - $add_product['id_category_default'] = $add_product_object->id_category_default; - $add_product['out_of_stock'] = $add_product_object->out_of_stock; - $add_product = Product::getProductProperties((int)Configuration::get('PS_LANG_DEFAULT'), $add_product); - } - } - - if (isset($add_product) && !in_array((int)Tools::getValue('id_product'), self::$products)) - { - self::$products[] = (int)Tools::getValue('id_product'); - $ga_products = $this->wrapProduct($add_product, $cart, 0, true); - - if (array_key_exists('id_product_attribute', $ga_products) && $ga_products['id_product_attribute'] != '' && $ga_products['id_product_attribute'] != 0) - $id_product = $ga_products['id_product_attribute']; - else - $id_product = Tools::getValue('id_product'); - - if (isset($this->context->cookie->ga_cart)) - $gacart = unserialize($this->context->cookie->ga_cart); - else - $gacart = array(); - - if ($cart['removeAction'] == 'delete') - $ga_products['quantity'] = -1; - elseif ($cart['extraAction'] == 'down') - { - if (array_key_exists($id_product, $gacart)) - $ga_products['quantity'] = $gacart[$id_product]['quantity'] - $cart['qty']; - else - $ga_products['quantity'] = $cart['qty'] * -1; - } - elseif (Tools::getValue('step') <= 0) // Sometimes cartsave is called in checkout - { - if (array_key_exists($id_product, $gacart)) - $ga_products['quantity'] = $gacart[$id_product]['quantity'] + $cart['qty']; - } - - $gacart[$id_product] = $ga_products; - $this->context->cookie->ga_cart = serialize($gacart); - } - } - - public function hookProcessCarrier($params){ - if(isset($params['cart']->id_carrier)){ - $carrier_name = Db::getInstance()->getValue('SELECT name FROM `'._DB_PREFIX_.'carrier` WHERE id_carrier = '.(int)$params['cart']->id_carrier); - $this->context->cookie->ga_cart .= 'MBG.addCheckoutOption(2,\''.$carrier_name.'\');'; - } + protected $js_state = 0; + protected $eligible = 0; + protected $filterable = 1; + protected static $products = array(); + protected $_debug = 0; + + public function __construct() + { + $this->name = 'ganalytics'; + $this->tab = 'analytics_stats'; + $this->version = '2.4.0'; + $this->author = 'PrestaShop'; + $this->module_key = 'fd2aaefea84ac1bb512e6f1878d990b8'; + $this->bootstrap = true; + + parent::__construct(); + + $this->displayName = $this->l('Google Analytics'); + $this->description = $this->l('Gain clear insights into important metrics about your customers, using Google Analytics'); + $this->confirmUninstall = $this->l('Are you sure you want to uninstall Google Analytics? You will lose all the data related to this module.'); + /* Backward compatibility */ + if (version_compare(_PS_VERSION_, '1.5', '<')) + require(_PS_MODULE_DIR_ . $this->name . '/backward_compatibility/backward.php'); + + $this->checkForUpdates(); + } + + public function install() + { + if (version_compare(_PS_VERSION_, '1.5', '>=') && Shop::isFeatureActive()) + Shop::setContext(Shop::CONTEXT_ALL); + + if (!parent::install() || !$this->installTab() || !$this->registerHook('header') || !$this->registerHook('adminOrder') + || !$this->registerHook('footer') || !$this->registerHook('home') + || !$this->registerHook('productfooter') || !$this->registerHook('orderConfirmation') + || !$this->registerHook('backOfficeHeader') || !$this->registerHook('processCarrier')) + return false; + + if (version_compare(_PS_VERSION_, '1.5', '>=') + && (!$this->registerHook('actionProductCancel') || !$this->registerHook('actionCartSave'))) + return false; + + Db::getInstance()->Execute('DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'ganalytics`'); + + if (!Db::getInstance()->Execute(' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'ganalytics` ( + `id_google_analytics` int(11) NOT NULL AUTO_INCREMENT, + `id_order` int(11) NOT NULL, + `id_customer` int(10) NOT NULL, + `id_shop` int(11) NOT NULL, + `sent` tinyint(1) DEFAULT NULL, + `date_add` datetime DEFAULT NULL, + PRIMARY KEY (`id_google_analytics`), + KEY `id_order` (`id_order`), + KEY `sent` (`sent`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8 AUTO_INCREMENT=1')) + return $this->uninstall(); + + return true; + } + + public function uninstall() + { + if (!$this->uninstallTab() || !parent::uninstall()) + return false; + + return Db::getInstance()->Execute('DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'ganalytics`'); + } + + public function installTab() + { + if (version_compare(_PS_VERSION_, '1.5', '<')) + return true; + + $tab = new Tab(); + $tab->active = 0; + $tab->class_name = 'AdminGanalyticsAjax'; + $tab->name = array(); + foreach (Language::getLanguages(true) as $lang) + $tab->name[$lang['id_lang']] = 'Google Analytics Ajax'; + $tab->id_parent = -1; //(int)Tab::getIdFromClassName('AdminAdmin'); + $tab->module = $this->name; + return $tab->add(); + } + + public function uninstallTab() + { + if (version_compare(_PS_VERSION_, '1.5', '<')) + return true; + + $id_tab = (int)Tab::getIdFromClassName('AdminGanalyticsAjax'); + if ($id_tab) { + $tab = new Tab($id_tab); + return $tab->delete(); } - protected function checkForUpdates() - { - // Used by PrestaShop 1.3 & 1.4 - if (version_compare(_PS_VERSION_, '1.5', '<') && self::isInstalled($this->name)) - foreach (array('2.0.0', '2.0.4', '2.0.5', '2.0.6', '2.1.0') as $version) - { - $file = dirname(__FILE__).'/upgrade/Upgrade-'.$version.'.php'; - if (version_compare(Configuration::get('GANALYTICS'), $version, '<') && file_exists($file)) - { - include_once($file); - call_user_func('upgrade_module_'.str_replace('.', '_', $version), $this); - } - } - } - - protected function _debugLog($function, $log) - { - if (!$this->_debug) - return true; - - $myFile = _PS_MODULE_DIR_.$this->name.'/logs/analytics.log'; - $fh = fopen($myFile, 'a'); - fwrite($fh, date('F j, Y, g:i a').' '.$function."\n"); - fwrite($fh, print_r($log, true)."\n\n"); - fclose($fh); - } + return true; + } + + public function displayForm() + { + // Get default language + $default_lang = (int)Configuration::get('PS_LANG_DEFAULT'); + + $helper = new HelperForm(); + + // Module, token and currentIndex + $helper->module = $this; + $helper->name_controller = $this->name; + $helper->token = Tools::getAdminTokenLite('AdminModules'); + $helper->currentIndex = AdminController::$currentIndex . '&configure=' . $this->name; + + // Language + $helper->default_form_language = $default_lang; + $helper->allow_employee_form_lang = $default_lang; + + // Title and toolbar + $helper->title = $this->displayName; + $helper->show_toolbar = true; // false -> remove toolbar + $helper->toolbar_scroll = true; // yes - > Toolbar is always visible on the top of the screen. + $helper->submit_action = 'submit' . $this->name; + $helper->toolbar_btn = array( + 'save' => + array( + 'desc' => $this->l('Save'), + 'href' => AdminController::$currentIndex . '&configure=' . $this->name . '&save' . $this->name . + '&token=' . Tools::getAdminTokenLite('AdminModules'), + ), + 'back' => array( + 'href' => AdminController::$currentIndex . '&token=' . Tools::getAdminTokenLite('AdminModules'), + 'desc' => $this->l('Back to list') + ) + ); + + $fields_form = array(); + // Init Fields form array + $fields_form[0]['form'] = array( + 'legend' => array( + 'title' => $this->l('Settings'), + ), + 'input' => array( + array( + 'type' => 'text', + 'label' => $this->l('Google Analytics Tracking ID'), + 'name' => 'GA_ACCOUNT_ID', + 'size' => 20, + 'required' => true, + 'hint' => $this->l('This information is available in your Google Analytics account') + ), + array( + 'type' => 'radio', + 'label' => $this->l('Enable User ID tracking'), + 'name' => 'GA_USERID_ENABLED', + 'hint' => $this->l('The User ID is set at the property level. To find a property, click Admin, then select an account and a property. From the Property column, click Tracking Info then User ID'), + 'values' => array( + array( + 'id' => 'ga_userid_enabled', + 'value' => 1, + 'label' => $this->l('Enabled') + ), + array( + 'id' => 'ga_userid_disabled', + 'value' => 0, + 'label' => $this->l('Disabled') + ), + ), + ), + ), + 'submit' => array( + 'title' => $this->l('Save'), + ) + ); + + // Load current value + $helper->fields_value['GA_ACCOUNT_ID'] = Configuration::get('GA_ACCOUNT_ID'); + $helper->fields_value['GA_USERID_ENABLED'] = Configuration::get('GA_USERID_ENABLED'); + + return $helper->generateForm($fields_form); + } + + /** + * back office module configuration page content + */ + public function getContent() + { + $output = ''; + if (Tools::isSubmit('submit' . $this->name)) { + $ga_account_id = Tools::getValue('GA_ACCOUNT_ID'); + if (!empty($ga_account_id)) { + Configuration::updateValue('GA_ACCOUNT_ID', $ga_account_id); + Configuration::updateValue('GANALYTICS_CONFIGURATION_OK', true); + $output .= $this->displayConfirmation($this->l('Account ID updated successfully')); + } + $ga_userid_enabled = Tools::getValue('GA_USERID_ENABLED'); + if (null !== $ga_userid_enabled) { + Configuration::updateValue('GA_USERID_ENABLED', (bool)$ga_userid_enabled); + $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully')); + } + } + + if (version_compare(_PS_VERSION_, '1.5', '>=')) + $output .= $this->displayForm(); + else { + $this->context->smarty->assign(array( + 'account_id' => Configuration::get('GA_ACCOUNT_ID'), + )); + $output .= $this->display(__FILE__, 'views/templates/admin/form-ps14.tpl'); + } + + return $this->display(__FILE__, 'views/templates/admin/configuration.tpl') . $output; + } + + protected function _getGoogleAnalyticsTag($back_office = false) + { + $user_id = null; + if (Configuration::get('GA_USERID_ENABLED') && + $this->context->customer && $this->context->customer->isLogged() + ) { + $user_id = (int)$this->context->customer->id; + } + + return ' + '; + } + + public function hookHeader() + { + if (Configuration::get('GA_ACCOUNT_ID')) { + $this->context->controller->addJs($this->_path . 'views/js/GoogleAnalyticActionLib.js'); + + return $this->_getGoogleAnalyticsTag(); + } + } + + /** + * Return a detailed transaction for Google Analytics + */ + public function wrapOrder($id_order) + { + $order = new Order((int)$id_order); + + if (Validate::isLoadedObject($order)) + return array( + 'id' => $id_order, + 'affiliation' => Shop::isFeatureActive() ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), + 'revenue' => $order->total_paid, + 'shipping' => $order->total_shipping, + 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, + 'url' => $this->context->link->getAdminLink('AdminGanalyticsAjax'), + 'customer' => $order->id_customer); + } + + /** + * To track transactions + */ + public function hookOrderConfirmation($params) + { + $order = $params['objOrder']; + if (Validate::isLoadedObject($order) && $order->getCurrentState() != (int)Configuration::get('PS_OS_ERROR')) { + $ga_order_sent = Db::getInstance()->getValue('SELECT id_order FROM `' . _DB_PREFIX_ . 'ganalytics` WHERE id_order = ' . (int)$order->id); + if ($ga_order_sent === false) { + Db::getInstance()->Execute('INSERT INTO `' . _DB_PREFIX_ . 'ganalytics` (id_order, id_shop, sent, date_add) VALUES (' . (int)$order->id . ', ' . (int)$this->context->shop->id . ', 0, NOW())'); + if ($order->id_customer == $this->context->cookie->id_customer) { + $order_products = array(); + $cart = new Cart($order->id_cart); + foreach ($cart->getProducts() as $order_product) + $order_products[] = $this->wrapProduct($order_product, array(), 0, true); + + $ga_scripts = 'MBG.addCheckoutOption(3,\'' . $order->payment . '\');'; + + $transaction = array( + 'id' => $order->id, + 'affiliation' => (version_compare(_PS_VERSION_, '1.5', '>=') && Shop::isFeatureActive()) ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), + 'revenue' => $order->total_paid, + 'shipping' => $order->total_shipping, + 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, + 'url' => $this->context->link->getModuleLink('ganalytics', 'ajax', array(), true), + 'customer' => $order->id_customer); + $ga_scripts .= $this->addTransaction($order_products, $transaction); + + $this->js_state = 1; + return $this->_runJs($ga_scripts); + } + } + } + } + + /** + * hook footer to load JS script for standards actions such as product clicks + */ + public function hookFooter() + { + $ga_scripts = ''; + $this->js_state = 0; + + if (isset($this->context->cookie->ga_cart)) { + $this->filterable = 0; + + $gacarts = unserialize($this->context->cookie->ga_cart); + foreach ($gacarts as $gacart) { + if ($gacart['quantity'] > 0) + $ga_scripts .= 'MBG.addToCart(' . Tools::jsonEncode($gacart) . ');'; + elseif ($gacart['quantity'] < 0) { + $gacart['quantity'] = abs($gacart['quantity']); + $ga_scripts .= 'MBG.removeFromCart(' . Tools::jsonEncode($gacart) . ');'; + } + } + unset($this->context->cookie->ga_cart); + } + + $controller_name = Tools::getValue('controller'); + $products = $this->wrapProducts($this->context->smarty->getTemplateVars('products'), array(), true); + + if ($controller_name == 'order' || $controller_name == 'orderopc') { + $this->js_state = 1; + $this->eligible = 1; + $step = Tools::getValue('step'); + if (empty($step)) + $step = 0; + $ga_scripts .= $this->addProductFromCheckout($products, $step); + $ga_scripts .= 'MBG.addCheckout(\'' . (int)$step . '\');'; + } + + if (version_compare(_PS_VERSION_, '1.5', '<')) { + if ($controller_name == 'orderconfirmation') + $this->eligible = 1; + } else { + $confirmation_hook_id = (int)Hook::getIdByName('orderConfirmation'); + if (isset(Hook::$executed_hooks[$confirmation_hook_id])) + $this->eligible = 1; + } + + if (isset($products) && count($products) && $controller_name != 'index') { + if ($this->eligible == 0) + $ga_scripts .= $this->addProductImpression($products); + $ga_scripts .= $this->addProductClick($products); + } + + return $this->_runJs($ga_scripts); + } + + protected function filter($ga_scripts) + { + if ($this->filterable = 1) + return implode(';', array_unique(explode(';', $ga_scripts))); + + return $ga_scripts; + } + + /** + * hook home to display generate the product list associated to home featured, news products and best sellers Modules + */ + public function hookHome() + { + $ga_scripts = ''; + + // Home featured products + if ($this->isModuleEnabled('homefeatured')) { + $category = new Category($this->context->shop->getCategory(), $this->context->language->id); + $home_featured_products = $this->wrapProducts($category->getProducts((int)Context::getContext()->language->id, 1, + (Configuration::get('HOME_FEATURED_NBR') ? (int)Configuration::get('HOME_FEATURED_NBR') : 8), 'position'), array(), true); + $ga_scripts .= $this->addProductImpression($home_featured_products) . $this->addProductClick($home_featured_products); + } + + // New products + if ($this->isModuleEnabled('blocknewproducts') && (Configuration::get('PS_NB_DAYS_NEW_PRODUCT') + || Configuration::get('PS_BLOCK_NEWPRODUCTS_DISPLAY'))) { + $new_products = Product::getNewProducts((int)$this->context->language->id, 0, (int)Configuration::get('NEW_PRODUCTS_NBR')); + $new_products_list = $this->wrapProducts($new_products, array(), true); + $ga_scripts .= $this->addProductImpression($new_products_list) . $this->addProductClick($new_products_list); + } + + // Best Sellers + if ($this->isModuleEnabled('blockbestsellers') && (!Configuration::get('PS_CATALOG_MODE') + || Configuration::get('PS_BLOCK_BESTSELLERS_DISPLAY'))) { + $ga_homebestsell_product_list = $this->wrapProducts(ProductSale::getBestSalesLight((int)$this->context->language->id, 0, 8), array(), true); + $ga_scripts .= $this->addProductImpression($ga_homebestsell_product_list) . $this->addProductClick($ga_homebestsell_product_list); + } + + $this->js_state = 1; + return $this->_runJs($this->filter($ga_scripts)); + } + + /** + * hook home to display generate the product list associated to home featured, news products and best sellers Modules + */ + public function isModuleEnabled($name) + { + if (version_compare(_PS_VERSION_, '1.5', '>=')) + if (Module::isEnabled($name)) { + $module = Module::getInstanceByName($name); + return $module->isRegisteredInHook('home'); + } else + return false; + else { + $module = Module::getInstanceByName($name); + return ($module && $module->active === true); + } + } + + /** + * wrap products to provide a standard products information for google analytics script + */ + public function wrapProducts($products, $extras = array(), $full = false) + { + $result_products = array(); + if (!is_array($products)) + return; + + $currency = new Currency($this->context->currency->id); + $usetax = (Product::getTaxCalculationMethod((int)$this->context->customer->id) != PS_TAX_EXC); + + if (count($products) > 20) + $full = false; + else + $full = true; + + foreach ($products as $index => $product) { + if ($product instanceof Product) + $product = (array)$product; + + if (!isset($product['price'])) + $product['price'] = (float)Tools::displayPrice(Product::getPriceStatic((int)$product['id_product'], $usetax), $currency); + $result_products[] = $this->wrapProduct($product, $extras, $index, $full); + } + + return $result_products; + } + + /** + * wrap product to provide a standard product information for google analytics script + */ + public function wrapProduct($product, $extras, $index = 0, $full = false) + { + $ga_product = ''; + + $variant = null; + if (isset($product['attributes_small'])) + $variant = $product['attributes_small']; + elseif (isset($extras['attributes_small'])) + $variant = $extras['attributes_small']; + + $product_qty = 1; + if (isset($extras['qty'])) + $product_qty = $extras['qty']; + elseif (isset($product['cart_quantity'])) + $product_qty = $product['cart_quantity']; + + $product_id = 0; + if (!empty($product['id_product'])) + $product_id = $product['id_product']; + else if (!empty($product['id'])) + $product_id = $product['id']; + + if (!empty($product['id_product_attribute'])) + $product_id .= '-' . $product['id_product_attribute']; + + $product_type = 'typical'; + if (isset($product['pack']) && $product['pack'] == 1) + $product_type = 'pack'; + elseif (isset($product['virtual']) && $product['virtual'] == 1) + $product_type = 'virtual'; + + if ($full) { + $ga_product = array( + 'id' => $product_id, + 'name' => Tools::str2url($product['name']), + 'category' => Tools::str2url($product['category']), + 'brand' => isset($product['manufacturer_name']) ? Tools::str2url($product['manufacturer_name']) : '', + 'variant' => Tools::str2url($variant), + 'type' => $product_type, + 'position' => $index ? $index : '0', + 'quantity' => $product_qty, + 'list' => Tools::getValue('controller'), + 'url' => isset($product['link']) ? urlencode($product['link']) : '', + 'price' => number_format($product['price'], 2, '.', '') + ); + } else { + $ga_product = array( + 'id' => $product_id, + 'name' => Tools::str2url($product['name']) + ); + } + return $ga_product; + } + + /** + * add order transaction + */ + public function addTransaction($products, $order) + { + if (!is_array($products)) + return; + + $js = ''; + foreach ($products as $product) + $js .= 'MBG.add(' . Tools::jsonEncode($product) . ');'; + + return $js . 'MBG.addTransaction(' . Tools::jsonEncode($order) . ');'; + } + + /** + * add product impression js and product click js + */ + public function addProductImpression($products) + { + if (!is_array($products)) + return; + + $js = ''; + foreach ($products as $product) + $js .= 'MBG.add(' . Tools::jsonEncode($product) . ",'',true);"; + + return $js; + } + + public function addProductClick($products) + { + if (!is_array($products)) + return; + + $js = ''; + foreach ($products as $product) + $js .= 'MBG.addProductClick(' . Tools::jsonEncode($product) . ');'; + + return $js; + } + + public function addProductClickByHttpReferal($products) + { + if (!is_array($products)) + return; + + $js = ''; + foreach ($products as $product) + $js .= 'MBG.addProductClickByHttpReferal(' . Tools::jsonEncode($product) . ');'; + + return $js; + } + + /** + * Add product checkout info + */ + public function addProductFromCheckout($products) + { + if (!is_array($products)) + return; + + $js = ''; + foreach ($products as $product) + $js .= 'MBG.add(' . Tools::jsonEncode($product) . ');'; + + return $js; + } + + /** + * hook product page footer to load JS for product details view + */ + public function hookProductFooter($params) + { + $controller_name = Tools::getValue('controller'); + if ($controller_name == 'product') { + // Add product view + $ga_product = $this->wrapProduct((array)$params['product'], null, 0, true); + $js = 'MBG.addProductDetailView(' . Tools::jsonEncode($ga_product) . ');'; + + if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) > 0) + $js .= $this->addProductClickByHttpReferal(array($ga_product)); + + $this->js_state = 1; + return $this->_runJs($js); + } + } + + /** + * Generate Google Analytics js + */ + protected function _runJs($js_code, $backoffice = 0) + { + if (Configuration::get('GA_ACCOUNT_ID')) { + $runjs_code = ''; + + if (!empty($js_code)) { + if ($backoffice) { + $js_code .= 'MBG.setCampaign(\'backoffice-orders\',\'backoffice\',\'cms\');'; + } + + $runjs_code .= ' + '; + } + + if (($this->js_state) != 1 && ($backoffice == 0)) + $runjs_code .= ' + '; + + return $runjs_code; + } + } + + /** + * Hook admin order to send transactions and refunds details + */ + public function hookAdminOrder() + { + echo $this->_runJs($this->context->cookie->ga_admin_refund, 1); + unset($this->context->cookie->ga_admin_refund); + } + + /** + * admin office header to add google analytics js + */ + public function hookBackOfficeHeader() + { + $js = ''; + if (strcmp(Tools::getValue('configure'), $this->name) === 0) { + if (version_compare(_PS_VERSION_, '1.5', '>') == true) { + $this->context->controller->addCSS($this->_path . 'views/css/ganalytics.css'); + if (version_compare(_PS_VERSION_, '1.6', '<') == true) + $this->context->controller->addCSS($this->_path . 'views/css/ganalytics-nobootstrap.css'); + } else { + $js .= '\ + '; + } + } + + $ga_account_id = Configuration::get('GA_ACCOUNT_ID'); + + if (!empty($ga_account_id) && $this->active) { + if (version_compare(_PS_VERSION_, '1.5', '>=') == true) + $this->context->controller->addJs($this->_path . 'views/js/GoogleAnalyticActionLib.js'); + else + $js .= ''; + + $this->context->smarty->assign('GA_ACCOUNT_ID', $ga_account_id); + + $ga_scripts = ''; + if ($this->context->controller->controller_name == 'AdminOrders') { + if (Tools::getValue('id_order')) { + $order = new Order((int)Tools::getValue('id_order')); + if (Validate::isLoadedObject($order) && strtotime('+1 day', strtotime($order->date_add)) > time()) { + $ga_order_sent = Db::getInstance()->getValue('SELECT id_order FROM `' . _DB_PREFIX_ . 'ganalytics` WHERE id_order = ' . (int)Tools::getValue('id_order')); + if ($ga_order_sent === false) + Db::getInstance()->Execute('INSERT IGNORE INTO `' . _DB_PREFIX_ . 'ganalytics` (id_order, id_shop, sent, date_add) VALUES (' . (int)Tools::getValue('id_order') . ', ' . (int)$this->context->shop->id . ', 0, NOW())'); + } + } else { + $ga_order_records = Db::getInstance()->ExecuteS('SELECT * FROM `' . _DB_PREFIX_ . 'ganalytics` WHERE sent = 0 AND id_shop = \'' . (int)$this->context->shop->id . '\' AND DATE_ADD(date_add, INTERVAL 30 minute) < NOW()'); + + if ($ga_order_records) + foreach ($ga_order_records as $row) { + $transaction = $this->wrapOrder($row['id_order']); + if (!empty($transaction)) { + Db::getInstance()->execute('UPDATE `' . _DB_PREFIX_ . 'ganalytics` SET date_add = NOW(), sent = 1 WHERE id_order = ' . (int)$row['id_order'] . ' AND id_shop = \'' . (int)$this->context->shop->id . '\''); + $transaction = Tools::jsonEncode($transaction); + $ga_scripts .= 'MBG.addTransaction(' . $transaction . ');'; + } + } + + } + } + return $js . $this->_getGoogleAnalyticsTag(true) . $this->_runJs($ga_scripts, 1); + } else return $js; + } + + /** + * Hook admin office header to add google analytics js + */ + public function hookActionProductCancel($params) + { + $qty_refunded = Tools::getValue('cancelQuantity'); + $ga_scripts = ''; + foreach ($qty_refunded as $orderdetail_id => $qty) { + // Display GA refund product + $order_detail = new OrderDetail($orderdetail_id); + $ga_scripts .= 'MBG.add(' . Tools::jsonEncode( + array( + 'id' => empty($order_detail->product_attribute_id) ? $order_detail->product_id : $order_detail->product_id . '-' . $order_detail->product_attribute_id, + 'quantity' => $qty) + ) . ');'; + } + $this->context->cookie->ga_admin_refund = $ga_scripts . 'MBG.refundByProduct(' . Tools::jsonEncode(array('id' => $params['order']->id)) . ');'; + } + + /** + * hook save cart event to implement addtocart and remove from cart functionality + */ + public function hookActionCartSave() + { + if (!isset($this->context->cart)) + return; + + if (!Tools::getIsset('id_product')) + return; + + $cart = array( + 'controller' => Tools::getValue('controller'), + 'addAction' => Tools::getValue('add') ? 'add' : '', + 'removeAction' => Tools::getValue('delete') ? 'delete' : '', + 'extraAction' => Tools::getValue('op'), + 'qty' => (int)Tools::getValue('qty', 1) + ); + + $cart_products = $this->context->cart->getProducts(); + if (isset($cart_products) && count($cart_products)) + foreach ($cart_products as $cart_product) + if ($cart_product['id_product'] == Tools::getValue('id_product')) + $add_product = $cart_product; + + if ($cart['removeAction'] == 'delete') { + $add_product_object = new Product((int)Tools::getValue('id_product'), true, (int)Configuration::get('PS_LANG_DEFAULT')); + if (Validate::isLoadedObject($add_product_object)) { + $add_product['name'] = $add_product_object->name; + $add_product['manufacturer_name'] = $add_product_object->manufacturer_name; + $add_product['category'] = $add_product_object->category; + $add_product['reference'] = $add_product_object->reference; + $add_product['link_rewrite'] = $add_product_object->link_rewrite; + $add_product['link'] = $add_product_object->link_rewrite; + $add_product['price'] = $add_product_object->price; + $add_product['ean13'] = $add_product_object->ean13; + $add_product['id_product'] = Tools::getValue('id_product'); + $add_product['id_category_default'] = $add_product_object->id_category_default; + $add_product['out_of_stock'] = $add_product_object->out_of_stock; + $add_product = Product::getProductProperties((int)Configuration::get('PS_LANG_DEFAULT'), $add_product); + } + } + + if (isset($add_product) && !in_array((int)Tools::getValue('id_product'), self::$products)) { + self::$products[] = (int)Tools::getValue('id_product'); + $ga_products = $this->wrapProduct($add_product, $cart, 0, true); + + if (array_key_exists('id_product_attribute', $ga_products) && $ga_products['id_product_attribute'] != '' && $ga_products['id_product_attribute'] != 0) + $id_product = $ga_products['id_product_attribute']; + else + $id_product = Tools::getValue('id_product'); + + if (isset($this->context->cookie->ga_cart)) + $gacart = unserialize($this->context->cookie->ga_cart); + else + $gacart = array(); + + if ($cart['removeAction'] == 'delete') + $ga_products['quantity'] = -1; + elseif ($cart['extraAction'] == 'down') { + if (array_key_exists($id_product, $gacart)) + $ga_products['quantity'] = $gacart[$id_product]['quantity'] - $cart['qty']; + else + $ga_products['quantity'] = $cart['qty'] * -1; + } elseif (Tools::getValue('step') <= 0) // Sometimes cartsave is called in checkout + { + if (array_key_exists($id_product, $gacart)) + $ga_products['quantity'] = $gacart[$id_product]['quantity'] + $cart['qty']; + } + + $gacart[$id_product] = $ga_products; + $this->context->cookie->ga_cart = serialize($gacart); + } + } + + public function hookProcessCarrier($params) + { + if (isset($params['cart']->id_carrier)) { + $carrier_name = Db::getInstance()->getValue('SELECT name FROM `' . _DB_PREFIX_ . 'carrier` WHERE id_carrier = ' . (int)$params['cart']->id_carrier); + $this->context->cookie->ga_cart .= 'MBG.addCheckoutOption(2,\'' . $carrier_name . '\');'; + } + } + + protected function checkForUpdates() + { + // Used by PrestaShop 1.3 & 1.4 + if (version_compare(_PS_VERSION_, '1.5', '<') && self::isInstalled($this->name)) + foreach (array('2.0.0', '2.0.4', '2.0.5', '2.0.6', '2.1.0') as $version) { + $file = dirname(__FILE__) . '/upgrade/Upgrade-' . $version . '.php'; + if (version_compare(Configuration::get('GANALYTICS'), $version, '<') && file_exists($file)) { + include_once($file); + call_user_func('upgrade_module_' . str_replace('.', '_', $version), $this); + } + } + } + + protected function _debugLog($function, $log) + { + if (!$this->_debug) + return true; + + $myFile = _PS_MODULE_DIR_ . $this->name . '/logs/analytics.log'; + $fh = fopen($myFile, 'a'); + fwrite($fh, date('F j, Y, g:i a') . ' ' . $function . "\n"); + fwrite($fh, print_r($log, true) . "\n\n"); + fclose($fh); + } } From 4face2f2fbc9ccc976afe012ba698a6efbc8bbd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Ag=C3=BC=C3=AD?= Date: Thu, 23 Jan 2020 12:05:55 +0100 Subject: [PATCH 2/7] unconditionally sends a pageview on all non backoffice pages --- ganalytics.php | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/ganalytics.php b/ganalytics.php index 6209db8..a0f3d08 100755 --- a/ganalytics.php +++ b/ganalytics.php @@ -29,7 +29,6 @@ class Ganalytics extends Module { - protected $js_state = 0; protected $eligible = 0; protected $filterable = 1; protected static $products = array(); @@ -241,6 +240,11 @@ public function getContent() return $this->display(__FILE__, 'views/templates/admin/configuration.tpl') . $output; } + protected function _getMeasurementId() + { + return Tools::safeOutput(Configuration::get('GA_ACCOUNT_ID')); + } + protected function _getGoogleAnalyticsTag($back_office = false) { $user_id = null; @@ -249,19 +253,17 @@ protected function _getGoogleAnalyticsTag($back_office = false) ) { $user_id = (int)$this->context->customer->id; } - - return ' - '; + $gaMeasurementId = $this->_getMeasurementId(); + $tag = " + + "; + return $tag; } public function hookHeader() @@ -319,7 +321,6 @@ public function hookOrderConfirmation($params) 'customer' => $order->id_customer); $ga_scripts .= $this->addTransaction($order_products, $transaction); - $this->js_state = 1; return $this->_runJs($ga_scripts); } } @@ -332,7 +333,6 @@ public function hookOrderConfirmation($params) public function hookFooter() { $ga_scripts = ''; - $this->js_state = 0; if (isset($this->context->cookie->ga_cart)) { $this->filterable = 0; @@ -353,7 +353,6 @@ public function hookFooter() $products = $this->wrapProducts($this->context->smarty->getTemplateVars('products'), array(), true); if ($controller_name == 'order' || $controller_name == 'orderopc') { - $this->js_state = 1; $this->eligible = 1; $step = Tools::getValue('step'); if (empty($step)) @@ -418,7 +417,6 @@ public function hookHome() $ga_scripts .= $this->addProductImpression($ga_homebestsell_product_list) . $this->addProductClick($ga_homebestsell_product_list); } - $this->js_state = 1; return $this->_runJs($this->filter($ga_scripts)); } @@ -608,7 +606,6 @@ public function hookProductFooter($params) if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) > 0) $js .= $this->addProductClickByHttpReferal(array($ga_product)); - $this->js_state = 1; return $this->_runJs($js); } } @@ -636,11 +633,6 @@ protected function _runJs($js_code, $backoffice = 0) '; } - if (($this->js_state) != 1 && ($backoffice == 0)) - $runjs_code .= ' - '; return $runjs_code; } From 558e3279a09f43a045eb8f5a0814a005d5094c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Ag=C3=BC=C3=AD?= Date: Thu, 23 Jan 2020 12:07:00 +0100 Subject: [PATCH 3/7] to spaces and beautified --- views/js/GoogleAnalyticActionLib.js | 324 ++++++++++++++-------------- 1 file changed, 162 insertions(+), 162 deletions(-) diff --git a/views/js/GoogleAnalyticActionLib.js b/views/js/GoogleAnalyticActionLib.js index 3cad86f..b96bf46 100755 --- a/views/js/GoogleAnalyticActionLib.js +++ b/views/js/GoogleAnalyticActionLib.js @@ -27,166 +27,166 @@ var GoogleAnalyticEnhancedECommerce = { - setCurrency: function(Currency) { - ga('set', '&cu', Currency); - }, - - setCampaign: function(Name,Source,Medium){ - ga('set', 'campaignName', Name); - ga('set', 'campaignSource', Source); - ga('set', 'campaignMedium', Medium); - }, - - add: function(Product, Order, Impression) { - var Products = {}; - var Orders = {}; - - var ProductFieldObject = ['id', 'name', 'category', 'brand', 'variant', 'price', 'quantity', 'coupon', 'list', 'position', 'dimension1']; - var OrderFieldObject = ['id', 'affiliation', 'revenue', 'tax', 'shipping', 'coupon', 'list', 'step', 'option']; - - if (Product != null) { - if (Impression && Product.quantity !== undefined) { - delete Product.quantity; - } - - for (var productKey in Product) { - for (var i = 0; i < ProductFieldObject.length; i++) { - if (productKey.toLowerCase() == ProductFieldObject[i]) { - if (Product[productKey] != null) { - Products[productKey.toLowerCase()] = Product[productKey]; - } - - } - } - - } - } - - if (Order != null) { - for (var orderKey in Order) { - for (var j = 0; j < OrderFieldObject.length; j++) { - if (orderKey.toLowerCase() == OrderFieldObject[j]) { - Orders[orderKey.toLowerCase()] = Order[orderKey]; - } - } - } - } - - if (Impression) { - ga('ec:addImpression', Products); - } else { - ga('ec:addProduct', Products); - } - }, - - addProductDetailView: function(Product) { - this.add(Product); - ga('ec:setAction', 'detail'); - ga('send', 'event', 'UX', 'detail', 'Product Detail View',{'nonInteraction': 1}); - }, - - addToCart: function(Product) { - this.add(Product); - ga('ec:setAction', 'add'); - ga('send', 'event', 'UX', 'click', 'Add to Cart'); // Send data using an event. - }, - - removeFromCart: function(Product) { - this.add(Product); - ga('ec:setAction', 'remove'); - ga('send', 'event', 'UX', 'click', 'Remove From cart'); // Send data using an event. - }, - - addProductImpression: function(Product) { - //ga('send', 'pageview'); - }, - - /** - id, type, affiliation, revenue, tax, shipping and coupon. - **/ - refundByOrderId: function(Order) { - /** - * Refund an entire transaction. - **/ - ga('ec:setAction', 'refund', { - 'id': Order.id // Transaction ID is only required field for full refund. - }); - ga('send', 'event', 'Ecommerce', 'Refund', {'nonInteraction': 1}); - }, - - refundByProduct: function(Order) { - /** - * Refund a single product. - **/ - //this.add(Product); - - ga('ec:setAction', 'refund', { - 'id': Order.id, // Transaction ID is required for partial refund. - }); - ga('send', 'event', 'Ecommerce', 'Refund', {'nonInteraction': 1}); - }, - - addProductClick: function(Product) { - var ClickPoint = jQuery('a[href$="' + Product.url + '"].quick-view'); - - ClickPoint.on("click", function() { - GoogleAnalyticEnhancedECommerce.add(Product); - ga('ec:setAction', 'click', { - list: Product.list - }); - - ga('send', 'event', 'Product Quick View', 'click', Product.list, { - 'hitCallback': function() { - return !ga.loaded; - } - }); - }); - - }, - - addProductClickByHttpReferal: function(Product) { - this.add(Product); - ga('ec:setAction', 'click', { - list: Product.list - }); - - ga('send', 'event', 'Product Click', 'click', Product.list, { - 'nonInteraction': 1, - 'hitCallback': function() { - return !ga.loaded; - } - }); - - }, - - addTransaction: function(Order) { - - //this.add(Product); - ga('ec:setAction', 'purchase', Order); - ga('send', 'event','Transaction','purchase', { - 'hitCallback': function() { - $.get(Order.url, { - orderid: Order.id, - customer: Order.customer - }); - } - }); - - }, - - addCheckout: function(Step) { - ga('ec:setAction', 'checkout', { - 'step': Step - //'option':'Visa' - }); - ga('send', 'pageview'); - }, - - addCheckoutOption: function(Step,Option) { - ga('ec:setAction', 'checkout_option', { - 'step': Step, - 'option': Option - }); - ga('send', 'event', 'Checkout', 'Option'); - } + setCurrency: function (Currency) { + ga('set', '&cu', Currency); + }, + + setCampaign: function (Name, Source, Medium) { + ga('set', 'campaignName', Name); + ga('set', 'campaignSource', Source); + ga('set', 'campaignMedium', Medium); + }, + + add: function (Product, Order, Impression) { + var Products = {}; + var Orders = {}; + + var ProductFieldObject = ['id', 'name', 'category', 'brand', 'variant', 'price', 'quantity', 'coupon', 'list', 'position', 'dimension1']; + var OrderFieldObject = ['id', 'affiliation', 'revenue', 'tax', 'shipping', 'coupon', 'list', 'step', 'option']; + + if (Product != null) { + if (Impression && Product.quantity !== undefined) { + delete Product.quantity; + } + + for (var productKey in Product) { + for (var i = 0; i < ProductFieldObject.length; i++) { + if (productKey.toLowerCase() == ProductFieldObject[i]) { + if (Product[productKey] != null) { + Products[productKey.toLowerCase()] = Product[productKey]; + } + + } + } + + } + } + + if (Order != null) { + for (var orderKey in Order) { + for (var j = 0; j < OrderFieldObject.length; j++) { + if (orderKey.toLowerCase() == OrderFieldObject[j]) { + Orders[orderKey.toLowerCase()] = Order[orderKey]; + } + } + } + } + + if (Impression) { + ga('ec:addImpression', Products); + } else { + ga('ec:addProduct', Products); + } + }, + + addProductDetailView: function (Product) { + this.add(Product); + ga('ec:setAction', 'detail'); + ga('send', 'event', 'UX', 'detail', 'Product Detail View', {'nonInteraction': 1}); + }, + + addToCart: function (Product) { + this.add(Product); + ga('ec:setAction', 'add'); + ga('send', 'event', 'UX', 'click', 'Add to Cart'); // Send data using an event. + }, + + removeFromCart: function (Product) { + this.add(Product); + ga('ec:setAction', 'remove'); + ga('send', 'event', 'UX', 'click', 'Remove From cart'); // Send data using an event. + }, + + addProductImpression: function (Product) { + //ga('send', 'pageview'); + }, + + /** + id, type, affiliation, revenue, tax, shipping and coupon. + **/ + refundByOrderId: function (Order) { + /** + * Refund an entire transaction. + **/ + ga('ec:setAction', 'refund', { + 'id': Order.id // Transaction ID is only required field for full refund. + }); + ga('send', 'event', 'Ecommerce', 'Refund', {'nonInteraction': 1}); + }, + + refundByProduct: function (Order) { + /** + * Refund a single product. + **/ + //this.add(Product); + + ga('ec:setAction', 'refund', { + 'id': Order.id, // Transaction ID is required for partial refund. + }); + ga('send', 'event', 'Ecommerce', 'Refund', {'nonInteraction': 1}); + }, + + addProductClick: function (Product) { + var ClickPoint = jQuery('a[href$="' + Product.url + '"].quick-view'); + + ClickPoint.on("click", function () { + GoogleAnalyticEnhancedECommerce.add(Product); + ga('ec:setAction', 'click', { + list: Product.list + }); + + ga('send', 'event', 'Product Quick View', 'click', Product.list, { + 'hitCallback': function () { + return !ga.loaded; + } + }); + }); + + }, + + addProductClickByHttpReferal: function (Product) { + this.add(Product); + ga('ec:setAction', 'click', { + list: Product.list + }); + + ga('send', 'event', 'Product Click', 'click', Product.list, { + 'nonInteraction': 1, + 'hitCallback': function () { + return !ga.loaded; + } + }); + + }, + + addTransaction: function (Order) { + + //this.add(Product); + ga('ec:setAction', 'purchase', Order); + ga('send', 'event', 'Transaction', 'purchase', { + 'hitCallback': function () { + $.get(Order.url, { + orderid: Order.id, + customer: Order.customer + }); + } + }); + + }, + + addCheckout: function (Step) { + ga('ec:setAction', 'checkout', { + 'step': Step + //'option':'Visa' + }); + ga('send', 'pageview'); + }, + + addCheckoutOption: function (Step, Option) { + ga('ec:setAction', 'checkout_option', { + 'step': Step, + 'option': Option + }); + ga('send', 'event', 'Checkout', 'Option'); + } }; From 3afb2fcf168e5c4492d76d4164150ad9de054584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Ag=C3=BC=C3=AD?= Date: Thu, 23 Jan 2020 18:00:47 +0100 Subject: [PATCH 4/7] implements analytics using the [google global site tag (gtag.js)](https://developers.google.com/analytics/devguides/collection/gtagjs) --- config_es.xml | 13 ++ ganalytics.php | 104 +++++------- views/js/GoogleAnalyticActionLib.js | 208 ++++++++++------------- views/templates/hook/googleanalytics.tpl | 21 +++ 4 files changed, 162 insertions(+), 184 deletions(-) create mode 100644 config_es.xml create mode 100644 views/templates/hook/googleanalytics.tpl diff --git a/config_es.xml b/config_es.xml new file mode 100644 index 0000000..3813bdc --- /dev/null +++ b/config_es.xml @@ -0,0 +1,13 @@ + + + ganalytics + + + + + + + 1 + 1 + + \ No newline at end of file diff --git a/ganalytics.php b/ganalytics.php index a0f3d08..51c2286 100755 --- a/ganalytics.php +++ b/ganalytics.php @@ -253,17 +253,15 @@ protected function _getGoogleAnalyticsTag($back_office = false) ) { $user_id = (int)$this->context->customer->id; } - $gaMeasurementId = $this->_getMeasurementId(); - $tag = " - - "; - return $tag; + + $this->smarty->assign( + array( + 'back_office' => $back_office, + 'user_id' => $user_id, + 'ga_measurement_id' => $this->_getMeasurementId(), + ) + ); + return $this->display(__FILE__, 'googleanalytics.tpl'); } public function hookHeader() @@ -284,9 +282,9 @@ public function wrapOrder($id_order) if (Validate::isLoadedObject($order)) return array( - 'id' => $id_order, + 'transaction_id' => $id_order, 'affiliation' => Shop::isFeatureActive() ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), - 'revenue' => $order->total_paid, + 'value' => $order->total_paid, 'shipping' => $order->total_shipping, 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, 'url' => $this->context->link->getAdminLink('AdminGanalyticsAjax'), @@ -309,17 +307,18 @@ public function hookOrderConfirmation($params) foreach ($cart->getProducts() as $order_product) $order_products[] = $this->wrapProduct($order_product, array(), 0, true); - $ga_scripts = 'MBG.addCheckoutOption(3,\'' . $order->payment . '\');'; + $ga_scripts = 'MBG.addCheckoutOption(3, \'payment\',\'' . $order->payment . '\');'; $transaction = array( - 'id' => $order->id, + 'transaction_id' => $order->id, 'affiliation' => (version_compare(_PS_VERSION_, '1.5', '>=') && Shop::isFeatureActive()) ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'), - 'revenue' => $order->total_paid, + 'value' => $order->total_paid, 'shipping' => $order->total_shipping, 'tax' => $order->total_paid_tax_incl - $order->total_paid_tax_excl, 'url' => $this->context->link->getModuleLink('ganalytics', 'ajax', array(), true), - 'customer' => $order->id_customer); - $ga_scripts .= $this->addTransaction($order_products, $transaction); + 'customer' => $order->id_customer, + 'items' => $order_products); + $ga_scripts .= $this->addTransaction($transaction); return $this->_runJs($ga_scripts); } @@ -352,13 +351,15 @@ public function hookFooter() $controller_name = Tools::getValue('controller'); $products = $this->wrapProducts($this->context->smarty->getTemplateVars('products'), array(), true); - if ($controller_name == 'order' || $controller_name == 'orderopc') { + if ($controller_name == 'order' || $controller_name == 'orderopc' || $controller_name == 'supercheckout') { $this->eligible = 1; $step = Tools::getValue('step'); if (empty($step)) $step = 0; - $ga_scripts .= $this->addProductFromCheckout($products, $step); - $ga_scripts .= 'MBG.addCheckout(\'' . (int)$step . '\');'; + $ga_scripts .= 'MBG.addCheckout(\'' . (int)$step . '\',['; + foreach ($products as $product) + $ga_scripts .= Tools::jsonEncode($product) . ','; + $ga_scripts .= ']);'; } if (version_compare(_PS_VERSION_, '1.5', '<')) { @@ -468,11 +469,14 @@ public function wrapProducts($products, $extras = array(), $full = false) /** * wrap product to provide a standard product information for google analytics script + * @param $product + * @param $extras + * @param int $index + * @param bool $full + * @return array */ - public function wrapProduct($product, $extras, $index = 0, $full = false) + private function wrapProduct($product, $extras, $index = 0, $full = false) { - $ga_product = ''; - $variant = null; if (isset($product['attributes_small'])) $variant = $product['attributes_small']; @@ -494,12 +498,6 @@ public function wrapProduct($product, $extras, $index = 0, $full = false) if (!empty($product['id_product_attribute'])) $product_id .= '-' . $product['id_product_attribute']; - $product_type = 'typical'; - if (isset($product['pack']) && $product['pack'] == 1) - $product_type = 'pack'; - elseif (isset($product['virtual']) && $product['virtual'] == 1) - $product_type = 'virtual'; - if ($full) { $ga_product = array( 'id' => $product_id, @@ -507,10 +505,9 @@ public function wrapProduct($product, $extras, $index = 0, $full = false) 'category' => Tools::str2url($product['category']), 'brand' => isset($product['manufacturer_name']) ? Tools::str2url($product['manufacturer_name']) : '', 'variant' => Tools::str2url($variant), - 'type' => $product_type, - 'position' => $index ? $index : '0', + 'list_position' => $index ? $index : '0', 'quantity' => $product_qty, - 'list' => Tools::getValue('controller'), + 'list_name' => Tools::getValue('controller'), 'url' => isset($product['link']) ? urlencode($product['link']) : '', 'price' => number_format($product['price'], 2, '.', '') ); @@ -526,16 +523,9 @@ public function wrapProduct($product, $extras, $index = 0, $full = false) /** * add order transaction */ - public function addTransaction($products, $order) + public function addTransaction($transaction) { - if (!is_array($products)) - return; - - $js = ''; - foreach ($products as $product) - $js .= 'MBG.add(' . Tools::jsonEncode($product) . ');'; - - return $js . 'MBG.addTransaction(' . Tools::jsonEncode($order) . ');'; + return 'MBG.addTransaction(' . Tools::jsonEncode($transaction) . ');'; } /** @@ -546,10 +536,10 @@ public function addProductImpression($products) if (!is_array($products)) return; - $js = ''; + $js = 'MBG.viewItemList(['; foreach ($products as $product) - $js .= 'MBG.add(' . Tools::jsonEncode($product) . ",'',true);"; - + $js .= Tools::jsonEncode($product) . ','; + $js .= ']);'; return $js; } @@ -577,20 +567,6 @@ public function addProductClickByHttpReferal($products) return $js; } - /** - * Add product checkout info - */ - public function addProductFromCheckout($products) - { - if (!is_array($products)) - return; - - $js = ''; - foreach ($products as $product) - $js .= 'MBG.add(' . Tools::jsonEncode($product) . ');'; - - return $js; - } /** * hook product page footer to load JS for product details view @@ -620,14 +596,16 @@ protected function _runJs($js_code, $backoffice = 0) if (!empty($js_code)) { if ($backoffice) { - $js_code .= 'MBG.setCampaign(\'backoffice-orders\',\'backoffice\',\'cms\');'; + $js_code .= 'MBG.setCampaign(\'backoffice-orders\',\'backoffice\',\'cms\',\'' . $this->_getMeasurementId() . '\');'; } $runjs_code .= ' '; @@ -718,7 +696,7 @@ public function hookActionProductCancel($params) 'quantity' => $qty) ) . ');'; } - $this->context->cookie->ga_admin_refund = $ga_scripts . 'MBG.refundByProduct(' . Tools::jsonEncode(array('id' => $params['order']->id)) . ');'; + $this->context->cookie->ga_admin_refund = $ga_scripts . 'MBG.refundByOrderId(' . Tools::jsonEncode(array('id' => $params['order']->id)) . ');'; } /** @@ -800,7 +778,7 @@ public function hookProcessCarrier($params) { if (isset($params['cart']->id_carrier)) { $carrier_name = Db::getInstance()->getValue('SELECT name FROM `' . _DB_PREFIX_ . 'carrier` WHERE id_carrier = ' . (int)$params['cart']->id_carrier); - $this->context->cookie->ga_cart .= 'MBG.addCheckoutOption(2,\'' . $carrier_name . '\');'; + $this->context->cookie->ga_cart .= 'MBG.addCheckoutOption(2, \'carrier\',\'' . $carrier_name . '\');'; } } diff --git a/views/js/GoogleAnalyticActionLib.js b/views/js/GoogleAnalyticActionLib.js index b96bf46..187d06e 100755 --- a/views/js/GoogleAnalyticActionLib.js +++ b/views/js/GoogleAnalyticActionLib.js @@ -23,170 +23,136 @@ * International Registered Trademark & Property of PrestaShop SA **/ -/* globals $, ga, jQuery */ +/* globals $, gtag, jQuery */ -var GoogleAnalyticEnhancedECommerce = { +const GoogleAnalyticEnhancedECommerce = { - setCurrency: function (Currency) { - ga('set', '&cu', Currency); + setCurrency: function (Currency, gaMeasurementId) { + gtag('config', gaMeasurementId, {'&cu': Currency}); }, - setCampaign: function (Name, Source, Medium) { - ga('set', 'campaignName', Name); - ga('set', 'campaignSource', Source); - ga('set', 'campaignMedium', Medium); + setCampaign: function (Name, Source, Medium, gaMeasurementId) { + gtag('config', gaMeasurementId, {'campaignName': Name}); + gtag('config', gaMeasurementId, {'campaignSource': Source}); + gtag('config', gaMeasurementId, {'campaignMedium': Medium}); }, - add: function (Product, Order, Impression) { - var Products = {}; - var Orders = {}; - - var ProductFieldObject = ['id', 'name', 'category', 'brand', 'variant', 'price', 'quantity', 'coupon', 'list', 'position', 'dimension1']; - var OrderFieldObject = ['id', 'affiliation', 'revenue', 'tax', 'shipping', 'coupon', 'list', 'step', 'option']; - - if (Product != null) { - if (Impression && Product.quantity !== undefined) { - delete Product.quantity; + toProductItem: function (product, impression) { + const ProductFieldObject = ['id', 'name', 'brand', 'category', 'variant', 'price', 'quantity', 'coupon', 'list_name', 'list_position', 'price']; + let productItem = {}; + if (product != null) { + if (impression && product.quantity !== undefined) { + delete product.quantity; } - for (var productKey in Product) { - for (var i = 0; i < ProductFieldObject.length; i++) { - if (productKey.toLowerCase() == ProductFieldObject[i]) { - if (Product[productKey] != null) { - Products[productKey.toLowerCase()] = Product[productKey]; + for (const [key, value] of Object.entries(product)) { + for (let i = 0; i < ProductFieldObject.length; i++) { + if (key.toLowerCase() == ProductFieldObject[i]) { + if (value != null) { + productItem[ProductFieldObject[i]] = value; } - } } - } } + return productItem; + }, + + toOrderItem: function (order) { + const orderFieldObject = ['transaction_id', 'affiliation', 'value', 'tax', 'shipping', 'coupon', 'items', 'checkout_step', 'checkout_option']; + let orderItem = {}; - if (Order != null) { - for (var orderKey in Order) { - for (var j = 0; j < OrderFieldObject.length; j++) { - if (orderKey.toLowerCase() == OrderFieldObject[j]) { - Orders[orderKey.toLowerCase()] = Order[orderKey]; + if (order != null) { + for (const [key, value] of Object.entries(order)) { + for (let j = 0; j < orderFieldObject.length; j++) { + if (key.toLowerCase() == orderFieldObject[j]) { + orderItem[orderFieldObject[j]] = value; } } } } - - if (Impression) { - ga('ec:addImpression', Products); - } else { - ga('ec:addProduct', Products); - } + return orderItem; }, - addProductDetailView: function (Product) { - this.add(Product); - ga('ec:setAction', 'detail'); - ga('send', 'event', 'UX', 'detail', 'Product Detail View', {'nonInteraction': 1}); + viewItemList: function (products) { + let productItems = []; + for (let product of products) + productItems.push(this.toProductItem(product)); + gtag('event', 'view_item_list', {'items': productItems}); }, - addToCart: function (Product) { - this.add(Product); - ga('ec:setAction', 'add'); - ga('send', 'event', 'UX', 'click', 'Add to Cart'); // Send data using an event. + addProductDetailView: function (product) { + let productItems = [this.toProductItem(product)]; + gtag('event', 'view_item', {'items': productItems, 'non_interaction': true}); }, - removeFromCart: function (Product) { - this.add(Product); - ga('ec:setAction', 'remove'); - ga('send', 'event', 'UX', 'click', 'Remove From cart'); // Send data using an event. + addToCart: function (product) { + let productItems = [this.toProductItem(product)]; + gtag('event', 'add_to_cart', {'items': productItems}); }, - addProductImpression: function (Product) { - //ga('send', 'pageview'); + removeFromCart: function (product) { + let productItems = [this.toProductItem(product)]; + gtag('event', 'remove_from_cart', {'items': productItems}); }, - /** - id, type, affiliation, revenue, tax, shipping and coupon. - **/ - refundByOrderId: function (Order) { - /** - * Refund an entire transaction. - **/ - ga('ec:setAction', 'refund', { - 'id': Order.id // Transaction ID is only required field for full refund. - }); - ga('send', 'event', 'Ecommerce', 'Refund', {'nonInteraction': 1}); + refundByOrderId: function (orderId) { + gtag('event', 'refund', {"transaction_id": orderId, 'non_interaction': true}) }, - refundByProduct: function (Order) { - /** - * Refund a single product. - **/ - //this.add(Product); - - ga('ec:setAction', 'refund', { - 'id': Order.id, // Transaction ID is required for partial refund. - }); - ga('send', 'event', 'Ecommerce', 'Refund', {'nonInteraction': 1}); - }, - - addProductClick: function (Product) { - var ClickPoint = jQuery('a[href$="' + Product.url + '"].quick-view'); - - ClickPoint.on("click", function () { - GoogleAnalyticEnhancedECommerce.add(Product); - ga('ec:setAction', 'click', { - list: Product.list - }); - - ga('send', 'event', 'Product Quick View', 'click', Product.list, { - 'hitCallback': function () { - return !ga.loaded; + addProductClick: function (product) { + let clickPoint = $('a[href$="' + product.url + '"].quick-view'); + clickPoint.on("click", function () { + const productItems = [GoogleAnalyticEnhancedECommerce.toProductItem(product)]; + gtag('event', 'select_content', { + 'items': productItems, + 'event_callback': function () { + return !gtag.loaded; } }); }); - }, - addProductClickByHttpReferal: function (Product) { - this.add(Product); - ga('ec:setAction', 'click', { - list: Product.list + addProductClickByHttpReferal: function (product) { + let productItems = [this.toProductItem(product)]; + gtag('event', 'select_content', { + 'items': productItems, + 'event_callback': function () { + return !gtag.loaded; + }, + 'non_interaction': true }); - - ga('send', 'event', 'Product Click', 'click', Product.list, { - 'nonInteraction': 1, - 'hitCallback': function () { - return !ga.loaded; - } - }); - }, - addTransaction: function (Order) { + addTransaction: function (order,products) { + let purchase = this.toOrderItem(order); + purchase.event_callback = function () { + $.get(order.url, { + orderid: order.id, + customer: order.customer + }) + }; + gtag('event', 'purchase', purchase); + }, - //this.add(Product); - ga('ec:setAction', 'purchase', Order); - ga('send', 'event', 'Transaction', 'purchase', { - 'hitCallback': function () { - $.get(Order.url, { - orderid: Order.id, - customer: Order.customer - }); - } - }); + addCheckout: function (step, products) { + let productItems = []; + for (let product of products) + productItems.push(this.toProductItem(product)); + if (step == 0) { + gtag('event', 'begin_checkout', {'items': productItems}); + } else { + gtag('event', 'checkout_progress', {'items': productItems}); + } }, - addCheckout: function (Step) { - ga('ec:setAction', 'checkout', { - 'step': Step - //'option':'Visa' + addCheckoutOption: function (step, option, value) { + gtag('event', 'set_checkout_option', { + "checkout_step": step, + "checkout_option": option, + "value": value }); - ga('send', 'pageview'); - }, - addCheckoutOption: function (Step, Option) { - ga('ec:setAction', 'checkout_option', { - 'step': Step, - 'option': Option - }); - ga('send', 'event', 'Checkout', 'Option'); } }; diff --git a/views/templates/hook/googleanalytics.tpl b/views/templates/hook/googleanalytics.tpl new file mode 100644 index 0000000..e7446ed --- /dev/null +++ b/views/templates/hook/googleanalytics.tpl @@ -0,0 +1,21 @@ + + + From b6ad24d28199ffe5213105fbd1aca0eeb4b21f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Ag=C3=BC=C3=AD?= Date: Fri, 24 Jan 2020 09:43:48 +0100 Subject: [PATCH 5/7] uses camel case for smarty variables sends userid only on non-backoffice pages renames method for getting the gaAccountId --- ganalytics.php | 12 ++++++------ views/templates/hook/googleanalytics.tpl | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ganalytics.php b/ganalytics.php index 51c2286..d3fd20c 100755 --- a/ganalytics.php +++ b/ganalytics.php @@ -240,7 +240,7 @@ public function getContent() return $this->display(__FILE__, 'views/templates/admin/configuration.tpl') . $output; } - protected function _getMeasurementId() + protected function _getGaAccountId() { return Tools::safeOutput(Configuration::get('GA_ACCOUNT_ID')); } @@ -256,9 +256,9 @@ protected function _getGoogleAnalyticsTag($back_office = false) $this->smarty->assign( array( - 'back_office' => $back_office, - 'user_id' => $user_id, - 'ga_measurement_id' => $this->_getMeasurementId(), + 'backOffice' => $back_office, + 'userId' => $user_id, + 'gaAccountId' => $this->_getGaAccountId(), ) ); return $this->display(__FILE__, 'googleanalytics.tpl'); @@ -596,7 +596,7 @@ protected function _runJs($js_code, $backoffice = 0) if (!empty($js_code)) { if ($backoffice) { - $js_code .= 'MBG.setCampaign(\'backoffice-orders\',\'backoffice\',\'cms\',\'' . $this->_getMeasurementId() . '\');'; + $js_code .= 'MBG.setCampaign(\'backoffice-orders\',\'backoffice\',\'cms\',\'' . $this->_getGaAccountId() . '\');'; } $runjs_code .= ' @@ -605,7 +605,7 @@ protected function _runJs($js_code, $backoffice = 0) var MBG = GoogleAnalyticEnhancedECommerce; MBG.setCurrency( \'' . Tools::safeOutput($this->context->currency->iso_code) . '\', - \'' . $this->_getMeasurementId() . '\'); + \'' . $this->_getGaAccountId() . '\'); ' . $js_code . ' }); '; diff --git a/views/templates/hook/googleanalytics.tpl b/views/templates/hook/googleanalytics.tpl index e7446ed..0936eed 100644 --- a/views/templates/hook/googleanalytics.tpl +++ b/views/templates/hook/googleanalytics.tpl @@ -1,4 +1,4 @@ - + From a84182e76a1aaf95ac740803c359790e1ff0046e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Ag=C3=BC=C3=AD?= Date: Fri, 24 Jan 2020 10:12:34 +0100 Subject: [PATCH 6/7] configures anonymized IP --- ganalytics.php | 29 ++++++++++++++++++++++++ views/templates/hook/googleanalytics.tpl | 8 +++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/ganalytics.php b/ganalytics.php index d3fd20c..f9c1f45 100755 --- a/ganalytics.php +++ b/ganalytics.php @@ -195,6 +195,24 @@ public function displayForm() ), ), ), + array( + 'type' => 'radio', + 'label' => $this->l('Anonymize IP'), + 'name' => 'GA_ANONYMIZE_ENABLED', + 'hint' => $this->l('Use this option to anonymize the visitor’s IP to comply with data privacy laws in some countries'), + 'values' => array( + array( + 'id' => 'ga_anonymize_enabled', + 'value' => 1, + 'label' => $this->l('Enabled') + ), + array( + 'id' => 'ga_anonymize_disabled', + 'value' => 0, + 'label' => $this->l('Disabled') + ), + ), + ), ), 'submit' => array( 'title' => $this->l('Save'), @@ -204,6 +222,7 @@ public function displayForm() // Load current value $helper->fields_value['GA_ACCOUNT_ID'] = Configuration::get('GA_ACCOUNT_ID'); $helper->fields_value['GA_USERID_ENABLED'] = Configuration::get('GA_USERID_ENABLED'); + $helper->fields_value['GA_ANONYMIZE_ENABLED'] = Configuration::get('GA_ANONYMIZE_ENABLED'); return $helper->generateForm($fields_form); } @@ -221,11 +240,18 @@ public function getContent() Configuration::updateValue('GANALYTICS_CONFIGURATION_OK', true); $output .= $this->displayConfirmation($this->l('Account ID updated successfully')); } + $ga_userid_enabled = Tools::getValue('GA_USERID_ENABLED'); if (null !== $ga_userid_enabled) { Configuration::updateValue('GA_USERID_ENABLED', (bool)$ga_userid_enabled); $output .= $this->displayConfirmation($this->l('Settings for User ID updated successfully')); } + + $ga_anonymize_enabled = Tools::getValue('GA_ANONYMIZE_ENABLED'); + if (null !== $ga_anonymize_enabled) { + Configuration::updateValue('GA_ANONYMIZE_ENABLED', (bool)$ga_anonymize_enabled); + $output .= $this->displayConfirmation($this->l('Settings for Anonymize IP updated successfully')); + } } if (version_compare(_PS_VERSION_, '1.5', '>=')) @@ -254,11 +280,14 @@ protected function _getGoogleAnalyticsTag($back_office = false) $user_id = (int)$this->context->customer->id; } + $ga_anonymize_enabled = Configuration::get('GA_ANONYMIZE_ENABLED'); + $this->smarty->assign( array( 'backOffice' => $back_office, 'userId' => $user_id, 'gaAccountId' => $this->_getGaAccountId(), + 'gaAnonymizeEnabled' => $ga_anonymize_enabled, ) ); return $this->display(__FILE__, 'googleanalytics.tpl'); diff --git a/views/templates/hook/googleanalytics.tpl b/views/templates/hook/googleanalytics.tpl index 0936eed..87d8d6f 100644 --- a/views/templates/hook/googleanalytics.tpl +++ b/views/templates/hook/googleanalytics.tpl @@ -12,10 +12,14 @@ {if isset($backOffice) && $backOffice} gtag('config', '{$gaAccountId|escape:'htmlall':'UTF-8'}', { 'send_page_view': false } ); {else} - gtag('config', '{$gaAccountId|escape:'htmlall':'UTF-8'}'); + let options = { }; {if isset($userId)} - gtag('config', '{$gaAccountId|escape:'htmlall':'UTF-8'}', { 'user_id': '{$userId|escape:'htmlall':'UTF-8'}' } ); + options['user_id'] = '{$userId|escape:'htmlall':'UTF-8'}'; {/if} + {if $gaAnonymizeEnabled} + options['anonymize_ip'] = true; + {/if} + gtag('config', '{$gaAccountId|escape:'htmlall':'UTF-8'}', options ); {/if} From eef56e146933380e180c778123f15d44998dc768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Ag=C3=BC=C3=AD?= Date: Mon, 27 Jan 2020 10:54:33 +0100 Subject: [PATCH 7/7] uses smarty delimiters instead of curly braces surrounded with spaces --- views/templates/hook/googleanalytics.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/views/templates/hook/googleanalytics.tpl b/views/templates/hook/googleanalytics.tpl index 87d8d6f..17d89f8 100644 --- a/views/templates/hook/googleanalytics.tpl +++ b/views/templates/hook/googleanalytics.tpl @@ -10,16 +10,16 @@ gtag('js', new Date()); {/literal} {if isset($backOffice) && $backOffice} - gtag('config', '{$gaAccountId|escape:'htmlall':'UTF-8'}', { 'send_page_view': false } ); + gtag('config', '{$gaAccountId|escape:'htmlall':'UTF-8'}', {ldelim}'send_page_view': false{rdelim}); {else} - let options = { }; + let options = {ldelim} {rdelim}; {if isset($userId)} options['user_id'] = '{$userId|escape:'htmlall':'UTF-8'}'; {/if} {if $gaAnonymizeEnabled} options['anonymize_ip'] = true; {/if} - gtag('config', '{$gaAccountId|escape:'htmlall':'UTF-8'}', options ); + gtag('config', '{$gaAccountId|escape:'htmlall':'UTF-8'}', options); {/if}