vendor/pimcore/pimcore/bundles/CoreBundle/EventListener/Frontend/GoogleTagManagerListener.php line 89

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\Bundle\CoreBundle\EventListener\Frontend;
  16. use Pimcore\Analytics\Code\CodeBlock;
  17. use Pimcore\Analytics\SiteId\SiteIdProvider;
  18. use Pimcore\Bundle\CoreBundle\EventListener\Traits\EnabledTrait;
  19. use Pimcore\Bundle\CoreBundle\EventListener\Traits\PimcoreContextAwareTrait;
  20. use Pimcore\Bundle\CoreBundle\EventListener\Traits\PreviewRequestTrait;
  21. use Pimcore\Bundle\CoreBundle\EventListener\Traits\ResponseInjectionTrait;
  22. use Pimcore\Config;
  23. use Pimcore\Event\Analytics\Google\TagManager\CodeEvent;
  24. use Pimcore\Event\Analytics\GoogleTagManagerEvents;
  25. use Pimcore\Http\Request\Resolver\PimcoreContextResolver;
  26. use Pimcore\Tool;
  27. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  28. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  29. use Symfony\Component\Templating\EngineInterface;
  30. class GoogleTagManagerListener
  31. {
  32.     const BLOCK_HEAD_BEFORE_SCRIPT_TAG 'beforeScriptTag';
  33.     const BLOCK_HEAD_AFTER_SCRIPT_TAG 'afterScriptTag';
  34.     const BLOCK_BODY_BEFORE_NOSCRIPT_TAG 'beforeNoscriptTag';
  35.     const BLOCK_BODY_AFTER_NOSCRIPT_TAG 'afterNoscriptTag';
  36.     use EnabledTrait;
  37.     use ResponseInjectionTrait;
  38.     use PimcoreContextAwareTrait;
  39.     use PreviewRequestTrait;
  40.     /**
  41.      * @var SiteIdProvider
  42.      */
  43.     private $siteIdProvider;
  44.     /**
  45.      * @var EventDispatcherInterface
  46.      */
  47.     private $eventDispatcher;
  48.     /**
  49.      * @var EngineInterface
  50.      */
  51.     private $templatingEngine;
  52.     /**
  53.      * @var array
  54.      */
  55.     private $headBlocks = [
  56.         self::BLOCK_HEAD_BEFORE_SCRIPT_TAG,
  57.         self::BLOCK_HEAD_AFTER_SCRIPT_TAG
  58.     ];
  59.     /**
  60.      * @var array
  61.      */
  62.     private $bodyBlocks = [
  63.         self::BLOCK_BODY_BEFORE_NOSCRIPT_TAG,
  64.         self::BLOCK_BODY_AFTER_NOSCRIPT_TAG
  65.     ];
  66.     public function __construct(
  67.         SiteIdProvider $siteIdProvider,
  68.         EventDispatcherInterface $eventDispatcher,
  69.         EngineInterface $templatingEngine
  70.     ) {
  71.         $this->siteIdProvider $siteIdProvider;
  72.         $this->eventDispatcher $eventDispatcher;
  73.         $this->templatingEngine $templatingEngine;
  74.     }
  75.     public function onKernelResponse(FilterResponseEvent $event)
  76.     {
  77.         if (!$this->isEnabled()) {
  78.             return;
  79.         }
  80.         $request $event->getRequest();
  81.         if (!$event->isMasterRequest()) {
  82.             return;
  83.         }
  84.         // only inject tag manager code on non-admin requests
  85.         if (!$this->matchesPimcoreContext($requestPimcoreContextResolver::CONTEXT_DEFAULT)) {
  86.             return;
  87.         }
  88.         if (!Tool::useFrontendOutputFilters()) {
  89.             return;
  90.         }
  91.         if ($this->isPreviewRequest($request)) {
  92.             return;
  93.         }
  94.         $siteId $this->siteIdProvider->getForRequest($event->getRequest());
  95.         $siteKey $siteId->getConfigKey();
  96.         $reportConfig Config::getReportConfig();
  97.         if (!isset($reportConfig->get('tagmanager')->sites->$siteKey->containerId)) {
  98.             return;
  99.         }
  100.         $containerId $reportConfig->get('tagmanager')->sites->$siteKey->containerId;
  101.         if (!$containerId) {
  102.             return;
  103.         }
  104.         $response $event->getResponse();
  105.         if (!$this->isHtmlResponse($response)) {
  106.             return;
  107.         }
  108.         $codeHead $this->generateCode(
  109.             GoogleTagManagerEvents::CODE_HEAD,
  110.             '@PimcoreCore/Google/TagManager/codeHead.html.twig',
  111.             $this->headBlocks,
  112.             [
  113.                 'containerId' => $containerId
  114.             ]
  115.         );
  116.         $codeBody $this->generateCode(
  117.             GoogleTagManagerEvents::CODE_BODY,
  118.             '@PimcoreCore/Google/TagManager/codeBody.html.twig',
  119.             $this->bodyBlocks,
  120.             [
  121.                 'containerId' => $containerId
  122.             ]
  123.         );
  124.         $content $response->getContent();
  125.         if (!empty($codeHead)) {
  126.             // search for the end <head> tag, and insert the google tag manager code before
  127.             // this method is much faster than using simple_html_dom and uses less memory
  128.             $headEndPosition stripos($content'</head>');
  129.             if ($headEndPosition !== false) {
  130.                 $content substr_replace($content$codeHead '</head>'$headEndPosition7);
  131.             }
  132.         }
  133.         if (!empty($codeBody)) {
  134.             // insert code after the opening <body> tag
  135.             $content preg_replace('@<body(>|.*?[^?]>)@'"<body$1\n\n" $codeBody$content);
  136.         }
  137.         $response->setContent($content);
  138.     }
  139.     private function generateCode(string $eventNamestring $template, array $blockNames, array $data): string
  140.     {
  141.         $blocks = [];
  142.         foreach ($blockNames as $blockName) {
  143.             $blocks[$blockName] = new CodeBlock();
  144.         }
  145.         $event = new CodeEvent($data$blocks$template);
  146.         $this->eventDispatcher->dispatch($eventName$event);
  147.         return $this->renderTemplate($event);
  148.     }
  149.     private function renderTemplate(CodeEvent $event): string
  150.     {
  151.         $data $event->getData();
  152.         $data['blocks'] = $event->getBlocks();
  153.         $code $this->templatingEngine->render(
  154.             $event->getTemplate(),
  155.             $data
  156.         );
  157.         $code trim($code);
  158.         if (!empty($code)) {
  159.             $code "\n" $code "\n";
  160.         }
  161.         return $code;
  162.     }
  163. }