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

feat: support Google Tag Manager and Google Tags via an env variable #455

Merged
merged 10 commits into from
Sep 1, 2024
3 changes: 1 addition & 2 deletions variants/backend-base/app/template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

# Configure app/views
template "variants/backend-base/app/views/layouts/application.html.erb", "app/views/layouts/application.html.erb", force: true
copy_file "variants/backend-base/app/views/application/_flash.html.erb", "app/views/application/_flash.html.erb"
copy_file "variants/backend-base/app/views/application/_header.html.erb", "app/views/application/_header.html.erb"
directory "variants/backend-base/app/views/application", "app/views/application"
copy_file "variants/backend-base/app/views/home/index.html.erb", "app/views/home/index.html.erb"

# Configure app/helpers
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<%# analytics configuration that goes in the <body> %>

<% google_analytics_id = Rails.application.config.app.google_analytics_id %>

<% if google_analytics_id&.start_with?("GTM-") %>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<%= google_analytics_id %>"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<%# analytics configuration that goes in the <head> %>

<% google_analytics_id = Rails.application.config.app.google_analytics_id %>

<% if google_analytics_id&.start_with?("GTM-") %>
<!-- Google Tag Manager -->
<%= javascript_tag nonce: true, defer: true do -%>
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]');
n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','<%= google_analytics_id %>');
<% end -%>
<!-- End Google Tag Manager -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a quick look at how we implemented this on my most recent client project:

  <%= javascript_tag nonce: true, defer: true do -%>
    var dataLayer = [];

    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;var n=d.querySelector('[nonce]');
    n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer','GTM-XXXXXX');
  <% end -%>

There are two things I think we should steal from this:

  1. use javascript_tag nonce: true, ... to avoid the CSP yelling at us about an embedded <script> without a nonce
  2. Define the dataLayer variable in JS. GTM does not define this for you.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 maybe it would be worth linking to https://github.com/google/data-layer-helper in a comment - it's really the best dev focused docs I've found about how to actually use the dataLayer

<% end %>

<% if google_analytics_id&.start_with?("G-") %>
<!-- Google tag (gtag.js) -->
<%= javascript_include_tag("https://www.googletagmanager.com/gtag/js?id=#{google_analytics_id}", nonce: true, defer: false) -%>
<%= javascript_tag nonce: true, defer: true do -%>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());

gtag('config', '<%= google_analytics_id %>');
<% end -%>
<% end %>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<%= app_const_base.titleize %>
</title>

<%%= render "application/analytics/head" %>

<%%= csrf_meta_tags %>
<%%= csp_meta_tag %>

Expand All @@ -30,6 +32,8 @@
</head>

<body>
<%%= render "application/analytics/body" %>

<%%= render("application/header") %>
<%%= render("application/flash") %>

Expand Down
3 changes: 3 additions & 0 deletions variants/backend-base/config/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ default: &default
# The default `From:` address to use for email sent by this application
mail_from: "<%= ENV['MAIL_FROM'] %>"

# this should either begin with GTM- (for a container) or G- (for a tag)
google_analytics_id: "<%= ENV.fetch('GOOGLE_ANALYTICS_ID', nil) %>"

active_record_encryption_primary_key:
"<%= ENV.fetch('ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY') %>"
active_record_encryption_deterministic_key:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,25 @@
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy

Rails.application.config.content_security_policy do |policy|
google_analytics_enabled = Rails.application.config.app.google_analytics_id.present?

# These directives define a quite strict policy by default. You may have to
# loosen this policy as you add elements to the app. Some examples of common
# additions are shown below.
#
policy.default_src :self
policy.font_src :self
policy.img_src :self
policy.img_src :self, :data, *[
*(["*.googletagmanager.com", "*.google-analytics.com"] if google_analytics_enabled)
].compact
policy.object_src :none
policy.script_src :self
policy.script_src :self, *[
*(["*.googletagmanager.com"] if google_analytics_enabled)
].compact
policy.style_src :self
policy.frame_src :self, *[
*(["*.googletagmanager.com"] if google_analytics_enabled)
].compact

# Allow inline-styles
# ###################
Expand Down Expand Up @@ -90,8 +99,11 @@
# * We want to minimize differences in the CSP header between environments so
# that we can find and fix CSP issues in development but enabling the
# webpack-dev-server to communicate over websockets is an exception.
#
policy.connect_src :self, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
policy.connect_src :self, *[
*(["*.googletagmanager.com", "*.google-analytics.com", "*.analytics.google.com"] if google_analytics_enabled),
# required for webpack-dev-server to be used in local development
*(["http://localhost:3035", "ws://localhost:3035"] if Rails.env.development?)
].compact

# Enable CSP reporting
# ####################
Expand Down
Loading