Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2d5adfb
Initial plan
Copilot Feb 16, 2026
73e2734
feat: implement Google Analytics tracking code injection via WebInter…
Copilot Feb 16, 2026
f655433
refactor: improve GA interceptor with afterIntercept pattern
Copilot Feb 16, 2026
9efe677
docs: add Google Analytics auto-injection documentation and simple tests
Copilot Feb 16, 2026
4653fe9
docs: add comprehensive implementation summary
Copilot Feb 16, 2026
2878d8b
refactor: remove Universal Analytics support, GA4 only
Copilot Feb 16, 2026
f4a57f9
docs: remove documentation files per review feedback
Copilot Feb 16, 2026
2e5435c
Merge branch 'main' into copilot/add-google-analytics-injection
fmontes Feb 17, 2026
6f06983
refactor: replace auto-inject with ViewTool approach for GA4
Copilot Feb 17, 2026
251cf97
docs: add ViewTool usage guide and example template
Copilot Feb 17, 2026
21e6ee4
docs: add implementation complete summary
Copilot Feb 17, 2026
f3eada4
feat: make trackingCode a function with optional parameter
Copilot Feb 17, 2026
216c713
docs: update all documentation for function-style syntax
Copilot Feb 17, 2026
3f7ddcb
docs: add function-style implementation summary
Copilot Feb 17, 2026
abf3814
chore: clean up documentation files
Copilot Feb 17, 2026
5b3728a
Merge branch 'main' into copilot/add-google-analytics-injection
fmontes Feb 17, 2026
e0eea9a
Merge branch 'main' into copilot/add-google-analytics-injection
fmontes Feb 18, 2026
2a60006
Merge branch 'main' into copilot/add-google-analytics-injection
nollymar Feb 18, 2026
e1d637f
fix: resolve GoogleAnalyticsTool unit test failures
fmontes Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package com.dotcms.analytics.viewtool;

import com.dotmarketing.beans.Host;
import com.dotmarketing.business.web.HostWebAPI;
import com.dotmarketing.business.web.WebAPILocator;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.UtilMethods;
import org.apache.velocity.tools.view.context.ViewContext;
import org.apache.velocity.tools.view.tools.ViewTool;

import javax.servlet.http.HttpServletRequest;

/**
* ViewTool for generating Google Analytics 4 (GA4) tracking code in Velocity templates.
*
* This tool provides a simple way to include GA4 tracking code in templates by reading
* the tracking ID from the current site's googleAnalytics field, or by providing a custom
* tracking ID as a parameter.
*
* Usage in Velocity templates:
* <pre>
* ## Include GA4 tracking code from site configuration
* $googleAnalytics.trackingCode()
*
* ## Include GA4 tracking code with custom tracking ID
* $googleAnalytics.trackingCode("G-CUSTOM123")
*
* ## With null check
* #if($googleAnalytics.trackingId)
* $googleAnalytics.trackingCode()
* #end
* </pre>
*
* Benefits of manual inclusion:
* - Developers have full control over placement in the template
* - Can be conditionally included based on user consent
* - Can override tracking ID with custom value
* - No automatic HTML parsing/modification overhead
* - More transparent and easier to debug
*
* @author dotCMS
*/
public class GoogleAnalyticsTool implements ViewTool {

private static final String GOOGLE_ANALYTICS_PROPERTY = "googleAnalytics";

private HttpServletRequest request;
private HostWebAPI hostWebAPI;

/**
* Default constructor - resolves HostWebAPI lazily via WebAPILocator
*/
public GoogleAnalyticsTool() {
this(null);
}

/**
* Constructor for testing/dependency injection
*
* @param hostWebAPI the HostWebAPI to use, or null to resolve lazily
*/
public GoogleAnalyticsTool(final HostWebAPI hostWebAPI) {
this.hostWebAPI = hostWebAPI;
}

private HostWebAPI getHostWebAPI() {
if (this.hostWebAPI == null) {
this.hostWebAPI = WebAPILocator.getHostWebAPI();
}
return this.hostWebAPI;
}

@Override
public void init(final Object initData) {
if (initData instanceof ViewContext) {
this.request = ((ViewContext) initData).getRequest();
}
}

/**
* Gets the Google Analytics tracking ID from the current site.
*
* @return The GA4 tracking ID (e.g., "G-XXXXXXXXXX") or null if not set
*/
public String getTrackingId() {
try {
final Host site = getHostWebAPI().getCurrentHostNoThrow(request);
if (site != null) {
final String trackingId = site.getStringProperty(GOOGLE_ANALYTICS_PROPERTY);
if (UtilMethods.isSet(trackingId)) {
return trackingId;
}
}
} catch (Exception e) {
Logger.error(this, "Error retrieving Google Analytics tracking ID", e);
}
return null;
}

/**
* Generates the complete Google Analytics 4 tracking code using the site's configured tracking ID.
*
* This includes the gtag.js script tag and initialization code.
* Place this code in your template where you want the tracking code to appear
* (typically before the closing &lt;/body&gt; tag).
*
* Usage in Velocity:
* <pre>
* $googleAnalytics.trackingCode()
* </pre>
*
* @return The complete GA4 tracking code HTML, or empty string if no tracking ID is set
*/
public String trackingCode() {
final String trackingId = getTrackingId();

if (!UtilMethods.isSet(trackingId)) {
Logger.debug(this, "No Google Analytics tracking ID found for current site");
return "";
}

return generateGA4Script(trackingId);
}

/**
* Generates the complete Google Analytics 4 tracking code using a custom tracking ID.
*
* This allows overriding the site's tracking ID with a custom value, useful for
* multi-environment setups or testing purposes.
*
* Usage in Velocity:
* <pre>
* $googleAnalytics.trackingCode("G-CUSTOM123")
* </pre>
*
* @param customTrackingId the custom GA4 tracking ID to use (e.g., "G-XXXXXXXXXX")
* @return The complete GA4 tracking code HTML, or empty string if tracking ID is not set
*/
public String trackingCode(final String customTrackingId) {
if (!UtilMethods.isSet(customTrackingId)) {
Logger.debug(this, "Custom tracking ID is empty, falling back to site configuration");
return trackingCode();
}

return generateGA4Script(customTrackingId);
}

/**
* Generates the complete Google Analytics 4 tracking code.
*
* @deprecated Use {@link #trackingCode()} instead. This method is kept for backward compatibility.
* @return The complete GA4 tracking code HTML, or empty string if no tracking ID is set
*/
@Deprecated
public String getTrackingCode() {
return trackingCode();
}

/**
* Generates the GA4 tracking script with the given tracking ID.
*
* @param trackingId the GA4 tracking ID
* @return the formatted GA4 tracking script
*/
private String generateGA4Script(final String trackingId) {
return String.format(
"<!-- Google tag (gtag.js) -->\n" +
"<script async src=\"https://www.googletagmanager.com/gtag/js?id=%s\"></script>\n" +
"<script>\n" +
" window.dataLayer = window.dataLayer || [];\n" +
" function gtag(){dataLayer.push(arguments);}\n" +
" gtag('js', new Date());\n" +
" gtag('config', '%s');\n" +
"</script>",
trackingId, trackingId
);
}
}
5 changes: 5 additions & 0 deletions dotCMS/src/main/webapp/WEB-INF/toolbox.xml
Original file line number Diff line number Diff line change
Expand Up @@ -327,4 +327,9 @@
<scope>request</scope>
<class>com.dotcms.analytics.viewtool.AnalyticsTool</class>
</tool>
<tool>
<key>googleAnalytics</key>
<scope>request</scope>
<class>com.dotcms.analytics.viewtool.GoogleAnalyticsTool</class>
</tool>
</toolbox>
Loading
Loading