Skip to content

Commit de9acd0

Browse files
authored
Merge pull request #96 from amplitude/glic-config-options
Capture GCLID url param, add option to disable saveParamsReferrerOncePerSession
2 parents 99acda0 + 0f46a16 commit de9acd0

File tree

12 files changed

+227
-313
lines changed

12 files changed

+227
-313
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Unreleased
22

3+
* Add option to track GCLID (Google Click ID) as a user property (set `includeGclid` to `true` in the SDK configuration).
4+
* Add option to track new UTM parameters, referrer, and GCLID values during the same session. By default the SDK only saves the values once at the start of the session. You can remove this restriction by setting `saveParamsReferrerOncePerSession` to `false` in the SDK configuration. See the [Readme](https://github.com/amplitude/Amplitude-Javascript#tracking-utm-parameters-referrer-and-gclid) for more information.
5+
36
### 3.2.0 (October 7, 2016)
47

58
* Block event property and user property dictionaries that have more than 1000 items. This is to block properties that are set unintentionally (for example in a loop). A single call to `logEvent` should not have more than 1000 event properties. Similarly a single call to `setUserProperties` should not have more than 1000 user properties.

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,19 +330,32 @@ amplitude.getInstance().init('YOUR_API_KEY_HERE', null, {
330330
| eventUploadPeriodMillis | number | Amount of time in milliseconds that the SDK waits before uploading events if `batchEvents` is `true`. | 30\*1000 (30 sec) |
331331
| eventUploadThreshold | number | Minimum number of events to batch together per request if `batchEvents` is `true`. | 30 |
332332
| forceHttps | boolean | If `true`, the events will always be uploaded to HTTPS endpoint. Otherwise it will use the embedding site's protocol. | `false` |
333+
| includeGclid | boolean | If `true`, captures the `gclid` url parameter as well as the user's `initial_gclid` via a set once operation. | `false` |
333334
| includeReferrer | boolean | If `true`, captures the `referrer` and `referring_domain` for each session, as well as the user's `initial_referrer` and `initial_referring_domain` via a set once operation. | `false` |
334335
| includeUtm | boolean | If `true`, finds utm parameters in the query string or the __utmz cookie, parses, and includes them as user propeties on all events uploaded. Also captures initial utm parameters for each session via a set once operation. | `false` |
335336
| language | string | Custom language to set | Language determined by browser |
336337
| optOut | boolean | Whether to disable tracking for the current user | `false` |
337338
| platform | string | Custom platform to set | 'Web' |
338339
| saveEvents | boolean | If `true`, saves events to localStorage and removes them upon successful upload.<br><i>NOTE:</i> Without saving events, events may be lost if the user navigates to another page before events are uploaded. | `true` |
339340
| savedMaxCount | number | Maximum number of events to save in localStorage. If more events are logged while offline, old events are removed. | 1000 |
341+
| saveParamsReferrerOncePerSession | boolean | If `true` then `includeGclid`, `includeReferrer`, and `includeUtm` will only track their respective properties once per session. New values that come in during the middle of the user's session will be ignored. Set to `false` to always capture new values. | `true` |
340342
| sessionTimeout | number | Time between logged events before a new session starts in milliseconds | 30\*60\*1000 (30 min) |
341343
| uploadBatchSize | number | Maximum number of events to send to the server per request. | 100 |
342344

343345
# Advanced #
344346
This SDK automatically grabs useful data about the browser, including browser type and operating system version.
345347

348+
### Tracking UTM Parameters, Referrer, and GCLID ###
349+
350+
Amplitude supports automatically tracking:
351+
* Standard UTM parameters from the user's cookie or URL parameters, just set configuration option `includeUtm` to `true` during initialization.
352+
* The referring URL, just set configuration option `includeReferrer` to `true` during initialization.
353+
* GCLID (Google Click ID) from URL params, just set configuration option `includeGclid` to `true` during initialization.
354+
355+
If tracking is enabled, then the SDK will set the values as user properties, for example `referrer` or `utm_source`, once per session (this is last touch). The SDK will also save the initial values using a `setOnce` operation, for example `initial_referrer` or `initial_utm_source`, and once set that value will never change (this is first touch).
356+
357+
**Note:** By default the SDK will only save the values at the start of the session. For example if a user lands on your site with an initial set of UTM parameters, triggers some flow that causes them to land on your site again with a different set of UTM parameters within the same Amplitude session, that second set will not be saved. You can set configuration option `saveParamsReferrerOncePerSession` to `false` to remove that restriction, so the SDK will always capture any new values from the user.
358+
346359
### Setting Groups ###
347360
348361
Amplitude supports assigning users to groups, and performing queries such as Count by Distinct on those groups. An example would be if you want to group your users based on what organization they are in by using an orgId. You can designate Joe to be in orgId 10, while Sue is in orgId 15. When performing an event segmentation query, you can then select Count by Distinct orgIds to query the number of different orgIds that have performed a specific event. As long as at least one member of that group has performed the specific event, that group will be included in the count. See our help article on [Count By Distinct](https://amplitude.zendesk.com/hc/en-us/articles/218824237) for more information.

amplitude.js

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -572,13 +572,15 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o
572572
this._sessionId = now;
573573

574574
// only capture UTM params and referrer if new session
575-
if (this.options.includeUtm) {
576-
this._initUtmData();
577-
}
578-
if (this.options.includeReferrer) {
579-
this._saveReferrer(this._getReferrer());
575+
if (this.options.saveParamsReferrerOncePerSession) {
576+
this._trackParamsAndReferrer();
580577
}
581578
}
579+
580+
if (!this.options.saveParamsReferrerOncePerSession) {
581+
this._trackParamsAndReferrer();
582+
}
583+
582584
this._lastEventTime = now;
583585
_saveCookieData(this);
584586

@@ -613,6 +615,21 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o
613615
}
614616
};
615617

618+
/**
619+
* @private
620+
*/
621+
AmplitudeClient.prototype._trackParamsAndReferrer = function _trackParamsAndReferrer() {
622+
if (this.options.includeUtm) {
623+
this._initUtmData();
624+
}
625+
if (this.options.includeReferrer) {
626+
this._saveReferrer(this._getReferrer());
627+
}
628+
if (this.options.includeGclid) {
629+
this._saveGclid(this._getUrlParams());
630+
}
631+
};
632+
616633
/**
617634
* Parse and validate user specified config values and overwrite existing option value
618635
* DEFAULT_OPTIONS provides list of all config keys that are modifiable, as well as expected types for values
@@ -913,17 +930,18 @@ var _saveCookieData = function _saveCookieData(scope) {
913930
* @private
914931
*/
915932
AmplitudeClient.prototype._initUtmData = function _initUtmData(queryParams, cookieParams) {
916-
queryParams = queryParams || location.search;
933+
queryParams = queryParams || this._getUrlParams();
917934
cookieParams = cookieParams || this.cookieStorage.get('__utmz');
918935
var utmProperties = getUtmData(cookieParams, queryParams);
919-
_sendUserPropertiesOncePerSession(this, Constants.UTM_PROPERTIES, utmProperties);
936+
_sendParamsReferrerUserProperties(this, utmProperties);
920937
};
921938

922939
/**
923-
* Since user properties are propagated on server, only send once per session, don't need to send with every event
940+
* The calling function should determine when it is appropriate to send these user properties. This function
941+
* will no longer contain any session storage checking logic.
924942
* @private
925943
*/
926-
var _sendUserPropertiesOncePerSession = function _sendUserPropertiesOncePerSession(scope, storageKey, userProperties) {
944+
var _sendParamsReferrerUserProperties = function _sendParamsReferrerUserProperties(scope, userProperties) {
927945
if (type(userProperties) !== 'object' || Object.keys(userProperties).length === 0) {
928946
return;
929947
}
@@ -933,20 +951,7 @@ var _sendUserPropertiesOncePerSession = function _sendUserPropertiesOncePerSessi
933951
for (var key in userProperties) {
934952
if (userProperties.hasOwnProperty(key)) {
935953
identify.setOnce('initial_' + key, userProperties[key]);
936-
}
937-
}
938-
939-
// only save userProperties if not already in sessionStorage under key or if storage disabled
940-
var hasSessionStorage = utils.sessionStorageEnabled();
941-
if ((hasSessionStorage && !(scope._getFromStorage(sessionStorage, storageKey))) || !hasSessionStorage) {
942-
for (var property in userProperties) {
943-
if (userProperties.hasOwnProperty(property)) {
944-
identify.set(property, userProperties[property]);
945-
}
946-
}
947-
948-
if (hasSessionStorage) {
949-
scope._setInStorage(sessionStorage, storageKey, JSON.stringify(userProperties));
954+
identify.set(key, userProperties[key]);
950955
}
951956
}
952957

@@ -960,6 +965,26 @@ AmplitudeClient.prototype._getReferrer = function _getReferrer() {
960965
return document.referrer;
961966
};
962967

968+
/**
969+
* @private
970+
*/
971+
AmplitudeClient.prototype._getUrlParams = function _getUrlParams() {
972+
return location.search;
973+
};
974+
975+
/**
976+
* Try to fetch Google Gclid from url params.
977+
* @private
978+
*/
979+
AmplitudeClient.prototype._saveGclid = function _saveGclid(urlParams) {
980+
var gclid = utils.getQueryParam('gclid', urlParams);
981+
if (utils.isEmptyString(gclid)) {
982+
return;
983+
}
984+
var gclidProperties = {'gclid': gclid};
985+
_sendParamsReferrerUserProperties(this, gclidProperties);
986+
};
987+
963988
/**
964989
* Parse the domain from referrer info
965990
* @private
@@ -988,7 +1013,7 @@ AmplitudeClient.prototype._saveReferrer = function _saveReferrer(referrer) {
9881013
'referrer': referrer,
9891014
'referring_domain': this._getReferringDomain(referrer)
9901015
};
991-
_sendUserPropertiesOncePerSession(this, Constants.REFERRER, referrerInfo);
1016+
_sendParamsReferrerUserProperties(this, referrerInfo);
9921017
};
9931018

9941019
/**
@@ -1632,9 +1657,7 @@ module.exports = {
16321657
LAST_EVENT_TIME: 'amplitude_lastEventTime',
16331658
LAST_IDENTIFY_ID: 'amplitude_lastIdentifyId',
16341659
LAST_SEQUENCE_NUMBER: 'amplitude_lastSequenceNumber',
1635-
REFERRER: 'amplitude_referrer',
16361660
SESSION_ID: 'amplitude_sessionId',
1637-
UTM_PROPERTIES: 'amplitude_utm_properties',
16381661

16391662
// Used in cookie as well
16401663
DEVICE_ID: 'amplitude_deviceId',
@@ -2864,9 +2887,18 @@ var validateGroupName = function validateGroupName(key, groupName) {
28642887
'. Please use strings or array of strings for groupName');
28652888
};
28662889

2890+
// parses the value of a url param (for example ?gclid=1234&...)
2891+
var getQueryParam = function getQueryParam(name, query) {
2892+
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
2893+
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
2894+
var results = regex.exec(query);
2895+
return results === null ? undefined : decodeURIComponent(results[1].replace(/\+/g, " "));
2896+
};
2897+
28672898
module.exports = {
28682899
log: log,
28692900
isEmptyString: isEmptyString,
2901+
getQueryParam: getQueryParam,
28702902
sessionStorageEnabled: sessionStorageEnabled,
28712903
truncate: truncate,
28722904
validateGroups: validateGroups,
@@ -3029,20 +3061,13 @@ module.exports = localStorage;
30293061
13: [function(require, module, exports) {
30303062
var utils = require('./utils');
30313063

3032-
var getUtmParam = function getUtmParam(name, query) {
3033-
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
3034-
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
3035-
var results = regex.exec(query);
3036-
return results === null ? undefined : decodeURIComponent(results[1].replace(/\+/g, " "));
3037-
};
3038-
30393064
var getUtmData = function getUtmData(rawCookie, query) {
30403065
// Translate the utmz cookie format into url query string format.
30413066
var cookie = rawCookie ? '?' + rawCookie.split('.').slice(-1)[0].replace(/\|/g, '&') : '';
30423067

30433068
var fetchParam = function fetchParam(queryName, query, cookieName, cookie) {
3044-
return getUtmParam(queryName, query) ||
3045-
getUtmParam(cookieName, cookie);
3069+
return utils.getQueryParam(queryName, query) ||
3070+
utils.getQueryParam(cookieName, cookie);
30463071
};
30473072

30483073
var utmSource = fetchParam('utm_source', query, 'utmcsr', cookie);
@@ -4950,6 +4975,8 @@ module.exports = {
49504975
eventUploadThreshold: 30,
49514976
eventUploadPeriodMillis: 30 * 1000, // 30s
49524977
forceHttps: false,
4978+
includeGclid: false,
4979+
saveParamsReferrerOncePerSession: true
49534980
};
49544981

49554982
}, {"./language":29}],

amplitude.min.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/amplitude-client.js

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,15 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o
9191
this._sessionId = now;
9292

9393
// only capture UTM params and referrer if new session
94-
if (this.options.includeUtm) {
95-
this._initUtmData();
96-
}
97-
if (this.options.includeReferrer) {
98-
this._saveReferrer(this._getReferrer());
94+
if (this.options.saveParamsReferrerOncePerSession) {
95+
this._trackParamsAndReferrer();
9996
}
10097
}
98+
99+
if (!this.options.saveParamsReferrerOncePerSession) {
100+
this._trackParamsAndReferrer();
101+
}
102+
101103
this._lastEventTime = now;
102104
_saveCookieData(this);
103105

@@ -132,6 +134,21 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o
132134
}
133135
};
134136

137+
/**
138+
* @private
139+
*/
140+
AmplitudeClient.prototype._trackParamsAndReferrer = function _trackParamsAndReferrer() {
141+
if (this.options.includeUtm) {
142+
this._initUtmData();
143+
}
144+
if (this.options.includeReferrer) {
145+
this._saveReferrer(this._getReferrer());
146+
}
147+
if (this.options.includeGclid) {
148+
this._saveGclid(this._getUrlParams());
149+
}
150+
};
151+
135152
/**
136153
* Parse and validate user specified config values and overwrite existing option value
137154
* DEFAULT_OPTIONS provides list of all config keys that are modifiable, as well as expected types for values
@@ -432,17 +449,18 @@ var _saveCookieData = function _saveCookieData(scope) {
432449
* @private
433450
*/
434451
AmplitudeClient.prototype._initUtmData = function _initUtmData(queryParams, cookieParams) {
435-
queryParams = queryParams || location.search;
452+
queryParams = queryParams || this._getUrlParams();
436453
cookieParams = cookieParams || this.cookieStorage.get('__utmz');
437454
var utmProperties = getUtmData(cookieParams, queryParams);
438-
_sendUserPropertiesOncePerSession(this, Constants.UTM_PROPERTIES, utmProperties);
455+
_sendParamsReferrerUserProperties(this, utmProperties);
439456
};
440457

441458
/**
442-
* Since user properties are propagated on server, only send once per session, don't need to send with every event
459+
* The calling function should determine when it is appropriate to send these user properties. This function
460+
* will no longer contain any session storage checking logic.
443461
* @private
444462
*/
445-
var _sendUserPropertiesOncePerSession = function _sendUserPropertiesOncePerSession(scope, storageKey, userProperties) {
463+
var _sendParamsReferrerUserProperties = function _sendParamsReferrerUserProperties(scope, userProperties) {
446464
if (type(userProperties) !== 'object' || Object.keys(userProperties).length === 0) {
447465
return;
448466
}
@@ -452,20 +470,7 @@ var _sendUserPropertiesOncePerSession = function _sendUserPropertiesOncePerSessi
452470
for (var key in userProperties) {
453471
if (userProperties.hasOwnProperty(key)) {
454472
identify.setOnce('initial_' + key, userProperties[key]);
455-
}
456-
}
457-
458-
// only save userProperties if not already in sessionStorage under key or if storage disabled
459-
var hasSessionStorage = utils.sessionStorageEnabled();
460-
if ((hasSessionStorage && !(scope._getFromStorage(sessionStorage, storageKey))) || !hasSessionStorage) {
461-
for (var property in userProperties) {
462-
if (userProperties.hasOwnProperty(property)) {
463-
identify.set(property, userProperties[property]);
464-
}
465-
}
466-
467-
if (hasSessionStorage) {
468-
scope._setInStorage(sessionStorage, storageKey, JSON.stringify(userProperties));
473+
identify.set(key, userProperties[key]);
469474
}
470475
}
471476

@@ -479,6 +484,26 @@ AmplitudeClient.prototype._getReferrer = function _getReferrer() {
479484
return document.referrer;
480485
};
481486

487+
/**
488+
* @private
489+
*/
490+
AmplitudeClient.prototype._getUrlParams = function _getUrlParams() {
491+
return location.search;
492+
};
493+
494+
/**
495+
* Try to fetch Google Gclid from url params.
496+
* @private
497+
*/
498+
AmplitudeClient.prototype._saveGclid = function _saveGclid(urlParams) {
499+
var gclid = utils.getQueryParam('gclid', urlParams);
500+
if (utils.isEmptyString(gclid)) {
501+
return;
502+
}
503+
var gclidProperties = {'gclid': gclid};
504+
_sendParamsReferrerUserProperties(this, gclidProperties);
505+
};
506+
482507
/**
483508
* Parse the domain from referrer info
484509
* @private
@@ -507,7 +532,7 @@ AmplitudeClient.prototype._saveReferrer = function _saveReferrer(referrer) {
507532
'referrer': referrer,
508533
'referring_domain': this._getReferringDomain(referrer)
509534
};
510-
_sendUserPropertiesOncePerSession(this, Constants.REFERRER, referrerInfo);
535+
_sendParamsReferrerUserProperties(this, referrerInfo);
511536
};
512537

513538
/**

src/constants.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ module.exports = {
1010
LAST_EVENT_TIME: 'amplitude_lastEventTime',
1111
LAST_IDENTIFY_ID: 'amplitude_lastIdentifyId',
1212
LAST_SEQUENCE_NUMBER: 'amplitude_lastSequenceNumber',
13-
REFERRER: 'amplitude_referrer',
1413
SESSION_ID: 'amplitude_sessionId',
15-
UTM_PROPERTIES: 'amplitude_utm_properties',
1614

1715
// Used in cookie as well
1816
DEVICE_ID: 'amplitude_deviceId',

src/options.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ module.exports = {
2121
eventUploadThreshold: 30,
2222
eventUploadPeriodMillis: 30 * 1000, // 30s
2323
forceHttps: false,
24+
includeGclid: false,
25+
saveParamsReferrerOncePerSession: true
2426
};

src/utils.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,18 @@ var validateGroupName = function validateGroupName(key, groupName) {
188188
'. Please use strings or array of strings for groupName');
189189
};
190190

191+
// parses the value of a url param (for example ?gclid=1234&...)
192+
var getQueryParam = function getQueryParam(name, query) {
193+
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
194+
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
195+
var results = regex.exec(query);
196+
return results === null ? undefined : decodeURIComponent(results[1].replace(/\+/g, " "));
197+
};
198+
191199
module.exports = {
192200
log: log,
193201
isEmptyString: isEmptyString,
202+
getQueryParam: getQueryParam,
194203
sessionStorageEnabled: sessionStorageEnabled,
195204
truncate: truncate,
196205
validateGroups: validateGroups,

0 commit comments

Comments
 (0)