Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide Google Analytics and other tracking script over own API #74

Open
alexander-schranz opened this issue Feb 15, 2021 · 1 comment
Labels
DX Only affecting the end developer Feature New functionality not yet included in the Bundle
Milestone

Comments

@alexander-schranz
Copy link
Member

alexander-schranz commented Feb 15, 2021

Sulu allows to configure google analytics and other tracking scripts it should be possible to provide them over api:

{
     "_embedded": {
              "scripts": [{
                   "title": "Google Analytics",
                   "type": "google-analytics",
                   "position": "body_close",
                   "script": "<script>...</script>",
              },{
                   "title": "Hotjar",
                   "type": "custom",
                   "position": "body_close",
                   "script": "<script>...</script>",
              }]
     },
}

It should return only the scripts which match the selected webspace and locales. The API could run under: /_api/trackings or something similar.

@alexander-schranz alexander-schranz changed the title Api Provide Google Analytics and other tracking script Provide Google Analytics and other tracking script over own API Feb 15, 2021
@alexander-schranz alexander-schranz added DX Only affecting the end developer Feature New functionality not yet included in the Bundle labels Feb 15, 2021
@alexander-schranz
Copy link
Member Author

In a project we did currently use the following solution:

<?php

namespace App\Controller\Website;

use Sulu\Bundle\WebsiteBundle\Entity\AnalyticsInterface;
use Sulu\Bundle\WebsiteBundle\Entity\AnalyticsRepositoryInterface;
use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Twig\Environment;

class AnalyticsController
{
    private static $positions = [
        'head-open',
        'head-close',
        'body-open',
        'body-close',
    ];

    /**
     * @var RequestAnalyzerInterface
     */
    private $requestAnalyzer;

    /**
     * @var AnalyticsRepositoryInterface
     */
    private $analyticsRepository;

    /**
     * @var Environment
     */
    private $twig;

    /**
     * @var string
     */
    private $kernelEnvironment;

    public function __construct(
        RequestAnalyzerInterface $requestAnalyzer,
        AnalyticsRepositoryInterface $analyticsRepository,
        Environment $twig,
        string $kernelEnvironment
    ) {
        $this->requestAnalyzer = $requestAnalyzer;
        $this->analyticsRepository = $analyticsRepository;
        $this->twig = $twig;
        $this->kernelEnvironment = $kernelEnvironment;
    }

    /**
     * @Route(
     *     "/api/analytics",
     *     name="app.api_analytics",
     *     methods={"GET"}
     * )
     */
    public function getAction(Request $request): Response
    {
        $portalUrl = $this->requestAnalyzer->getAttribute('urlExpression');

        $analyticObjects = $this->analyticsRepository->findByUrl(
            $portalUrl,
            $this->requestAnalyzer->getPortalInformation()->getWebspaceKey(),
            $this->kernelEnvironment
        );

        $dataList = [];
        foreach ($analyticObjects as $analytics) {
            $dataList[] = $this->generateScriptData($analytics);
        }

        return new JsonResponse([
            '_embedded' => [
                'analytics' => $dataList,
            ],
        ]);
    }

    /**
     * @return array{
     *     id: int,
     *     title: string,
     *     type: string,
     *     key?: string,
     *     url?: string,
     *     siteId?: string,
     *     head-open?: string,
     *     head-close?: string,
     *     body-close?: string,
     *     body-close?: string,
     * }
     */
    protected function generateScriptData(AnalyticsInterface $analytics): array
    {
        $type = $analytics->getType();

        $data = [
            'id' => $analytics->getId(),
            'title' => $analytics->getTitle(),
            'type' => $type,
            'content' => [],
        ];

        $content = $analytics->getContent();
        if (is_string($content)) {
            $content = ['key' => $content];
        }

        $data['content'] = $content;

        $data['scripts'] = [];
        foreach (self::$positions as $position) {
            $template = '@SuluWebsite/Analytics/' . $analytics->getType() . '/' . $position . '.html.twig';

            if (!$this->twig->getLoader()->exists($template)) {
                continue;
            }

            $content = $this->twig->render($template, ['analytics' => $analytics]);
            if (!$content) {
                continue;
            }

            $data['scripts'][$position] = $content;
        }

        return $data;
    }
}

Which returns:

{
    "_embedded":{
        "analytics":[
            {
                "id":1,
                "title":"Analytics",
                "type":"google",
                "content":{
                    "key":"asdfasdf"
                },
                "scripts":{
                    "head-close":"\u003Cscript\u003E\n    (function(i,s,o,g,r,a,m){i[\u0027GoogleAnalyticsObject\u0027]=r;i[r]=i[r]||function(){\n                (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n            m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n    })(window,document,\u0027script\u0027,\u0027\/\/www.google-analytics.com\/analytics.js\u0027,\u0027ga\u0027);\n\n    ga(\u0027create\u0027, \u0027asdfasdf\u0027, \u0027auto\u0027);\n    ga(\u0027set\u0027, \u0027anonymizeIp\u0027, true);\n    ga(\u0027send\u0027, \u0027pageview\u0027);\n\n\u003C\/script\u003E\n"
                }
            },
            {
                "id":2,
                "title":"Tag Manager",
                "type":"google_tag_manager",
                "content":{
                    "key":"adfasdfasdf"
                },
                "scripts":{
                    "head-open":"\u003C!-- Google Tag Manager --\u003E\n\u003Cscript\u003E(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({\u0027gtm.start\u0027:\nnew Date().getTime(),event:\u0027gtm.js\u0027});var f=d.getElementsByTagName(s)[0],\nj=d.createElement(s),dl=l!=\u0027dataLayer\u0027?\u0027\u0026l=\u0027+l:\u0027\u0027;j.async=true;j.src=\n\u0027\/\/www.googletagmanager.com\/gtm.js?id=\u0027+i+dl;f.parentNode.insertBefore(j,f);\n})(window,document,\u0027script\u0027,\u0027dataLayer\u0027,\u0027adfasdfasdf\u0027);\u003C\/script\u003E\n\u003C!-- End Google Tag Manager --\u003E\n",
                    "body-open":"\u003C!-- Google Tag Manager (noscript) --\u003E\n\u003Cnoscript\u003E\u003Ciframe src=\u0022\/\/www.googletagmanager.com\/ns.html?id=adfasdfasdf\u0022\nheight=\u00220\u0022 width=\u00220\u0022 style=\u0022display:none;visibility:hidden\u0022\u003E\u003C\/iframe\u003E\u003C\/noscript\u003E\n\u003C!-- End Google Tag Manager (noscript) --\u003E\n"
                }
            },
            {
                "id":3,
                "title":"Matomo",
                "type":"matomo",
                "content":{
                    "url":"world",
                    "siteId":"adfasdf"
                },
                "scripts":{
                    "head-close":"\u003Cscript\u003E\n    var _paq = _paq || [];\n    _paq.push([\u0027trackPageView\u0027]);\n    _paq.push([\u0027enableLinkTracking\u0027]);\n    (function() {\n        var u=\u0022world\u0022;\n        _paq.push([\u0027setTrackerUrl\u0027, u+\u0027\/piwik.php\u0027]);\n        _paq.push([\u0027setSiteId\u0027, \u0027adfasdf\u0027]);\n        var d=document, g=d.createElement(\u0027script\u0027), s=d.getElementsByTagName(\u0027script\u0027)[0];\n        g.type=\u0027text\/javascript\u0027; g.async=true; g.defer=true; g.src=u+\u0027\/piwik.js\u0027; s.parentNode.insertBefore(g,s);\n    })();\n\u003C\/script\u003E\n",
                    "body-open":"\u003Cnoscript\u003E\u003Cp\u003E\u003Cimg src=\u0022world\/piwik.php?idsite=adfasdf\u0022 style=\u0022border:0;\u0022 alt=\u0022\u0022 \/\u003E\u003C\/p\u003E\u003C\/noscript\u003E\n"
                }
            },
            {
                "id":4,
                "title":"Custom",
                "type":"custom",
                "content":{
                    "value":"asdfasdf asdf asfd",
                    "position":"headClose"
                },
                "scripts":{
                    "head-close":"asdfasdf asdf asfd"
                }
            }
        ]
    }
}

It could be discussed if the scripts part is optional and can be enabled over /api/analytics?with-scripts=true.

@alexander-schranz alexander-schranz added this to the 1.0 Release milestone Sep 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DX Only affecting the end developer Feature New functionality not yet included in the Bundle
Projects
None yet
Development

No branches or pull requests

1 participant