diff --git a/Controller/Ajax/Analytics.php b/Controller/Ajax/Analytics.php
new file mode 100644
index 00000000..ea699614
--- /dev/null
+++ b/Controller/Ajax/Analytics.php
@@ -0,0 +1,77 @@
+resultJsonFactory->create();
+ if ($this->config->isAnalyticsEnabled()) {
+ $type = $this->getRequest()->getParam('type');
+ $profileKey = $this->config->getProfileKey();
+
+ $tweakwiseRequest = $this->requestFactory->create();
+ $tweakwiseRequest->setProfileKey($profileKey);
+ $value = $this->getRequest()->getParam('value');
+
+ if ($type === 'product') {
+ $tweakwiseRequest->setParameter('productKey', $value);
+ $tweakwiseRequest->setPath('pageview');
+ } elseif ($type === 'search') {
+ $tweakwiseRequest->setParameter('searchTerm', $value);
+ $tweakwiseRequest->setPath('search');
+ }
+
+ if (!empty($tweakwiseRequest->getPath())) {
+ try {
+ $this->client->request($tweakwiseRequest);
+ $result->setData(['success' => true]);
+ } catch (\Exception $e) {
+ $result->setData(
+ [
+ 'success' => false,
+ 'message' => $e->getMessage()
+ ]
+ );
+ }
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/Model/Client.php b/Model/Client.php
index 18ee7fcf..74916bc1 100644
--- a/Model/Client.php
+++ b/Model/Client.php
@@ -20,12 +20,12 @@
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7\Request as HttpRequest;
-use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\RequestOptions;
use Magento\Framework\Profiler;
use Psr\Http\Message\ResponseInterface;
use SimpleXMLElement;
use GuzzleHttp\Client as HttpClient;
+use Magento\Framework\UrlInterface;
class Client
{
@@ -71,8 +71,6 @@ class Client
* @param Logger $log
* @param ResponseFactory $responseFactory
* @param EndpointManager $endpointManager
- * @param Timer $timer
- * @param ModuleInformation $moduleInformation
*/
public function __construct(
Config $config,
@@ -80,7 +78,7 @@ public function __construct(
ResponseFactory $responseFactory,
EndpointManager $endpointManager,
Timer $timer,
- private readonly ModuleInformation $moduleInformation
+ private UrlInterface $urlBuilder
) {
$this->config = $config;
$this->log = $log;
@@ -99,8 +97,7 @@ protected function getClient(): HttpClient
RequestOptions::TIMEOUT => self::REQUEST_TIMEOUT,
RequestOptions::HEADERS => [
'user-agent' => $this->config->getUserAgentString(),
- 'Accept-Encoding' => 'gzip, deflate',
- 'TWN-Source' => $this->moduleInformation->getModuleVersion(),
+ 'Accept-Encoding' => 'gzip, deflate'
]
];
$this->client = new HttpClient($options);
@@ -114,10 +111,42 @@ protected function getClient(): HttpClient
* @return HttpRequest
*/
protected function createHttpRequest(Request $tweakwiseRequest): HttpRequest
+ {
+ if ($tweakwiseRequest->isPostRequest()) {
+ return $this->createPostRequest($tweakwiseRequest);
+ } else {
+ return $this->createGetRequest($tweakwiseRequest);
+ }
+ }
+
+ /**
+ * @param Request $tweakwiseRequest
+ * @return HttpRequest
+ */
+ protected function createPostRequest(Request $tweakwiseRequest): HttpRequest
+ {
+ $path = $tweakwiseRequest->getPath();
+ $headers = [];
+
+ $headers['Content-Type'] = 'application/json';
+ $headers['Instance-Key'] = $this->config->getGeneralAuthenticationKey();
+ $body = json_encode($tweakwiseRequest->getParameters());
+ //post request are used for the analytics api
+ $uri = $this->urlBuilder->getUrl($tweakwiseRequest->getApiUrl() . '/' . $path);
+ return new HttpRequest('POST', $uri, $headers, $body);
+ }
+
+ /**
+ * @param Request $tweakwiseRequest
+ * @return HttpRequest
+ */
+ protected function createGetRequest(Request $tweakwiseRequest): HttpRequest
{
$path = $tweakwiseRequest->getPath();
$pathSuffix = $tweakwiseRequest->getPathSuffix();
+ $headers = [];
+
if ($path === "recommendations/featured") {
if ($this->config->getRecommendationsFeaturedCategory()) {
$tweakwiseRequest->setCategory();
@@ -138,8 +167,7 @@ protected function createHttpRequest(Request $tweakwiseRequest): HttpRequest
}
$uri = new Uri($url);
-
- return new HttpRequest('GET', $uri);
+ return new HttpRequest('GET', $uri, $headers);
}
/**
@@ -208,6 +236,10 @@ public function handleRequestSuccess(
)
);
+ if ($statusCode === 204) {
+ return $this->responseFactory->create($tweakwiseRequest, []);
+ }
+
if ($statusCode !== 200) {
throw new ApiException(
sprintf(
diff --git a/Model/Client/Request.php b/Model/Client/Request.php
index 233124bf..901f06fa 100644
--- a/Model/Client/Request.php
+++ b/Model/Client/Request.php
@@ -326,4 +326,21 @@ public function setProfileKey(string $profileKey)
$this->setParameter('tn_profilekey', $profileKey);
return $this;
}
+
+ /**
+ * @return string|null
+ */
+ public function setParameterArray(string $parameter, array $value)
+ {
+ $this->parameters[$parameter] = $value;
+ return $this;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function isPostRequest()
+ {
+ return false;
+ }
}
diff --git a/Model/Client/Request/AnalyticsRequest.php b/Model/Client/Request/AnalyticsRequest.php
new file mode 100644
index 00000000..10abb1b7
--- /dev/null
+++ b/Model/Client/Request/AnalyticsRequest.php
@@ -0,0 +1,69 @@
+apiUrl;
+ }
+
+ /**
+ * @return void
+ */
+ public function setProfileKey(string $profileKey)
+ {
+ $this->setParameter('ProfileKey', $profileKey);
+ }
+
+ /**
+ * @return void
+ */
+ public function setPath($path)
+ {
+ $this->path = $path;
+ }
+
+ /**
+ * @return string
+ */
+ public function getProfileKey()
+ {
+ $profileKey = $this->getCookie($this->config->getPersonalMerchandisingCookieName());
+ if (!$profileKey) {
+ $profileKey = $this->generateProfileKey();
+ $this->setCookie('profileKey', $profileKey);
+ }
+
+ return $profileKey;
+ }
+}
diff --git a/Model/Client/Request/PurchaseRequest.php b/Model/Client/Request/PurchaseRequest.php
new file mode 100644
index 00000000..094ee3aa
--- /dev/null
+++ b/Model/Client/Request/PurchaseRequest.php
@@ -0,0 +1,54 @@
+apiUrl;
+ }
+
+ /**
+ * @param string $profileKey
+ *
+ * @return void
+ */
+ public function setProfileKey(string $profileKey)
+ {
+ $this->setParameter('ProfileKey', $profileKey);
+ }
+}
diff --git a/Model/Config.php b/Model/Config.php
index f356aac4..9f1cfe3a 100644
--- a/Model/Config.php
+++ b/Model/Config.php
@@ -408,7 +408,13 @@ public function isSearchBannersActive(Store $store = null)
*/
public function getPersonalMerchandisingCookieName(Store $store = null)
{
- return (string) $this->getStoreConfig('tweakwise/personal_merchandising/cookie_name', $store);
+ $cookie = $this->getStoreConfig('tweakwise/personal_merchandising/cookie_name', $store);
+
+ if (empty($cookie)) {
+ $cookie = 'tw_analytics';
+ }
+
+ return $cookie;
}
/**
diff --git a/Model/PersonalMerchandisingConfig.php b/Model/PersonalMerchandisingConfig.php
new file mode 100644
index 00000000..066859cf
--- /dev/null
+++ b/Model/PersonalMerchandisingConfig.php
@@ -0,0 +1,82 @@
+getStoreConfig('tweakwise/personal_merchandising/analytics_enabled', $store);
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getProfileKey()
+ {
+ $profileKey = $this->cookieManager->getCookie(
+ $this->getPersonalMerchandisingCookieName(),
+ null
+ );
+
+ if ($this->isAnalyticsEnabled()) {
+ if ($profileKey === null) {
+ $profileKey = $this->generateProfileKey();
+ $this->cookieManager->setPublicCookie(
+ $this->getPersonalMerchandisingCookieName(),
+ $profileKey,
+ $this->getCookieMetadata()
+ );
+ }
+ }
+
+ return $profileKey;
+ }
+
+ /**
+ * @return string
+ */
+ private function getCookieMetadata()
+ {
+ return $this->cookieMetadataFactory
+ ->createPublicCookieMetadata()
+ ->setDuration(86400)
+ ->setPath('/')
+ ->setSecure(true);
+ }
+
+ /**
+ * @return string
+ */
+ private function generateProfileKey()
+ {
+ return uniqid('', true);
+ }
+}
diff --git a/Observer/TweakwiseCheckout.php b/Observer/TweakwiseCheckout.php
new file mode 100644
index 00000000..2be13a8e
--- /dev/null
+++ b/Observer/TweakwiseCheckout.php
@@ -0,0 +1,86 @@
+config->isAnalyticsEnabled('tweakwise/personal_merchandising/analytics_enabled')) {
+ $order = $observer->getEvent()->getOrder();
+ // Get the order items
+ $items = $order->getAllItems();
+
+ $this->sendCheckout($items);
+ }
+ }
+
+ /**
+ * @param Items $items
+ *
+ * @return void
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ private function sendCheckout($items)
+ {
+ $storeId = (int)$this->storeManager->getStore()->getId();
+ $profileKey = $this->config->getProfileKey();
+ $tweakwiseRequest = $this->requestFactory->create();
+
+ $tweakwiseRequest->setParameter('profileKey', $profileKey);
+ $tweakwiseRequest->setPath('purchase');
+
+ foreach ($items as $item) {
+ $productTwId[] = $this->helper->getTweakwiseId($storeId, (int)$item->getProductId());
+ }
+
+ $tweakwiseRequest->setParameterArray('ProductKeys', $productTwId);
+
+ // @phpcs:disable
+ try {
+ $this->client->request($tweakwiseRequest);
+ } catch (\Exception $e) {
+ // Do nothing so that the checkout process can continue
+ }
+ // @phpcs:enable
+ }
+}
diff --git a/ViewModel/PersonalMerchandisingAnalytics.php b/ViewModel/PersonalMerchandisingAnalytics.php
new file mode 100644
index 00000000..a67f6ba0
--- /dev/null
+++ b/ViewModel/PersonalMerchandisingAnalytics.php
@@ -0,0 +1,92 @@
+request->getParam('id');
+ $storeId = $this->storeManager->getStore()->getId();
+
+ if (!$productId) {
+ return '0';
+ }
+
+ return $this->helper->getTweakwiseId((int)$storeId, (int)$productId);
+ }
+
+ /**
+ * Get the API URL.
+ *
+ * @return string
+ */
+ public function getApiUrl(): string
+ {
+ return 'https://navigator-analytics.tweakwise.com/api/';
+ }
+
+ /**
+ * Get the instance key.
+ *
+ * @return string
+ */
+ public function getInstanceKey(): string
+ {
+ return $this->tweakwiseConfig->getGeneralAuthenticationKey();
+ }
+
+ /**
+ * Get the cookie name.
+ *
+ * @return string
+ */
+ public function getCookieName(): string
+ {
+ return $this->tweakwiseConfig->getPersonalMerchandisingCookieName();
+ }
+
+ /**
+ * Get the search query.
+ *
+ * @return string
+ */
+ public function getSearchQuery(): string
+ {
+ return $this->request->getParam('q');
+ }
+}
diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml
index 0503db62..097860e5 100644
--- a/etc/adminhtml/system.xml
+++ b/etc/adminhtml/system.xml
@@ -235,9 +235,14 @@
setting below)
+
+
+ Magento\Config\Model\Config\Source\Yesno
+ Note only enable this if you don't use javascript to send productviews/search/purchases requests to tweakwise
+
- Name of cookie which holds tweakwise profile information, this is usually set in the tweakwise measure script.
+ Name of cookie which holds tweakwise profile information, this is usually set in the tweakwise measure script. Or when analytics is enabled, it is done automaticly
1
diff --git a/etc/config.xml b/etc/config.xml
index 8b86c9e7..6183c6b7 100644
--- a/etc/config.xml
+++ b/etc/config.xml
@@ -42,6 +42,7 @@ cid
3600
+ 0
0
diff --git a/etc/di.xml b/etc/di.xml
index 0a1374fa..5b732537 100644
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -235,6 +235,16 @@
Tweakwise\Magento2Tweakwise\Model\Client\RequestFactory\FacetAttributeRequest
+
+
+ Tweakwise\Magento2Tweakwise\Model\Client\RequestFactory\PurchaseRequest
+
+
+
+
+ Tweakwise\Magento2Tweakwise\Model\Client\RequestFactory\AnalyticsRequest
+
+
@@ -418,5 +428,14 @@
Tweakwise\Magento2Tweakwise\Model\Client\Request\Recommendations\FeaturedRequest
-
+
+
+ Tweakwise\Magento2Tweakwise\Model\Client\Request\PurchaseRequest
+
+
+
+
+ Tweakwise\Magento2Tweakwise\Model\Client\Request\AnalyticsRequest
+
+
diff --git a/etc/events.xml b/etc/events.xml
index 48e717da..8486f01e 100644
--- a/etc/events.xml
+++ b/etc/events.xml
@@ -5,4 +5,7 @@
instance="Tweakwise\Magento2Tweakwise\Observer\CreateTweakwiseSlugsAfterSaveAttribute"
/>
+
+
+
diff --git a/view/frontend/layout/catalog_product_view.xml b/view/frontend/layout/catalog_product_view.xml
index b6a0211b..4290a020 100644
--- a/view/frontend/layout/catalog_product_view.xml
+++ b/view/frontend/layout/catalog_product_view.xml
@@ -10,17 +10,25 @@
-
+
+
-
- Tweakwise\Magento2Tweakwise\ViewModel\LinkedProductListItem
-
+ Tweakwise\Magento2Tweakwise\ViewModel\PersonalMerchandisingAnalytics
+ product
-
-
+
+
+
+
+
+ Tweakwise\Magento2Tweakwise\ViewModel\LinkedProductListItem
+
+
+
+
diff --git a/view/frontend/layout/catalogsearch_result_index.xml b/view/frontend/layout/catalogsearch_result_index.xml
index 7fcff54f..8ffe3fce 100644
--- a/view/frontend/layout/catalogsearch_result_index.xml
+++ b/view/frontend/layout/catalogsearch_result_index.xml
@@ -15,6 +15,12 @@
+
+
+ Tweakwise\Magento2Tweakwise\ViewModel\PersonalMerchandisingAnalytics
+ search
+
+
Tweakwise\Magento2Tweakwise\Model\NavigationConfig\Search
@@ -28,6 +34,13 @@
+
+
+
+ Tweakwise\Magento2Tweakwise\ViewModel\ProductListItem
+
+
+
Tweakwise\Magento2Tweakwise\Model\NavigationConfig\Search
diff --git a/view/frontend/templates/analytics.phtml b/view/frontend/templates/analytics.phtml
new file mode 100644
index 00000000..482bee6c
--- /dev/null
+++ b/view/frontend/templates/analytics.phtml
@@ -0,0 +1,21 @@
+getData('viewModel');
+$analyticsType = $block->getData('analyticsType');
+$value = $analyticsType === 'product' ? $viewModel->getProductKey() : $viewModel->getSearchQuery();
+?>
+
+
+
diff --git a/view/frontend/web/js/analytics.js b/view/frontend/web/js/analytics.js
new file mode 100644
index 00000000..599e5f06
--- /dev/null
+++ b/view/frontend/web/js/analytics.js
@@ -0,0 +1,21 @@
+define('Tweakwise_Magento2Tweakwise/js/analytics', ['jquery'], function($) {
+ 'use strict';
+
+ return function(config) {
+ $(document).ready(function() {
+ var requestData = {
+ type: config.type,
+ value: config.value
+ };
+
+ $.ajax({
+ url: '/tweakwise/ajax/analytics',
+ method: 'POST',
+ data: requestData,
+ error: function(error) {
+ console.error('Tweakwise API call failed:', error);
+ }
+ });
+ });
+ };
+});