Skip to content

Commit 29da031

Browse files
authored
Merge pull request #6154 from influxdata/jts-track-code-copy-events
chore(analytics): Track code block copy clicks on Core and Ent3 download commands
2 parents a925e3e + d20492b commit 29da031

File tree

4 files changed

+184
-11
lines changed

4 files changed

+184
-11
lines changed

assets/js/code-controls.js

Lines changed: 153 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import $ from 'jquery';
2+
import { context } from './page-context.js';
23

34
function initialize() {
45
var codeBlockSelector = '.article--content pre';
@@ -68,9 +69,94 @@ function initialize() {
6869
// Trigger copy failure state lifecycle
6970

7071
$('.copy-code').click(function () {
71-
let text = $(this)
72+
let codeElement = $(this)
7273
.closest('.code-controls')
73-
.prevAll('pre:has(code)')[0].innerText;
74+
.prevAll('pre:has(code)')[0];
75+
76+
let text = codeElement.innerText;
77+
78+
// Extract additional code block information
79+
const codeBlockInfo = extractCodeBlockInfo(codeElement);
80+
81+
// Add Google Analytics event tracking
82+
const currentUrl = new URL(window.location.href);
83+
84+
// Determine which tracking parameter to add based on product context
85+
switch (context) {
86+
case 'cloud':
87+
currentUrl.searchParams.set('dl', 'cloud');
88+
break;
89+
case 'core':
90+
/** Track using the same value used by www.influxdata.com pages */
91+
currentUrl.searchParams.set('dl', 'oss3');
92+
break;
93+
case 'enterprise':
94+
/** Track using the same value used by www.influxdata.com pages */
95+
currentUrl.searchParams.set('dl', 'enterprise');
96+
break;
97+
case 'serverless':
98+
currentUrl.searchParams.set('dl', 'serverless');
99+
break;
100+
case 'dedicated':
101+
currentUrl.searchParams.set('dl', 'dedicated');
102+
break;
103+
case 'clustered':
104+
currentUrl.searchParams.set('dl', 'clustered');
105+
break;
106+
case 'oss/enterprise':
107+
currentUrl.searchParams.set('dl', 'oss');
108+
break;
109+
case 'other':
110+
default:
111+
// No tracking parameter for other/unknown products
112+
break;
113+
}
114+
115+
// Add code block specific tracking parameters
116+
if (codeBlockInfo.language) {
117+
currentUrl.searchParams.set('code_lang', codeBlockInfo.language);
118+
}
119+
if (codeBlockInfo.lineCount) {
120+
currentUrl.searchParams.set('code_lines', codeBlockInfo.lineCount);
121+
}
122+
if (codeBlockInfo.hasPlaceholders) {
123+
currentUrl.searchParams.set('has_placeholders', 'true');
124+
}
125+
if (codeBlockInfo.blockType) {
126+
currentUrl.searchParams.set('code_type', codeBlockInfo.blockType);
127+
}
128+
if (codeBlockInfo.sectionTitle) {
129+
currentUrl.searchParams.set(
130+
'section',
131+
encodeURIComponent(codeBlockInfo.sectionTitle)
132+
);
133+
}
134+
if (codeBlockInfo.firstLine) {
135+
currentUrl.searchParams.set(
136+
'first_line',
137+
encodeURIComponent(codeBlockInfo.firstLine.substring(0, 100))
138+
);
139+
}
140+
141+
// Update browser history without triggering page reload
142+
if (window.history && window.history.replaceState) {
143+
window.history.replaceState(null, '', currentUrl.toString());
144+
}
145+
146+
// Send custom Google Analytics event if gtag is available
147+
if (typeof window.gtag !== 'undefined') {
148+
window.gtag('event', 'code_copy', {
149+
language: codeBlockInfo.language,
150+
line_count: codeBlockInfo.lineCount,
151+
has_placeholders: codeBlockInfo.hasPlaceholders,
152+
dl: codeBlockInfo.dl || null,
153+
section_title: codeBlockInfo.sectionTitle,
154+
first_line: codeBlockInfo.firstLine
155+
? codeBlockInfo.firstLine.substring(0, 100)
156+
: null,
157+
product: context,
158+
});
159+
}
74160

75161
const copyContent = async () => {
76162
try {
@@ -84,6 +170,71 @@ function initialize() {
84170
copyContent();
85171
});
86172

173+
/**
174+
* Extract contextual information about a code block
175+
* @param {HTMLElement} codeElement - The code block element
176+
* @returns {Object} Information about the code block
177+
*/
178+
function extractCodeBlockInfo(codeElement) {
179+
const codeTag = codeElement.querySelector('code');
180+
const info = {
181+
language: null,
182+
lineCount: 0,
183+
hasPlaceholders: false,
184+
blockType: 'code',
185+
dl: null, // Download script type
186+
sectionTitle: null,
187+
firstLine: null,
188+
};
189+
190+
// Extract language from class attribute
191+
if (codeTag && codeTag.className) {
192+
const langMatch = codeTag.className.match(
193+
/language-(\w+)|hljs-(\w+)|(\w+)/
194+
);
195+
if (langMatch) {
196+
info.language = langMatch[1] || langMatch[2] || langMatch[3];
197+
}
198+
}
199+
200+
// Count lines
201+
const text = codeElement.innerText || '';
202+
const lines = text.split('\n');
203+
info.lineCount = lines.length;
204+
205+
// Get first non-empty line
206+
info.firstLine = lines.find((line) => line.trim() !== '') || null;
207+
208+
// Check for placeholders (common patterns)
209+
info.hasPlaceholders =
210+
/\b[A-Z_]{2,}\b|\{\{[^}]+\}\}|\$\{[^}]+\}|<[^>]+>/.test(text);
211+
212+
// Determine if this is a download script
213+
if (text.includes('https://www.influxdata.com/d/install_influxdb3.sh')) {
214+
if (text.includes('install_influxdb3.sh enterprise')) {
215+
info.dl = 'enterprise';
216+
} else {
217+
info.dl = 'oss3';
218+
}
219+
} else if (text.includes('docker pull influxdb:3-enterprise')) {
220+
info.dl = 'enterprise';
221+
} else if (text.includes('docker pull influxdb3-core')) {
222+
info.dl = 'oss3';
223+
}
224+
225+
// Find nearest section heading
226+
let element = codeElement;
227+
while (element && element !== document.body) {
228+
element = element.previousElementSibling || element.parentElement;
229+
if (element && element.tagName && /^H[1-6]$/.test(element.tagName)) {
230+
info.sectionTitle = element.textContent.trim();
231+
break;
232+
}
233+
}
234+
235+
return info;
236+
}
237+
87238
/////////////////////////////// FULL WINDOW CODE ///////////////////////////////
88239

89240
/*

config/staging/hugo.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ minify:
1313
params:
1414
env: staging
1515
environment: staging
16-
server:
16+
server: {
1717
disableLiveReload: true
18+
}
1819

layouts/partials/header.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<!doctype html>
66
<html lang="en">
77
<head>
8-
{{ if not hugo.IsServer }}{{ partial "header/google-analytics-head.html" }}{{ end }}
8+
{{ partial "header/google-analytics-head.html" }}
99
<meta charset="utf-8">
1010
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
1111

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
1-
<!-- Google Tag Manager -->
2-
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
3-
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
4-
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
5-
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
6-
})(window,document,'script','dataLayer','GTM-WXRH9C');</script>
7-
<!-- End Google Tag Manager -->
1+
{{ if and hugo.IsServer (not (eq .Site.Params.environment "staging")) }}
2+
<!-- Development: GA4 in debug mode -->
3+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-3C2W1T8YS4"></script>
4+
<script>
5+
const gtagID = 'G-3C2W1T8YS4';
6+
window.dataLayer = window.dataLayer || [];
7+
function gtag(){dataLayer.push(arguments);}
8+
gtag('js', new Date());
9+
gtag('config', gtagID, {
10+
'debug_mode': true,
11+
'send_page_view': false,
12+
'transport_type': 'beacon'
13+
});
14+
console.log('GA4 loaded in debug mode -', gtagID);
15+
</script>
16+
{{ else }}
17+
<!-- Production/Staging: Normal GA4 -->
18+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-3C2W1T8YS4"></script>
19+
<script>
20+
window.dataLayer = window.dataLayer || [];
21+
function gtag(){dataLayer.push(arguments);}
22+
gtag('js', new Date());
23+
gtag('config', 'G-3C2W1T8YS4', {
24+
'send_page_view': true,
25+
'transport_type': 'beacon'
26+
});
27+
</script>
28+
{{ end }}

0 commit comments

Comments
 (0)