001/*
002 *  Copyright 2023 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.web.analytics;
017
018import java.net.URISyntaxException;
019import java.util.Random;
020
021import org.apache.commons.lang.StringUtils;
022
023import org.ametys.core.util.URIUtils;
024import org.ametys.web.repository.site.Site;
025
026/**
027 * The google analytics provider
028 */
029public class GoogleAnalyticsProvider extends AbstractWebAnalyticsProvider
030{
031    /** The analytics GIF URL. */
032    public static final String GA_GIF_URL = "https://www.google-analytics.com/__utm.gif";
033    
034    /** The analytics API version. */
035    public static final String GA_VERSION = "5.3.8";
036    
037    /** The domain hash. */
038    public static final String DOMAIN_HASH = "109627128"; // TODO Really hash the domain name?
039    
040    private static final Random _RNG = new Random((long) Math.random() * System.nanoTime());
041    
042    public String getEventImageUri(Site site, String category, String action, String label, int value, boolean usePlaceholderTokens)
043    {
044        String gaWebId = site.getValue("google-web-property-id");
045        if (StringUtils.isNotBlank(gaWebId))
046        {
047            String identifier = _getEventIdentifier(category, action, label, value);
048            return _getEventImageUri(gaWebId, identifier, usePlaceholderTokens);
049        }
050        
051        return null;
052    }
053    
054    /**
055     * Get the event identifier
056     * @param category the event category
057     * @param action the event action
058     * @param label the event label
059     * @param value the event value
060     * @return the event identifier
061     */
062    protected String _getEventIdentifier(String category, String action, String label, int value)
063    {
064        try
065        {
066            StringBuilder eventId = new StringBuilder();
067            
068            eventId.append("5(");
069            eventId.append(_encodeValue(category));
070            eventId.append('*').append(_encodeValue(action));
071            eventId.append('*').append(_encodeValue(label));
072            eventId.append(')');
073            if (value > 0)
074            {
075                eventId.append('(').append(value).append(')');
076            }
077            return eventId.toString();
078        }
079        catch (URISyntaxException e)
080        {
081            getLogger().error("An error occurred computing the event gif uri", e);
082        }
083        
084        return null;
085    }
086    
087    /**
088     * Get an event image URI.
089     * @param gaWebId the GA web ID.
090     * @param eventIdentifier the event identifier.
091     * @param usePlaceholderTokens True to use tokens instead of randomly generated values
092     * @return the event image URI.
093     */
094    protected String _getEventImageUri(String gaWebId, String eventIdentifier, boolean usePlaceholderTokens)
095    {
096        long now = System.currentTimeMillis() / 1000L;
097        
098        StringBuilder cookieValue = new StringBuilder();
099        // UTMA
100        cookieValue.append("__utma=").append(DOMAIN_HASH).append('.').append(usePlaceholderTokens ? RANDOM_NUMBER_TOKEN : _RNG.nextInt(Integer.MAX_VALUE));
101        cookieValue.append('.').append(now).append('.').append(now).append('.').append(now).append(".1");
102        // UTMZ
103        cookieValue.append(";+__utmz=").append(DOMAIN_HASH).append('.').append(now);
104        cookieValue.append(".1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none);");
105        
106        StringBuilder uri = new StringBuilder();
107        
108        uri.append(GA_GIF_URL);
109        uri.append("?utmwv=").append(GA_VERSION);
110        uri.append("&utmn=").append(usePlaceholderTokens ? RANDOM_NUMBER_TOKEN : _RNG.nextInt(Integer.MAX_VALUE));
111        uri.append("&utmt=event");
112        uri.append("&utme=").append(eventIdentifier);
113        uri.append("&utmcs=UTF-8");
114        uri.append("&utmje=1");
115        uri.append("&utmhid=").append(usePlaceholderTokens ? RANDOM_NUMBER_TOKEN : _RNG.nextInt(Integer.MAX_VALUE));
116        // TODO utmhn with main domain?
117        uri.append("&utmr=-");
118        uri.append("&utmac=").append(gaWebId);
119        
120        // Cookie
121        String encodedCookie = URIUtils.encodeParameter(cookieValue.toString());
122        uri.append("&utmcc=").append(encodedCookie);
123        
124        return uri.toString();
125    }
126    
127    public String getEventLinkCampaignParams(Site site, String campaign, String medium, String source)
128    {
129        StringBuilder linkCampaignParams = new StringBuilder();
130        linkCampaignParams.append("utm_campaign=");
131        linkCampaignParams.append(campaign);
132        linkCampaignParams.append("&utm_medium=");
133        linkCampaignParams.append(medium);
134        linkCampaignParams.append("&utm_source=");
135        linkCampaignParams.append(source);
136        
137        return linkCampaignParams.toString();
138    }
139}