Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

View File

@@ -0,0 +1,138 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Database;
use Configuration;
use Db;
use Language;
use Ps_Googleanalytics;
use Shop;
use Tab;
class Install
{
/**
* @var Ps_Googleanalytics
*/
private $module;
public function __construct(Ps_Googleanalytics $module)
{
if (Shop::isFeatureActive()) {
Shop::setContext(Shop::CONTEXT_ALL);
}
$this->module = $module;
}
/**
* installTables
*
* @return bool
*/
public function installTables()
{
$sql = [];
$sql[] = '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,
`refund_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';
$sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'ganalytics_data` (
`id_cart` int(11) NOT NULL,
`id_shop` int(11) NOT NULL,
`data` TEXT DEFAULT NULL,
PRIMARY KEY (`id_cart`)
) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8';
foreach ($sql as $query) {
if (!Db::getInstance()->execute($query)) {
return false;
}
}
return true;
}
/**
* Insert default data to database
*
* @return bool
*/
public function setDefaultConfiguration()
{
Configuration::updateValue('GA_CANCELLED_STATES', json_encode([Configuration::get('PS_OS_CANCELED')]));
Configuration::updateValue('GA_BACKLOAD_ENABLED', false);
Configuration::updateValue('GA_BACKLOAD_DAYS', 30);
return true;
}
/**
* Register Module hooks
*
* @return bool
*/
public function registerHooks()
{
return $this->module->registerHook('displayHeader') &&
$this->module->registerHook('displayAdminOrder') &&
$this->module->registerHook('displayBeforeBodyClosingTag') &&
$this->module->registerHook('displayFooterProduct') &&
$this->module->registerHook('displayOrderConfirmation') &&
$this->module->registerHook('actionProductCancel') &&
$this->module->registerHook('actionValidateOrder') &&
$this->module->registerHook('actionOrderStatusPostUpdate') &&
$this->module->registerHook('actionCartUpdateQuantityBefore') &&
$this->module->registerHook('actionObjectProductInCartDeleteBefore') &&
$this->module->registerHook('displayBackOfficeHeader') &&
$this->module->registerHook('actionCarrierProcess');
}
/**
* Installs hidden tab for our ajax controller
*
* @return bool
*/
public function installTab()
{
$tab = new Tab();
$tab->class_name = 'AdminGanalyticsAjax';
$tab->module = $this->module->name;
$tab->active = true;
$tab->id_parent = -1;
$tab->name = array_fill_keys(
Language::getIDs(false),
$this->module->displayName
);
return $tab->add();
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Database;
use Db;
use Tab;
use Validate;
class Uninstall
{
/**
* uninstallTables
*
* @return bool
*/
public function uninstallTables()
{
$sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'ganalytics`';
$sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'ganalytics_data`';
foreach ($sql as $query) {
if (!Db::getInstance()->execute($query)) {
return false;
}
}
return true;
}
/**
* uninstall tab
*
* @return bool
*/
public function uninstallTab()
{
$result = true;
$id_tab = (int) Tab::getIdFromClassName('AdminGanalyticsAjax');
$tab = new Tab($id_tab);
if (Validate::isLoadedObject($tab)) {
$result = $tab->delete();
}
return $result;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header('Expires: Mon, 26 Jul 1998 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,260 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Form;
use Configuration;
use Context;
use HelperForm;
use OrderState;
use Ps_Googleanalytics;
use Tools;
class ConfigurationForm
{
private $module;
public function __construct(Ps_Googleanalytics $module)
{
$this->module = $module;
}
/**
* generate
*
* @return string
*/
public function generate()
{
// Get default language
$default_lang = (int) Configuration::get('PS_LANG_DEFAULT');
$helper = new HelperForm();
// Module, token and currentIndex
$helper->module = $this->module;
$helper->name_controller = $this->module->name;
$helper->token = Tools::getAdminTokenLite('AdminModules');
$helper->currentIndex = Context::getContext()->link->getAdminLink('AdminModules', true, [], [
'configure' => $this->module->name,
'tab_module' => $this->module->tab,
'module_name' => $this->module->name,
]);
// Language
$helper->default_form_language = $default_lang;
$helper->allow_employee_form_lang = $default_lang;
// Title and toolbar
$helper->title = $this->module->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->module->name;
$helper->toolbar_btn = [
'save' => [
'desc' => $this->module->getTranslator()->trans('Save', [], 'Modules.Googleanalytics.Admin'),
'href' => Context::getContext()->link->getAdminLink('AdminModules', true, [], [
'configure' => $this->module->name,
'tab_module' => $this->module->tab,
'module_name' => $this->module->name,
'save' => $this->module->name,
]),
],
'back' => [
'href' => Context::getContext()->link->getAdminLink('AdminModules', true),
'desc' => $this->module->getTranslator()->trans('Back to list', [], 'Modules.Googleanalytics.Admin'),
],
];
$fields_form = [];
// Init Fields form array
$fields_form[0]['form'] = [
'legend' => [
'title' => $this->module->getTranslator()->trans('Settings', [], 'Modules.Googleanalytics.Admin'),
],
'input' => [
[
'type' => 'text',
'label' => $this->module->getTranslator()->trans('Google Analytics Tracking ID', [], 'Modules.Googleanalytics.Admin'),
'name' => 'GA_ACCOUNT_ID',
'size' => 20,
'required' => true,
'desc' => $this->module->getTranslator()->trans('This information is available in your Google Analytics account. Google Analytics 4 tracking ID starts with "G-".', [], 'Modules.Googleanalytics.Admin'),
],
[
'type' => 'switch',
'label' => $this->module->getTranslator()->trans('Enable User ID tracking', [], 'Modules.Googleanalytics.Admin'),
'name' => 'GA_USERID_ENABLED',
'desc' => $this->module->getTranslator()->trans('This option adds unique user ID to the tag to better track the customer. Use this option only if it complies with laws in your country.', [], 'Modules.Googleanalytics.Admin'),
'values' => [
[
'id' => 'ga_userid_enabled',
'value' => 1,
'label' => $this->module->getTranslator()->trans('Yes', [], 'Modules.Googleanalytics.Admin'),
],
[
'id' => 'ga_userid_disabled',
'value' => 0,
'label' => $this->module->getTranslator()->trans('No', [], 'Modules.Googleanalytics.Admin'),
],
],
],
[
'type' => 'switch',
'label' => $this->module->getTranslator()->trans('Anonymize IP', [], 'Modules.Googleanalytics.Admin'),
'name' => 'GA_ANONYMIZE_ENABLED',
'desc' => $this->module->getTranslator()->trans('Use this option to anonymize the visitors IP to comply with data privacy laws in some countries', [], 'Modules.Googleanalytics.Admin'),
'values' => [
[
'id' => 'ga_anonymize_enabled',
'value' => 1,
'label' => $this->module->getTranslator()->trans('Yes', [], 'Modules.Googleanalytics.Admin'),
],
[
'id' => 'ga_anonymize_disabled',
'value' => 0,
'label' => $this->module->getTranslator()->trans('No', [], 'Modules.Googleanalytics.Admin'),
],
],
],
[
'type' => 'switch',
'label' => $this->module->getTranslator()->trans('Enable Back Office Tracking', [], 'Modules.Googleanalytics.Admin'),
'name' => 'GA_TRACK_BACKOFFICE_ENABLED',
'desc' => $this->module->getTranslator()->trans('Use this option to enable the tracking inside the Back Office', [], 'Modules.Googleanalytics.Admin'),
'values' => [
[
'id' => 'ga_track_backoffice',
'value' => 1,
'label' => $this->module->getTranslator()->trans('Yes', [], 'Modules.Googleanalytics.Admin'),
],
[
'id' => 'ga_do_not_track_backoffice',
'value' => 0,
'label' => $this->module->getTranslator()->trans('No', [], 'Modules.Googleanalytics.Admin'),
],
],
],
[
'type' => 'select',
'label' => $this->module->getTranslator()->trans('Canceled order states', [], 'Modules.Googleanalytics.Admin'),
'name' => 'GA_CANCELLED_STATES',
'desc' => $this->module->getTranslator()->trans('Choose order states in which you consider the given order canceled. This will usually be the default "Canceled" state, but some stores may have extra states like "Returned", etc.', [], 'Modules.Googleanalytics.Admin'),
'class' => 'chosen',
'multiple' => true,
'options' => [
'query' => OrderState::getOrderStates((int) Context::getContext()->language->id),
'id' => 'id_order_state',
'name' => 'name',
],
],
[
'type' => 'switch',
'label' => $this->module->getTranslator()->trans('Re-send failed orders', [], 'Modules.Googleanalytics.Admin'),
'name' => 'GA_BACKLOAD_ENABLED',
'desc' => $this->module->getTranslator()->trans('This option will resend all orders that failed to be sent normally in front-office, due to failures or ad-blockers.', [], 'Modules.Googleanalytics.Admin'),
'values' => [
[
'id' => 'ga_backload_enabled',
'value' => 1,
'label' => $this->module->getTranslator()->trans('Yes', [], 'Modules.Googleanalytics.Admin'),
],
[
'id' => 'ga_backload_enabled',
'value' => 0,
'label' => $this->module->getTranslator()->trans('No', [], 'Modules.Googleanalytics.Admin'),
],
],
],
[
'type' => 'text',
'label' => $this->module->getTranslator()->trans('Failed orders period', [], 'Modules.Googleanalytics.Admin'),
'name' => 'GA_BACKLOAD_DAYS',
'class' => 'input fixed-width-md',
'suffix' => 'days',
'desc' => $this->module->getTranslator()->trans('If you want to resend failed orders, specify how many days back the module should look for them. Default: 30.', [], 'Modules.Googleanalytics.Admin'),
],
],
'submit' => [
'title' => $this->module->getTranslator()->trans('Save', [], 'Modules.Googleanalytics.Admin'),
],
];
// Load current value
$helper->fields_value['GA_ACCOUNT_ID'] = Configuration::get('GA_ACCOUNT_ID');
$helper->fields_value['GA_USERID_ENABLED'] = (bool) Configuration::get('GA_USERID_ENABLED');
$helper->fields_value['GA_ANONYMIZE_ENABLED'] = (bool) Configuration::get('GA_ANONYMIZE_ENABLED');
$helper->fields_value['GA_TRACK_BACKOFFICE_ENABLED'] = (bool) Configuration::get('GA_TRACK_BACKOFFICE_ENABLED');
$helper->fields_value['GA_CANCELLED_STATES[]'] = json_decode(Configuration::get('GA_CANCELLED_STATES'), true);
$helper->fields_value['GA_BACKLOAD_ENABLED'] = (bool) Configuration::get('GA_BACKLOAD_ENABLED');
$helper->fields_value['GA_BACKLOAD_DAYS'] = (int) Configuration::get('GA_BACKLOAD_DAYS');
return $helper->generateForm($fields_form);
}
/**
* treat the form datas if submited
*
* @return string
*/
public function treat()
{
$gaAccountId = Tools::getValue('GA_ACCOUNT_ID');
$gaUserIdEnabled = Tools::getValue('GA_USERID_ENABLED');
$gaAnonymizeEnabled = Tools::getValue('GA_ANONYMIZE_ENABLED');
$gaTrackBackOffice = Tools::getValue('GA_TRACK_BACKOFFICE_ENABLED');
$gaCancelledStates = Tools::getValue('GA_CANCELLED_STATES');
$gaBackloadEnabled = Tools::getValue('GA_BACKLOAD_ENABLED');
$gaBackloadDays = Tools::getValue('GA_BACKLOAD_DAYS');
if (!empty($gaAccountId)) {
Configuration::updateValue('GA_ACCOUNT_ID', $gaAccountId);
Configuration::updateValue('GANALYTICS_CONFIGURATION_OK', true);
}
if (null !== $gaUserIdEnabled) {
Configuration::updateValue('GA_USERID_ENABLED', (bool) $gaUserIdEnabled);
}
if (null !== $gaAnonymizeEnabled) {
Configuration::updateValue('GA_ANONYMIZE_ENABLED', (bool) $gaAnonymizeEnabled);
}
if (null !== $gaTrackBackOffice) {
Configuration::updateValue('GA_TRACK_BACKOFFICE_ENABLED', (bool) $gaTrackBackOffice);
}
if (null !== $gaBackloadEnabled) {
Configuration::updateValue('GA_BACKLOAD_ENABLED', (bool) $gaBackloadEnabled);
}
if (null !== $gaBackloadDays) {
Configuration::updateValue('GA_BACKLOAD_DAYS', (int) $gaBackloadDays);
}
if ($gaCancelledStates === false) {
Configuration::updateValue('GA_CANCELLED_STATES', '');
} else {
Configuration::updateValue('GA_CANCELLED_STATES', json_encode($gaCancelledStates));
}
return $this->module->displayConfirmation($this->module->getTranslator()->trans('Settings updated successfully.', [], 'Modules.Googleanalytics.Admin'));
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header('Expires: Mon, 26 Jul 1998 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,115 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics;
use Configuration;
class GoogleAnalyticsTools
{
/**
* Renders purchase event for order
*
* @param array $orderProducts
* @param array $orderData
* @param string $callbackUrl
*
* @return string|void
*/
public function renderPurchaseEvent($orderProducts, $orderData, $callbackUrl)
{
if (!is_array($orderProducts)) {
return;
}
$callbackData = [
'orderid' => $orderData['transaction_id'],
'customer' => $orderData['customer'],
];
$eventData = [
'transaction_id' => (int) $orderData['transaction_id'],
'affiliation' => $orderData['affiliation'],
'value' => (float) $orderData['value'],
'tax' => (float) $orderData['tax'],
'shipping' => (float) $orderData['shipping'],
'currency' => $orderData['currency'],
'items' => $orderProducts,
'event_callback' => "function() {
$.get('" . $callbackUrl . "', " . json_encode($callbackData, JSON_UNESCAPED_UNICODE) . ');
}',
];
return $this->renderEvent(
'purchase',
$eventData,
['event_callback']
);
}
/**
* Encodes array of data into JSON, optionally ignoring some of the values
*
* @param array $data Data pairs
* @param array $ignoredKeys Values of these keys won't be encoded, for literal output of functions
*
* @return string json encoded data
*/
public function jsonEncodeWithBlacklist($data, $ignoredKeys = [])
{
$return = [];
foreach ($data as $k => $v) {
if (in_array($k, $ignoredKeys)) {
$return[] = json_encode($k, JSON_UNESCAPED_UNICODE) . ': ' . $v;
} else {
$return[] = json_encode($k, JSON_UNESCAPED_UNICODE) . ': ' . json_encode($v, JSON_UNESCAPED_UNICODE);
}
}
return '{' . implode(', ', $return) . '}';
}
/**
* Renders gtag event and encodes the data. You can optionally pass which data keys you want to
* output in a raw way - callbacks for example.
*
* @param string $eventName
* @param array $eventData
* @param array $ignoredKeys Values of these keys won't be encoded, for literal output of functions
*
* @return string render gtag event for output
*/
public function renderEvent($eventName, $eventData, $ignoredKeys = [])
{
// Automatically add send_to parameter to all events to avoid sending extra events
// to other gtag configs (Ads for example).
$eventData = array_merge(
['send_to' => Configuration::get('GA_ACCOUNT_ID')],
$eventData
);
return sprintf(
'gtag("event", "%1$s", %2$s);',
$eventName,
$this->jsonEncodeWithBlacklist($eventData, $ignoredKeys)
);
}
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Handler;
use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsDataRepository;
class GanalyticsDataHandler
{
private $ganalyticsDataRepository;
private $cartId;
private $shopId;
/**
* __construct
*
* @param int $cartId
* @param int $shopId
*/
public function __construct($cartId, $shopId)
{
$this->ganalyticsDataRepository = new GanalyticsDataRepository();
$this->cartId = (int) $cartId;
$this->shopId = (int) $shopId;
}
/**
* readData
*
* @return array
*/
public function readData()
{
$dataReturned = $this->ganalyticsDataRepository->findDataByCartIdAndShopId(
$this->cartId,
$this->shopId
);
if (false === $dataReturned) {
return [];
}
return $this->jsonDecodeValidJson($dataReturned);
}
/**
* Deletes all persisted data, probably because it was flushed.
*
* @return bool
*/
public function deleteData()
{
return $this->ganalyticsDataRepository->deleteRow(
$this->cartId,
$this->shopId
);
}
/**
* Stores event into data repository so we can output it
* on first available chance.
*
* @param string $dataToPersist
*
* @return bool
*/
public function persistData($dataToPersist)
{
// Try to get current data
$currentData = $this->readData();
// If no data has been persisted yet, we create a new array, otherwise
// we add it to the previous events stored.
if (!empty($currentData)) {
$newData = $currentData;
$newData[] = $dataToPersist;
} else {
$newData = [$dataToPersist];
}
return $this->ganalyticsDataRepository->addNewRow(
(int) $this->cartId,
(int) $this->shopId,
json_encode($newData)
);
}
/**
* Check if the json is valid and returns an empty array if not
*
* @param string $json
*
* @return array
*/
protected function jsonDecodeValidJson($json)
{
$array = json_decode($json, true);
if (JSON_ERROR_NONE === json_last_error()) {
return $array;
}
return [];
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Handler;
use Configuration;
use Context;
use Ps_Googleanalytics;
class GanalyticsJsHandler
{
private $module;
private $context;
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* Generate Google Analytics js
*
* @param string $jsCode
*
* @return void|string
*/
public function generate($jsCode)
{
if (Configuration::get('GA_ACCOUNT_ID')) {
$this->context->smarty->assign(
[
'jsCode' => $jsCode,
]
);
return $this->module->display(
$this->module->getLocalPath() . $this->module->name,
'ga_tag.tpl'
);
}
}
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Handler;
use Module;
class ModuleHandler
{
/**
* @param string $moduleName
*
* @return bool
*/
public function isModuleEnabled($moduleName)
{
$module = Module::getInstanceByName($moduleName);
if (!($module instanceof Module)) {
return false;
}
if (false === Module::isInstalled($moduleName)) {
return false;
}
if (false === $module->active) {
return false;
}
return true;
}
/**
* @param string $moduleName
* @param string $hookName
*
* @return bool
*/
public function isModuleEnabledAndHookedOn($moduleName, $hookName)
{
$module = Module::getInstanceByName($moduleName);
if (false === $this->isModuleEnabled($moduleName)) {
return false;
}
return $module->isRegisteredInHook($hookName);
}
/**
* @param string $moduleName
*
* @return bool
*/
public function uninstallModule($moduleName)
{
if (false === Module::isInstalled($moduleName)) {
return false;
}
$oldModule = Module::getInstanceByName($moduleName);
if (!($oldModule instanceof Module)) {
return false;
}
if (method_exists($oldModule, 'uninstallTab')) {
$oldModule->uninstallTab();
}
// This closure calls the parent class to prevent data to be erased
$parentUninstallClosure = function () {
return parent::uninstall();
};
$parentUninstallClosure = $parentUninstallClosure->bindTo($oldModule, get_class($oldModule));
return $parentUninstallClosure();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header('Expires: Mon, 26 Jul 1998 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,80 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Context;
use PrestaShop\Module\Ps_Googleanalytics\Repository\CarrierRepository;
use Ps_Googleanalytics;
class HookActionCarrierProcess implements HookInterface
{
private $module;
private $context;
private $params;
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* run
*
* @return void
*/
public function run()
{
if (isset($this->params['cart']->id_carrier)) {
$carrierRepository = new CarrierRepository();
// Load carrier name
$carrierName = (string) $carrierRepository->findByCarrierId((int) $this->params['cart']->id_carrier);
// Check if we actually have some name
if (empty($carrierName)) {
return;
}
// Prepare and render the event
$eventData = [
'currency' => $this->context->currency->iso_code,
'value' => (float) $this->context->cart->getSummaryDetails()['total_price'],
'shipping_tier' => $carrierName,
];
$jsCode = $this->module->getTools()->renderEvent(
'add_shipping_info',
$eventData
);
// Store it into our repository so we can flush it on next page load
$this->module->getDataHandler()->persistData($jsCode);
}
}
/**
* @param array $params
*/
public function setParams($params)
{
$this->params = $params;
}
}

View File

@@ -0,0 +1,106 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Context;
use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper;
use Product;
use Ps_Googleanalytics;
class HookActionCartUpdateQuantityBefore implements HookInterface
{
private $module;
/**
* @var Context
*/
private $context;
private $params;
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* run
*
* @return void
*/
public function run()
{
/*
* The hook passes a legacy Product object to add, but no attribute information.
* But thankfully, we can use id_product_attribute for this.
*
* Other info is fairly standard:
*
* Add to cart from product page + up from cart page
* $this->params['operator'] == up
* $this->params['quantity'] to determine quantity
*
* Down from cart page
* $this->params['operator'] == down
* $this->params['quantity'] to determine quantity
*/
// Format product and standardize ID
$product = (array) $this->params['product'];
$product['id_product'] = $product['id'];
// Add information about attribute
if (!empty($this->params['id_product_attribute'])) {
$product['id_product_attribute'] = (int) $this->params['id_product_attribute'];
}
// Get some basic information
$product = Product::getProductProperties($this->context->language->id, $product);
// Add information about quantity difference
$product['quantity'] = (int) $this->params['quantity'];
// Prepare it and format it for our purpose
$productWrapper = new ProductWrapper($this->context);
$item = $productWrapper->prepareItemFromProduct($product, true);
// Prepare and render event
$eventData = [
'currency' => $this->context->currency->iso_code,
'value' => $item['price'] * $item['quantity'],
'items' => [$item],
];
$jsCode = $this->module->getTools()->renderEvent(
$this->params['operator'] == 'up' ? 'add_to_cart' : 'remove_from_cart',
$eventData
);
// Store this event
$this->module->getDataHandler()->persistData($jsCode);
}
/**
* @param array $params
*/
public function setParams($params)
{
$this->params = $params;
}
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Context;
use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper;
use Product;
use Ps_Googleanalytics;
use Validate;
class HookActionObjectProductInCartDeleteBefore implements HookInterface
{
private $module;
/**
* @var Context
*/
private $context;
private $params;
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* run
*
* @return void
*/
public function run()
{
// Format product and standardize ID
$product = new Product((int) $this->params['id_product'], false, (int) $this->context->language->id);
if (!Validate::isLoadedObject($product)) {
return;
}
$product = (array) $product;
$product['id_product'] = $product['id'];
// Get some basic information
$product = Product::getProductProperties($this->context->language->id, $product);
// Add information about attribute
if (!empty($this->params['id_product_attribute'])) {
$product['id_product_attribute'] = (int) $this->params['id_product_attribute'];
}
// Prepare it and format it for our purpose
$productWrapper = new ProductWrapper($this->context);
$item = $productWrapper->prepareItemFromProduct($product, false);
// Prepare and render event
$eventData = [
'currency' => $this->context->currency->iso_code,
'value' => $item['price'] * $item['quantity'],
'items' => [$item],
];
$jsCode = $this->module->getTools()->renderEvent(
'remove_from_cart',
$eventData
);
// Store this event
$this->module->getDataHandler()->persistData($jsCode);
}
/**
* @param array $params
*/
public function setParams($params)
{
$this->params = $params;
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Configuration;
use Context;
use Db;
use Ps_Googleanalytics;
class HookActionOrderStatusPostUpdate implements HookInterface
{
/**
* @var Ps_Googleanalytics
*/
private $module;
/**
* @var Context
*/
private $context;
/**
* @var array
*/
private $params;
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* run
*
* @return void
*/
public function run()
{
// If we do not have an order or a new order status, we return
if (empty($this->params['id_order']) || empty($this->params['newOrderStatus']->id)) {
return;
}
// We get all states in which the merchant want to have refund sent and check if the new state being set belongs there
$gaCancelledStates = json_decode(Configuration::get('GA_CANCELLED_STATES'), true);
if (empty($gaCancelledStates) || !in_array($this->params['newOrderStatus']->id, $gaCancelledStates)) {
return;
}
// We check if the refund was already sent to Google Analytics
$gaRefundSent = Db::getInstance()->getValue(
'SELECT id_order FROM `' . _DB_PREFIX_ . 'ganalytics` WHERE id_order = ' . (int) $this->params['id_order'] . ' AND refund_sent = 1'
);
// If it was not already refunded
if ($gaRefundSent === false) {
// We refund it and set the "sent" flag to true
$jsCode = $this->getGoogleAnalytics4($this->params['id_order']);
$this->context->cookie->ga_admin_refund = $jsCode;
$this->context->cookie->write();
// We save this information to database
Db::getInstance()->execute(
'UPDATE `' . _DB_PREFIX_ . 'ganalytics` SET refund_sent = 1 WHERE id_order = ' . (int) $this->params['id_order']
);
}
}
/**
* setParams
*
* @param array $params
*/
public function setParams($params)
{
$this->params = $params;
}
/**
* @param int $idOrder
*/
protected function getGoogleAnalytics4($idOrder)
{
$eventData = [
'transaction_id' => (int) $idOrder,
];
return $this->module->getTools()->renderEvent(
'refund',
$eventData
);
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Context;
use OrderDetail;
use Ps_Googleanalytics;
use Validate;
class HookActionProductCancel implements HookInterface
{
/**
* @var Ps_Googleanalytics
*/
private $module;
/**
* @var Context
*/
private $context;
private $params;
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* run
*
* @return void
*/
public function run()
{
if (!isset($this->params['id_order_detail']) || !isset($this->params['cancel_quantity'])) {
return;
}
// Display GA refund product
$orderDetail = new OrderDetail($this->params['id_order_detail']);
// Check if the hook provided us with a valid existing ID of order detail.
// An example are automatic tests, which do not provide it unfortunately.
if (!Validate::isLoadedObject($orderDetail)) {
return;
}
$idProduct = empty($orderDetail->product_attribute_id) ? $orderDetail->product_id : $orderDetail->product_id . '-' . $orderDetail->product_attribute_id;
$jsCode = $this->getGoogleAnalytics4(
(int) $this->params['order']->id,
$idProduct,
(float) $this->params['cancel_quantity'],
$orderDetail->product_name
);
$this->context->cookie->ga_admin_refund = $jsCode;
$this->context->cookie->write();
}
/**
* setParams
*
* @param array $params
*/
public function setParams($params)
{
$this->params = $params;
}
/**
* @param int $idOrder
* @param string $idProduct
* @param float $quantity
* @param string $nameProduct
*/
protected function getGoogleAnalytics4($idOrder, $idProduct, $quantity, $nameProduct)
{
$eventData = [
'transaction_id' => (int) $idOrder,
'items' => [
[
'item_id' => (int) $idProduct,
'item_name' => $nameProduct,
'quantity' => (int) $quantity,
],
],
];
return $this->module->getTools()->renderEvent(
'refund',
$eventData
);
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Context;
use Ps_Googleanalytics;
class HookActionValidateOrder implements HookInterface
{
/**
* @var Ps_Googleanalytics
*/
private $module;
/**
* @var Context
*/
private $context;
/**
* @var array
*/
private $params;
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* run
*
* @return void
*/
public function run()
{
// Check if we are creating backoffice order, we are only launching this hook when creating backoffice order
// For FO purposes, we use displayOrderConfirmation.
if (empty($this->context->controller->controller_name)
|| !in_array($this->context->controller->controller_name, ['AdminOrders', 'Admin'])) {
return;
}
// Mark this ID to immediately display it on next page load
$order = $this->params['order'];
// We are checking this, because in case of multishipping, there could be multiple orders
if (!empty($this->context->cookie->ga_admin_order)) {
$ga_admin_order = sprintf(
'%1$s,%2$s',
$this->context->cookie->ga_admin_order,
$order->id
);
} else {
$ga_admin_order = $order->id;
}
$this->context->cookie->ga_admin_order = $ga_admin_order;
$this->context->cookie->write();
}
/**
* setParams
*
* @param array $params
*/
public function setParams($params)
{
$this->params = $params;
}
}

View File

@@ -0,0 +1,184 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Cart;
use Configuration;
use Context;
use Db;
use Order;
use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler;
use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository;
use PrestaShop\Module\Ps_Googleanalytics\Wrapper\OrderWrapper;
use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper;
use Ps_Googleanalytics;
use Tools;
use Validate;
class HookDisplayBackOfficeHeader implements HookInterface
{
private $module;
private $context;
private $gaScripts = '';
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* run
*
* @return string
*/
public function run()
{
// Add assets if we are on configuration page
if (strcmp(Tools::getValue('configure'), $this->module->name) === 0) {
$this->context->controller->addCSS($this->module->getPathUri() . 'views/css/ganalytics.css');
}
// Render base tag using displayHeader hook with backoffice parameter
$this->gaScripts .= $this->module->hookDisplayHeader(null, true);
// Process manual orders instantly, we have their IDs in cookie
$this->processManualOrders();
// Backload old orders that failed to load normally
$this->processFailedOrders();
return $this->gaScripts;
}
/**
* Checks if there are any orders that failed to be sent normally through front office and processes them
*/
protected function processFailedOrders()
{
if (empty(Configuration::get('GA_BACKLOAD_ENABLED'))) {
return;
}
// Check for value on how long back we will get them
$backloadDays = (int) Configuration::get('GA_BACKLOAD_DAYS');
if ($backloadDays < 1) {
return;
}
// Get all failed orders (either not present in our table or not sent)
// We go GA_BACKLOAD_DAYS into the past and at least 30 minutes old
$failedOrders = Db::getInstance()->ExecuteS(
'SELECT DISTINCT o.id_order, g.sent FROM `' . _DB_PREFIX_ . 'orders` o
LEFT JOIN `' . _DB_PREFIX_ . GanalyticsRepository::TABLE_NAME . '` g ON o.id_order = g.id_order
WHERE (g.sent IS NULL OR g.sent = 0) AND o.date_add BETWEEN NOW() - INTERVAL ' . $backloadDays . ' DAY AND NOW() - INTERVAL 30 MINUTE'
);
// Process each failed order
foreach ($failedOrders as $row) {
$this->processOrder((int) $row['id_order']);
}
}
/**
* Checks if there are any manual orders in cookie and processes them
*/
protected function processManualOrders()
{
$adminOrders = $this->context->cookie->ga_admin_order;
if (empty($adminOrders)) {
return;
}
// Separate them by IDs and process one by one
$adminOrders = explode(',', $adminOrders);
foreach ($adminOrders as $idOrder) {
$this->processOrder((int) $idOrder);
}
// Clean up the cookie
unset($this->context->cookie->ga_admin_order);
$this->context->cookie->write();
}
/**
* Renders tracking code for given order
*
* @param int $idOrder
*/
public function processOrder($idOrder)
{
$order = new Order((int) $idOrder);
if (!Validate::isLoadedObject($order) || $order->getCurrentState() == (int) Configuration::get('PS_OS_ERROR')) {
return;
}
// Load up our handlers and repositories
$ganalyticsRepository = new GanalyticsRepository();
$gaTagHandler = new GanalyticsJsHandler($this->module, $this->context);
$productWrapper = new ProductWrapper($this->context);
$orderWrapper = new OrderWrapper($this->context);
// If it's a completely new order, add order to repository, so we can later mark it as sent
if (empty($ganalyticsRepository->findGaOrderByOrderId((int) $order->id))) {
$ganalyticsRepository->addOrder((int) $order->id, (int) $order->id_shop);
}
// If the order was already sent for some reason, don't do anything
if ($ganalyticsRepository->hasOrderBeenAlreadySent((int) $order->id)) {
return;
}
// Prepare transaction data
$orderData = $orderWrapper->wrapOrder($order);
// Empty script for the current order
$gaScripts = '';
// Prepare order products, if the cart still exists
$orderProducts = [];
$cart = new Cart($order->id_cart);
if (Validate::isLoadedObject($cart)) {
$orderProducts = $productWrapper->prepareItemListFromProductList($cart->getProducts(), true);
}
// Add payment event
$gaScripts .= $this->module->getTools()->renderEvent(
'add_payment_info',
[
'currency' => $orderData['currency'],
'value' => (float) $orderData['value'],
'payment_type' => $orderData['payment_type'],
'items' => $orderProducts,
]
);
// Render transaction code
$gaScripts .= $this->module->getTools()->renderPurchaseEvent(
$orderProducts,
$orderData,
$this->context->link->getAdminLink('AdminGanalyticsAjax')
);
$this->gaScripts .= $gaTagHandler->generate($gaScripts);
}
}

View File

@@ -0,0 +1,279 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Context;
use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler;
use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper;
use Ps_Googleanalytics;
class HookDisplayBeforeBodyClosingTag implements HookInterface
{
private $module;
private $context;
private $gaScripts = '';
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* run
*
* @return string
*/
public function run()
{
// Prepare our tag handler
$gaTagHandler = new GanalyticsJsHandler($this->module, $this->context);
// Log information about product listing
$this->saveInformationAboutListing();
// Flush events stored in data storage from previous pages
$this->outputStoredEvents();
// Add events
$this->renderProductListing();
$this->renderSearch();
$this->renderCartPage();
$this->renderBeginCheckout();
$this->renderLogin();
$this->renderRegistration();
// Output everything
return $gaTagHandler->generate($this->gaScripts);
}
/**
* This method renders tracking code for product listings, like category pages.
*/
private function renderProductListing()
{
// Try to get product list variable
$listing = $this->context->smarty->getTemplateVars('listing');
if (empty($listing['products'])) {
return;
}
// Prepare items to our format
$productWrapper = new ProductWrapper($this->context);
$items = $productWrapper->prepareItemListFromProductList($listing['products']);
// Prepare info about the list
$item_list_id = $this->context->controller->php_self;
$item_list_name = $listing['label'];
// Render the event
$eventData = [
'item_list_id' => $item_list_id,
'item_list_name' => $item_list_name,
'items' => $items,
];
$this->gaScripts .= $this->module->getTools()->renderEvent(
'view_item_list',
$eventData
);
// Render quickview events
foreach ($items as $item) {
$eventData = [
'item_list_id' => $item_list_id,
'item_list_name' => $item_list_name,
'items' => [$item],
];
// Keep only product ID if id_product_attribute was appended
$productId = explode('-', $item['item_id']);
$productId = $productId[0];
// Render the event wrapped in onclick
$this->gaScripts .= '
$(\'article[data-id-product="' . $productId . '"] a.quick-view\').on(
"click",
function() {' . $this->module->getTools()->renderEvent('select_item', $eventData) . '}
);
';
}
}
/**
* This method renders tracking code when user searches on the shop.
*/
private function renderSearch()
{
// Check if we are on search page and we have a search string
if ($this->context->controller->php_self != 'search' || empty($_GET['s'])) {
return;
}
// Render the event
$eventData = [
'search_term' => (string) $_GET['s'],
];
$this->gaScripts .= $this->module->getTools()->renderEvent(
'search',
$eventData
);
}
/**
* This method renders tracking code for product listings, like category pages.
*/
private function renderCartpage()
{
// Check if we are on cart page
if ($this->context->controller->php_self != 'cart') {
return;
}
// Try to get product list variable and check if it's not empty
$cart = $this->context->smarty->getTemplateVars('cart');
if (empty($cart['products'])) {
return;
}
// Prepare items to our format
$productWrapper = new ProductWrapper($this->context);
$items = $productWrapper->prepareItemListFromProductList($cart['products'], true);
// Render the event
$eventData = [
'currency' => $this->context->currency->iso_code,
'value' => $cart['totals']['total']['amount'],
'items' => $items,
];
$this->gaScripts .= $this->module->getTools()->renderEvent(
'view_cart',
$eventData
);
}
/**
* This method renders tracking code for product listings, like category pages.
*/
private function renderBeginCheckout()
{
// Check if we are on some supported order controller
$allowed_controllers = ['order', 'orderopc', 'checkout'];
if (!in_array($this->context->controller->php_self, $allowed_controllers)) {
return;
}
// If the user reliably came from previous page, we won't render this event
// We want to do it just for first visiting checkout
if (!empty($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['REQUEST_URI']) !== false) {
return;
}
// Try to get product list variable and check if it's not empty
$cart = $this->context->smarty->getTemplateVars('cart');
if (empty($cart['products'])) {
return;
}
// Prepare items to our format
$productWrapper = new ProductWrapper($this->context);
$items = $productWrapper->prepareItemListFromProductList($cart['products'], true);
// Render the event
$eventData = [
'currency' => $this->context->currency->iso_code,
'value' => $cart['totals']['total']['amount'],
'items' => $items,
];
$this->gaScripts .= $this->module->getTools()->renderEvent(
'begin_checkout',
$eventData
);
}
/**
* This method renders tracking code after user logs in.
*/
private function renderLogin()
{
// Render it only on login page AND if we are not creating a new account in older PS versions
// For newer versions, registrations are handled with standalone registration controller.
if ($this->context->controller->php_self != 'authentication' || isset($_GET['create_account'])) {
return;
}
// Render the event
$this->gaScripts .= $this->module->getTools()->renderEvent('login', []);
}
/**
* This method renders tracking code after user registers.
*/
private function renderRegistration()
{
if ($this->context->controller->php_self != 'registration' &&
($this->context->controller->php_self != 'authentication' || !isset($_GET['create_account']))
) {
return;
}
// Render the event
$this->gaScripts .= $this->module->getTools()->renderEvent('sign_up', []);
}
/**
* Saves information about last visited product listing, so we can later use it for select_item event.
*/
private function saveInformationAboutListing()
{
// Try to get product list variable
$listing = $this->context->smarty->getTemplateVars('listing');
if (empty($listing['products']) || empty($listing['label'])) {
return;
}
// Save this information to a cookie
$this->context->cookie->ga_last_listing = json_encode([
'item_list_url' => $_SERVER['REQUEST_URI'],
'item_list_id' => $this->context->controller->php_self,
'item_list_name' => $listing['label'],
]);
}
/**
* Outputs all events we stored into data repository during previous AJAX requests
* on previous page.
*/
private function outputStoredEvents()
{
// Get all stored events
$storedEvents = $this->module->getDataHandler()->readData();
if (empty($storedEvents)) {
return;
}
foreach ($storedEvents as $event) {
$this->gaScripts .= $event;
}
// Delete the repository because everything has been flushed
$this->module->getDataHandler()->deleteData();
}
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Context;
use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler;
use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper;
use Ps_Googleanalytics;
class HookDisplayFooterProduct implements HookInterface
{
private $module;
private $context;
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* run
*
* @return string|void
*/
public function run()
{
// Check we are really on product page
if ($this->context->controller->php_self !== 'product') {
return;
}
// Get lazy array from context
$product = $this->context->smarty->getTemplateVars('product');
if (empty($product)) {
return;
}
// Initialize tag handler
$gaTagHandler = new GanalyticsJsHandler($this->module, $this->context);
// Prepare it and format it for our purpose
$productWrapper = new ProductWrapper($this->context);
$item = $productWrapper->prepareItemFromProduct($product);
$jsCode = '';
// Prepare and render event
$eventData = [
'currency' => $this->context->currency->iso_code,
'value' => $item['price'],
'items' => [$item],
];
$jsCode .= $this->module->getTools()->renderEvent(
'view_item',
$eventData
);
// If the user got to the product page from previous page on our shop,
// we will also send select_item event.
if ($this->wasPreviousPageOurShop()) {
$eventData = [
'items' => [$item],
];
// We will also try to get the information about the last visited listing.
// We save this information into a cookie. If it's the page that got the user here,
// we will use it.
$previousListingData = $this->getLastVisitedListing();
if (!empty($previousListingData)) {
$eventData = array_merge($previousListingData, $eventData);
}
// Render the event
$jsCode .= $this->module->getTools()->renderEvent(
'select_item',
$eventData
);
}
return $gaTagHandler->generate($jsCode);
}
/**
* Checks HTTP_REFERER to see if the previous page that got user to this product
* was our shop.
*
* @return bool
*/
private function wasPreviousPageOurShop()
{
if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) !== false) {
return true;
}
return false;
}
/**
* Tries to get details of previous listing from the cookie.
*
* @return bool|array
*/
private function getLastVisitedListing()
{
// Fetch it from the cookie
$last_listing = $this->context->cookie->ga_last_listing;
if (empty($last_listing)) {
return false;
}
// Decode the data and check if it contains something sensible
$last_listing = json_decode($last_listing, true);
if (empty($last_listing['item_list_id'])) {
return false;
}
// Check if the last listing is the page the user came from
if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $last_listing['item_list_url']) !== false) {
unset($last_listing['item_list_url']);
return $last_listing;
}
return false;
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Configuration;
use Context;
use Customer;
use Ps_Googleanalytics;
use Tools;
class HookDisplayHeader implements HookInterface
{
/**
* @var Ps_Googleanalytics
*/
private $module;
/**
* @var Context
*/
private $context;
/**
* @var bool
*/
private $backOffice;
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* @return false|string
*/
public function run()
{
if (!Configuration::get('GA_ACCOUNT_ID')) {
return '';
}
// Resolve if we should add user ID into the code
$userId = null;
if (Configuration::get('GA_USERID_ENABLED')
&& $this->context->customer instanceof Customer
&& $this->context->customer->isLogged()
) {
$userId = (int) $this->context->customer->id;
}
$this->context->smarty->assign(
[
'backOffice' => $this->backOffice,
'trackBackOffice' => Configuration::get('GA_TRACK_BACKOFFICE_ENABLED'),
'userId' => $userId,
'gaAccountId' => Tools::safeOutput(Configuration::get('GA_ACCOUNT_ID')),
'gaAnonymizeEnabled' => Configuration::get('GA_ANONYMIZE_ENABLED'),
]
);
return $this->module->display(
$this->module->getLocalPath() . $this->module->name,
'ps_googleanalytics.tpl'
);
}
/**
* @param bool $backOffice
*/
public function setBackOffice($backOffice)
{
$this->backOffice = $backOffice;
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Cart;
use Configuration;
use Context;
use PrestaShop\Module\Ps_Googleanalytics\Handler\GanalyticsJsHandler;
use PrestaShop\Module\Ps_Googleanalytics\Repository\GanalyticsRepository;
use PrestaShop\Module\Ps_Googleanalytics\Wrapper\OrderWrapper;
use PrestaShop\Module\Ps_Googleanalytics\Wrapper\ProductWrapper;
use Ps_Googleanalytics;
use Validate;
class HookDisplayOrderConfirmation implements HookInterface
{
private $module;
private $context;
private $params;
public function __construct(Ps_Googleanalytics $module, Context $context)
{
$this->module = $module;
$this->context = $context;
}
/**
* run
*
* @return string
*/
public function run()
{
$gaScripts = '';
$order = $this->params['order'];
if (!Validate::isLoadedObject($order) || $order->getCurrentState() == (int) Configuration::get('PS_OS_ERROR')) {
return $gaScripts;
}
// Load up our handlers and repositories
$ganalyticsRepository = new GanalyticsRepository();
$gaTagHandler = new GanalyticsJsHandler($this->module, $this->context);
$productWrapper = new ProductWrapper($this->context);
$orderWrapper = new OrderWrapper($this->context);
// If it's a completely new order, add order to repository, so we can later mark it as sent
if (empty($ganalyticsRepository->findGaOrderByOrderId((int) $order->id))) {
$ganalyticsRepository->addOrder((int) $order->id, (int) $order->id_shop);
}
// If the customer is revisiting confirmation screen and the order was already sent, we don't do anything
if ($ganalyticsRepository->hasOrderBeenAlreadySent((int) $order->id)) {
return $gaScripts;
}
// Prepare transaction data
$orderData = $orderWrapper->wrapOrder($order);
// Prepare order products, if the cart still exists
$orderProducts = [];
$cart = new Cart($order->id_cart);
if (Validate::isLoadedObject($cart)) {
$orderProducts = $productWrapper->prepareItemListFromProductList($cart->getProducts(), true);
}
// Add payment event
$gaScripts .= $this->module->getTools()->renderEvent(
'add_payment_info',
[
'currency' => $orderData['currency'],
'value' => (float) $orderData['value'],
'payment_type' => $orderData['payment_type'],
'items' => $orderProducts,
]
);
// Render transaction code
$gaScripts .= $this->module->getTools()->renderPurchaseEvent(
$orderProducts,
$orderData,
$this->context->link->getModuleLink('ps_googleanalytics', 'ajax', [], true)
);
return $gaTagHandler->generate($gaScripts);
}
/**
* setParams
*
* @param array $params
*/
public function setParams($params)
{
$this->params = $params;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Hooks;
use Context;
use Ps_Googleanalytics;
interface HookInterface
{
/**
* @param Ps_Googleanalytics $module
* @param Context $context
*/
public function __construct(Ps_Googleanalytics $module, Context $context);
public function run();
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header('Expires: Mon, 26 Jul 1998 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,44 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Repository;
use Db;
class CarrierRepository
{
const TABLE_NAME = 'carrier';
/**
* findByCarrierId
*
* @param int $carrierId
*
* @return string
*/
public function findByCarrierId($carrierId)
{
return Db::getInstance()->getValue(
'SELECT name
FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '`
WHERE id_carrier = ' . (int) $carrierId
);
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Repository;
use Db;
class GanalyticsDataRepository
{
const TABLE_NAME = 'ganalytics_data';
/**
* findByCartId
*
* @param int $cartId
* @param int $shopId
*
* @return mixed
*/
public function findDataByCartIdAndShopId($cartId, $shopId)
{
return Db::getInstance()->getValue(
'SELECT data
FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '`
WHERE id_cart = ' . (int) $cartId . '
AND id_shop = ' . (int) $shopId
);
}
/**
* addNewRow
*
* @param int $cartId
* @param int $shopId
* @param string $data
*
* @return bool
*/
public function addNewRow($cartId, $shopId, $data)
{
return Db::getInstance()->Execute(
'INSERT INTO `' . _DB_PREFIX_ . self::TABLE_NAME . '` (id_cart, id_shop, data)
VALUES(\'' . (int) $cartId . '\',\'' . (int) $shopId . '\',\'' . pSQL($data) . '\')
ON DUPLICATE KEY UPDATE data = \'' . pSQL($data) . '\';'
);
}
/**
* deleteRow
*
* @param int $cartId
* @param int $shopId
*
* @return bool
*/
public function deleteRow($cartId, $shopId)
{
return Db::getInstance()->delete(
self::TABLE_NAME,
'id_cart = ' . (int) $cartId . ' AND id_shop = ' . (int) $shopId
);
}
}

View File

@@ -0,0 +1,153 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Repository;
use Db;
class GanalyticsRepository
{
const TABLE_NAME = 'ganalytics';
/**
* Finds if we have a record for this order ID.
*
* @param int $orderId
*
* @return mixed
*/
public function findGaOrderByOrderId($orderId)
{
return Db::getInstance()->getValue(
'SELECT id_order
FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '`
WHERE id_order = ' . (int) $orderId);
}
/**
* Checks if order is already sent to GA
*
* @param int $idOrder
*
* @return bool
*/
public function hasOrderBeenAlreadySent($idOrder)
{
return (bool) Db::getInstance()->getValue(
'SELECT `sent`
FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '`
WHERE id_order = ' . (int) $idOrder);
}
/**
* findAllByShopIdAndDateAdd
*
* @param int $shopId
*
* @return array
*/
public function findAllByShopIdAndDateAdd($shopId)
{
return Db::getInstance()->ExecuteS(
'SELECT *
FROM `' . _DB_PREFIX_ . self::TABLE_NAME . '`
WHERE sent = 0
AND id_shop = ' . (int) $shopId . '
AND DATE_ADD(date_add, INTERVAL 30 minute) < NOW()'
);
}
/**
* addNewRow
*
* @param array $data
* @param int $type
*
* @return bool
*/
public function addNewRow(array $data, $type = Db::INSERT_IGNORE)
{
return Db::getInstance()->insert(
self::TABLE_NAME,
$data,
false,
true,
$type
);
}
/**
* Adds new order into repository
*
* @param int $idOrder
* @param int $idShop
*
* @return bool
*/
public function addOrder(int $idOrder, int $idShop)
{
return $this->addNewRow(
[
'id_order' => (int) $idOrder,
'id_shop' => (int) $idShop,
'sent' => 0,
'date_add' => ['value' => 'NOW()', 'type' => 'sql'],
]
);
}
/**
* updateData
*
* @param array $data
* @param string $where
* @param int $limit
*
* @return bool
*/
public function updateData($data, $where, $limit = 0)
{
return Db::getInstance()->update(
self::TABLE_NAME,
$data,
$where,
$limit
);
}
/**
* Marks order as successfully sent to GA via callback
*
* @param int $idOrder
*
* @return bool
*/
public function markOrderAsSent($idOrder)
{
return Db::getInstance()->update(
self::TABLE_NAME,
[
'date_add' => ['value' => 'NOW()', 'type' => 'sql'],
'sent' => 1,
],
'id_order = ' . (int) $idOrder
);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header('Expires: Mon, 26 Jul 1998 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,56 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Wrapper;
use Configuration;
use Context;
use Currency;
use Shop;
class OrderWrapper
{
private $context;
public function __construct(Context $context)
{
$this->context = $context;
}
/**
* Return a detailed transaction for Google Analytics
*/
public function wrapOrder($order)
{
// Prepare currency information
$currency = new Currency((int) $order->id_currency);
return [
'transaction_id' => (int) $order->id,
'affiliation' => Shop::isFeatureActive() ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'),
'value' => (float) $order->total_paid,
'shipping' => (float) $order->total_shipping,
'tax' => (float) $order->total_paid_tax_incl - $order->total_paid_tax_excl,
'customer' => (int) $order->id_customer,
'currency' => $currency->iso_code,
'payment_type' => (string) $order->payment,
];
}
}

View File

@@ -0,0 +1,256 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
namespace PrestaShop\Module\Ps_Googleanalytics\Wrapper;
use Configuration;
use Context;
use Db;
use Manufacturer;
use PrestaShop\PrestaShop\Adapter\Presenter\Product\ProductLazyArray;
use PrestaShop\PrestaShop\Adapter\Presenter\Product\ProductListingLazyArray;
use Shop;
class ProductWrapper
{
private $context;
private $categories = [];
private $homeCategory = 0;
public function __construct(Context $context)
{
$this->context = $context;
}
/**
* Takes provided list of product (lazy) arrays and converts it to a format that GA4 requires.
*
* @param array $productList
* @param bool $useProvidedQuantity Should provided quantity be used, usually for cart related events
*
* @return array Item data standardized for GA
*/
public function prepareItemListFromProductList($productList, $useProvidedQuantity = false)
{
$items = [];
// Check we actually got some product
if (empty($productList)) {
return [];
}
// Preload categories for the whole list
$productIds = [];
foreach ($productList as $product) {
if (!empty($product['id_product'])) {
$productIds[] = $product['id_product'];
} elseif (!empty($product['id'])) {
$productIds[] = $product['id'];
}
}
$this->loadCategories($productIds);
// Prepare each item and override the counter
$counter = 0;
foreach ($productList as $product) {
$product = $this->prepareItemFromProduct($product, $useProvidedQuantity);
$product['index'] = $counter;
$items[] = $product;
++$counter;
}
return $items;
}
/**
* Loads all product categories for provided product IDs
*
* @param array $productIds
*/
private function loadCategories($productIds)
{
if (empty($productIds)) {
return;
}
// Initialize home category
$this->homeCategory = (int) Configuration::get('PS_HOME_CATEGORY');
// Initialize our cache
$this->categories = [];
foreach ($productIds as $id) {
$this->categories[(int) $id] = [];
}
// Load categories for all products
$result = Db::getInstance()->executeS(
'
SELECT cp.`id_category`, cp.`id_product`, cl.`name` FROM `' . _DB_PREFIX_ . 'category_product` cp
LEFT JOIN `' . _DB_PREFIX_ . 'category` c ON (c.id_category = cp.id_category)
LEFT JOIN `' . _DB_PREFIX_ . 'category_lang` cl ON (cp.`id_category` = cl.`id_category`' . Shop::addSqlRestrictionOnLang('cl') . ')
' . Shop::addSqlAssociation('category', 'c') . '
WHERE cp.`id_product` IN (' . implode(',', $productIds) . ') AND cl.`id_lang` = ' . (int) $this->context->language->id . '
ORDER BY c.`level_depth` DESC'
);
// Store it by product ID
foreach ($result as $row) {
$this->categories[(int) $row['id_product']][] = $row;
}
}
/**
* Takes provided (lazy) array and converts it to a format that GA4 requires. It can handle:
* - ProductLazyArray from product page
* - ProductListingLazyArray from presented listings
* - ProductListingLazyArray from presented cart
* - Raw $cart->getProducts()
* - Legacy product object converted to an array enriched with Product::getProductProperties
*
* @param ProductLazyArray|ProductListingLazyArray|array $product
* @param bool $useProvidedQuantity Should provided quantity be used, usually for cart related events
*
* @return array Item data standardized for GA
*/
public function prepareItemFromProduct($product, $useProvidedQuantity = false)
{
// Standardize product ID
$product_id = 0;
if (!empty($product['id_product'])) {
$product_id = $product['id_product'];
} elseif (!empty($product['id'])) {
$product_id = $product['id'];
}
// Standardize product price, make sure this price went through calculation before you pass it here
if (empty($product['price_amount'])) {
$product['price_amount'] = $product['price'];
}
$item = [
'item_id' => (int) $product_id,
'item_name' => (string) $product['name'],
'affiliation' => Shop::isFeatureActive() ? $this->context->shop->name : Configuration::get('PS_SHOP_NAME'),
'index' => 0,
'price' => $product['price_amount'],
'quantity' => 1,
];
// Add manufacturer info if we have it
if (!empty($product['manufacturer_name'])) {
$item['item_brand'] = $product['manufacturer_name'];
// If we don't, which can happen due to some bugs in getProductProperties, we will fetch it manually
} elseif (!empty($product['id_manufacturer'])) {
$manufacturerName = Manufacturer::getNameById((int) $product['id_manufacturer']);
if (!empty($manufacturerName)) {
$item['item_brand'] = $manufacturerName;
}
}
// We will specify variant ID if we have it
if (!empty($product['id_product_attribute'])) {
$item['item_id'] .= '-' . $product['id_product_attribute'];
}
// Information about a chosen variant, if we have it (cart list has this out of the box)
if (!empty($product['attributes_small'])) {
$item['item_variant'] = $product['attributes_small'];
// If we don't, we will construct it in the same format
} elseif (!empty($product['id_product_attribute'])) {
$variant = $this->getProductVariant((int) $product['id_product_attribute']);
if (!empty($variant)) {
$item['item_variant'] = $variant;
}
}
if ($useProvidedQuantity === true) {
// Info about quantity in cart, if we have it
$item['quantity'] = $product['quantity'];
}
// Prepare category information, if not loaded before, we will fetch it
if (!isset($this->categories[(int) $product_id])) {
$this->loadCategories([(int) $product_id]);
}
$productCategories = $this->categories[(int) $product_id];
// If the category is our default one, we will move it to the beginning of that list
foreach ($productCategories as $key => $category) {
if ($category['id_category'] == $product['id_category_default']) {
$productCategories = [$key => $category] + $productCategories;
break;
}
}
// Add it to our item
$counter = 1;
foreach ($productCategories as $productCategory) {
// Skip home category if we have more than 1 category
if (count($productCategories) > 1 && $productCategory['id_category'] == $this->homeCategory) {
continue;
}
// Add it with proper key
$item[$counter == 1 ? 'item_category' : 'item_category' . $counter] = $productCategory['name'];
if ($counter == 5) {
break;
}
++$counter;
}
return $item;
}
/**
* Method that will provide product combination attribute in the same format and order as cart does.
*
* @param int $id_product_attribute ID of the combination
*
* @return string Attribute list
*/
public function getProductVariant($id_product_attribute)
{
$result = Db::getInstance()->executeS(
'SELECT al.`name` AS attribute_name
FROM `' . _DB_PREFIX_ . 'product_attribute_combination` pac
LEFT JOIN `' . _DB_PREFIX_ . 'attribute` a ON a.`id_attribute` = pac.`id_attribute`
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group`
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_lang` al ON (
a.`id_attribute` = al.`id_attribute`
AND al.`id_lang` = ' . (int) $this->context->language->id . '
)
WHERE pac.`id_product_attribute` = ' . $id_product_attribute . '
ORDER BY ag.`position` ASC, a.`position` ASC'
);
$attributes = array_column($result, 'attribute_name');
// Prepare our separator
$separator = Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR');
if ($separator === '-') {
// Add a space before the dash between attributes
$separator = ' - ';
}
return implode($separator, $attributes);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header('Expires: Mon, 26 Jul 1998 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,28 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
header('Expires: Mon, 26 Jul 1998 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;