vendor/pimcore/pimcore/lib/Analytics/Google/Tracker.php line 158

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4.  * Pimcore
  5.  *
  6.  * This source file is available under two different licenses:
  7.  * - GNU General Public License version 3 (GPLv3)
  8.  * - Pimcore Enterprise License (PEL)
  9.  * Full copyright and license information is available in
  10.  * LICENSE.md which is distributed with this source code.
  11.  *
  12.  * @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  13.  * @license    http://www.pimcore.org/license     GPLv3 and PEL
  14.  */
  15. namespace Pimcore\Analytics\Google;
  16. use Pimcore\Analytics\AbstractTracker;
  17. use Pimcore\Analytics\Code\CodeBlock;
  18. use Pimcore\Analytics\Code\CodeCollector;
  19. use Pimcore\Analytics\Google\Config\Config;
  20. use Pimcore\Analytics\Google\Config\ConfigProvider;
  21. use Pimcore\Analytics\Google\Event\TrackingDataEvent;
  22. use Pimcore\Analytics\SiteId\SiteId;
  23. use Pimcore\Analytics\SiteId\SiteIdProvider;
  24. use Pimcore\Config\Config as ConfigObject;
  25. use Pimcore\Event\Analytics\GoogleAnalyticsEvents;
  26. use Psr\Log\LoggerInterface;
  27. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  28. use Symfony\Component\Templating\EngineInterface;
  29. class Tracker extends AbstractTracker
  30. {
  31.     const BLOCK_BEFORE_SCRIPT_TAG 'beforeScriptTag';
  32.     const BLOCK_BEFORE_SCRIPT 'beforeScript';
  33.     const BLOCK_BEFORE_INIT 'beforeInit';
  34.     const BLOCK_BEFORE_TRACK 'beforeTrack';
  35.     const BLOCK_AFTER_TRACK 'afterTrack';
  36.     const BLOCK_AFTER_SCRIPT 'afterScript';
  37.     const BLOCK_AFTER_SCRIPT_TAG 'afterScriptTag';
  38.     /**
  39.      * @var SiteIdProvider
  40.      */
  41.     private $siteIdProvider;
  42.     /**
  43.      * @var ConfigProvider
  44.      */
  45.     private $configProvider;
  46.     /**
  47.      * @var EventDispatcherInterface
  48.      */
  49.     private $eventDispatcher;
  50.     /**
  51.      * @var EngineInterface
  52.      */
  53.     private $templatingEngine;
  54.     /**
  55.      * @var string|null
  56.      */
  57.     private $defaultPath;
  58.     /**
  59.      * @var LoggerInterface
  60.      */
  61.     private $logger;
  62.     /**
  63.      * @var array
  64.      */
  65.     private $blocks = [
  66.         self::BLOCK_BEFORE_SCRIPT_TAG,
  67.         self::BLOCK_BEFORE_SCRIPT,
  68.         self::BLOCK_BEFORE_INIT,
  69.         self::BLOCK_BEFORE_TRACK,
  70.         self::BLOCK_AFTER_TRACK,
  71.         self::BLOCK_AFTER_SCRIPT,
  72.         self::BLOCK_AFTER_SCRIPT_TAG,
  73.     ];
  74.     public function __construct(
  75.         SiteIdProvider $siteIdProvider,
  76.         ConfigProvider $configProvider,
  77.         EventDispatcherInterface $eventDispatcher,
  78.         EngineInterface $templatingEngine
  79.     ) {
  80.         parent::__construct($siteIdProvider);
  81.         $this->siteIdProvider $siteIdProvider;
  82.         $this->configProvider $configProvider;
  83.         $this->eventDispatcher $eventDispatcher;
  84.         $this->templatingEngine $templatingEngine;
  85.     }
  86.     public function getDefaultPath()
  87.     {
  88.         return $this->defaultPath;
  89.     }
  90.     public function setDefaultPath(string $defaultPath null)
  91.     {
  92.         $this->defaultPath $defaultPath;
  93.     }
  94.     /**
  95.      * TODO Pimcore 6 set logger as constructor dependency
  96.      */
  97.     public function setLogger(LoggerInterface $logger)
  98.     {
  99.         $this->logger $logger;
  100.     }
  101.     protected function buildCodeCollector(): CodeCollector
  102.     {
  103.         return new CodeCollector($this->blocksself::BLOCK_AFTER_TRACK);
  104.     }
  105.     protected function buildCode(SiteId $siteId)
  106.     {
  107.         $config $this->configProvider->getConfig();
  108.         $configKey $siteId->getConfigKey();
  109.         if (!$config->isSiteConfigured($configKey)) {
  110.             return null;
  111.         }
  112.         $siteConfig $config->getConfigForSite($configKey);
  113.         return $this->doBuildCode($siteId$config$siteConfig);
  114.     }
  115.     /**
  116.      * This method exists for BC with the existing Pimcore\Google\Analytics implementation which supports to pass a config
  117.      * object without a Site ID. Should be removed at a later point.
  118.      *
  119.      * @param ConfigObject $siteConfig
  120.      * @param SiteId|null $siteId
  121.      *
  122.      * @return string
  123.      */
  124.     public function generateCodeForSiteConfig(ConfigObject $siteConfigSiteId $siteId null)
  125.     {
  126.         if (null === $siteId) {
  127.             $siteId $this->siteIdProvider->getForRequest();
  128.         }
  129.         $config $this->configProvider->getConfig();
  130.         return $this->doBuildCode($siteId$config$siteConfig);
  131.     }
  132.     private function doBuildCode(SiteId $siteIdConfig $configConfigObject $siteConfig)
  133.     {
  134.         $data = [
  135.             'siteId' => $siteId,
  136.             'config' => $config,
  137.             'siteConfig' => $siteConfig,
  138.             'trackId' => $siteConfig->trackid,
  139.             'defaultPath' => $this->getDefaultPath(),
  140.             'universalConfiguration' => $siteConfig->universal_configuration ?? null,
  141.             'retargeting' => $siteConfig->retargetingcode ?? false,
  142.         ];
  143.         if ($siteConfig->gtagcode) {
  144.             $template '@PimcoreCore/Analytics/Tracking/Google/Analytics/gtagTrackingCode.html.twig';
  145.             $data['gtagConfig'] = $this->getTrackerConfigurationFromJson($siteConfig->universal_configuration ?? null, [
  146.                 'anonymize_ip' => true
  147.             ]);
  148.         } elseif ($siteConfig->asynchronouscode || $siteConfig->retargetingcode) {
  149.             $template '@PimcoreCore/Analytics/Tracking/Google/Analytics/asynchronousTrackingCode.html.twig';
  150.         } else {
  151.             $template '@PimcoreCore/Analytics/Tracking/Google/Analytics/universalTrackingCode.html.twig';
  152.         }
  153.         $blocks $this->buildCodeBlocks($siteId$siteConfig);
  154.         $event = new TrackingDataEvent($config$siteId$data$blocks$template);
  155.         $this->eventDispatcher->dispatch(GoogleAnalyticsEvents::CODE_TRACKING_DATA$event);
  156.         return $this->renderTemplate($event);
  157.     }
  158.     private function getTrackerConfigurationFromJson($configValue null, array $defaultConfig = []): array
  159.     {
  160.         $config = [];
  161.         if (!empty($configValue)) {
  162.             $jsonConfig = @json_decode($configValuetrue);
  163.             if (JSON_ERROR_NONE === json_last_error() && is_array($jsonConfig)) {
  164.                 $config $jsonConfig;
  165.             } else {
  166.                 $this->logger->warning('Failed to parse analytics tracker custom configuration: {error}', [
  167.                     'error' => json_last_error_msg() ?? 'not an array'
  168.                 ]);
  169.             }
  170.         }
  171.         return array_merge($defaultConfig$config);
  172.     }
  173.     private function buildCodeBlocks(SiteId $siteIdConfigObject $siteConfig): array
  174.     {
  175.         $blockData $this->buildBlockData($siteConfig);
  176.         $blocks = [];
  177.         foreach ($this->blocks as $block) {
  178.             $codeBlock = new CodeBlock();
  179.             if (isset($blockData[$block])) {
  180.                 $codeBlock->append($blockData[$block]);
  181.             }
  182.             $this->getCodeCollector()->enrichCodeBlock($siteId$codeBlock$block);
  183.             $blocks[$block] = $codeBlock;
  184.         }
  185.         return $blocks;
  186.     }
  187.     private function buildBlockData(ConfigObject $siteConfig): array
  188.     {
  189.         $blockData = [];
  190.         if (!empty($siteConfig->additionalcodebeforeinit)) {
  191.             $blockData[self::BLOCK_BEFORE_INIT] = $siteConfig->additionalcodebeforeinit;
  192.         }
  193.         if (!empty($siteConfig->additionalcodebeforepageview)) {
  194.             $blockData[self::BLOCK_BEFORE_TRACK] = $siteConfig->additionalcodebeforepageview;
  195.         }
  196.         if (!empty($siteConfig->additionalcode)) {
  197.             $blockData[self::BLOCK_AFTER_TRACK] = $siteConfig->additionalcode;
  198.         }
  199.         return $blockData;
  200.     }
  201.     private function renderTemplate(TrackingDataEvent $event): string
  202.     {
  203.         $data $event->getData();
  204.         $data['blocks'] = $event->getBlocks();
  205.         $code $this->templatingEngine->render(
  206.             $event->getTemplate(),
  207.             $data
  208.         );
  209.         $code trim($code);
  210.         if (!empty($code)) {
  211.             $code "\n" $code "\n";
  212.         }
  213.         return $code;
  214.     }
  215. }