Last active
October 31, 2023 10:05
Revisions
-
sperand-io revised this gist
Jun 10, 2019 . 2 changed files with 189 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,188 @@ // To get started, make sure you're using the latest version of the analytics.js snippet (4.1.0 or above) // and remove the `analytics.load("YOUR_WRITE_KEY")` call. then drop this script below the snippet. // this is a standalone script for modern browsers. if you'd like to target older browsers, include // a fetch polyfill and use that instead of window.fetch. This would require that you build and package the // script somehow (rollup, webpack, browserify, etc). You may also want to transpile it to ES5 w eg Babel. // This script is configured to make all collection opt-in (rather than opt-out) for all users. // If you want to conditionally require whether or not to block data collection before affirmative consent, use something // like inEU below and pass that function into `conditionallyLoadAnalytics`. If you want to collect data until the user // opts out, just change OPT_IN to false. // import fetch from 'isomorphic-fetch' // import inEU from '@segment/in-eu' // CONFIG — EDIT ME! // CONFIG — EDIT ME! const OPT_IN = true; // false = only disable after opt out, can replace with a function such as inEU above const YOUR_DOMAIN = "domain.com"; // the hostname of your website const WEBSITE_WRITE_KEY = "sNOks4InIbuBaPcSQa76ny0neogp6yDf"; // your segment website source write key const SEGMENT_MAPPING = 1; const OTHER_WRITE_KEYS = []; // any other sources w destinations that you want to include in config // gets enabled destination configurations from across your source (and optionally other sources in workspace) fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]).then( destinations => { // KEY LOGIC HERE // eg. could instead use trustArc's other APIs such as getConsentCategories // and map between the returned vendor domains to specific Segment Integrationss, etc // review this logic carefully, and edit per your business requirements // // in this case, we go with all when customer provides full consent // remove Advertising tools when the customer provides functional // and mark Segment as required (for data to flow to warehouse etc— if you make that clear in your policy) // note that though Segment will receive data we wont forward (even server side) to other sources because we // decorate the calls with the full set of integrations you have enabled with "false." // // no preference here goes with all on — you may want to flip that! const { consentDecision } = truste.cma.callApi( "getGDPRConsentDecision", YOUR_DOMAIN ); const destinationPreferences = destinations .map(function(dest) { if (consentDecision.includes(3)) return { [dest.id]: true }; if (consentDecision.includes(2)) return { [dest.id]: dest.category === "Advertising" ? false : true }; if (consentDecision.includes(1)) return { [dest.id]: false }; if (consentDecision.includes(0)) return { [dest.id]: !OPT_IN }; }) .reduce( (acc, val) => { return { ...val, ...acc }; }, { "Segment.io": consentDecision.some(d => d === SEGMENT_MAPPING) } ); conditionallyLoadAnalytics({ writeKey: WEBSITE_WRITE_KEY, destinations, destinationPreferences, isConsentRequired: OPT_IN }); } ); // instructs trustarc to notify on changes to consent window.top.postMessage(JSON.stringify({ PrivacyManagerAPI: { action: "getConsent", timestamp: new Date().getTime(), self: YOUR_DOMAIN } }), "*"); // registers listener for consent changes. // some care has been taken here to handle messages safely. // will reload window if they've "denied"... to reset tracking. window.addEventListener("message", function reload(e) { let data = e.data; if (typeof data === "string") { try { data = JSON.parse(data); } catch (e) { /* weird message, bail */} } if ( data && data.PrivacyManagerAPI && data.PrivacyManagerAPI.consent === "denied" ) { return window.location.reload(); } // otherwise approved... carry on! }, false); // helper functions below... function conditionallyLoadAnalytics({ writeKey, destinations, destinationPreferences, isConsentRequired, shouldReload = true // change if you dont want to reload on consent changes }) { let isAnythingEnabled = false; if (!destinationPreferences) { if (isConsentRequired) { return; } // Load a.js normally when consent isn't required and there's no preferences if (!window.analytics.initialized) { window.analytics.load(writeKey); } return; } for (const destination of destinationPreferences) { const isEnabled = destinationPreferences[destination]; if (isEnabled) { isAnythingEnabled = true; } } // Reload the page if the trackers have already been initialized so that // the user's new preferences can take effect if (window.analytics.initialized) { if (shouldReload) { window.location.reload(); } return; } // Don't load a.js at all if nothing has been enabled if (isAnythingEnabled) { window.analytics.load(writeKey, { integrations: destinationPreferences }); } } async function fetchDestinationForWriteKey(writeKey) { const res = await window.fetch( `https://cdn.segment.com/v1/projects/${writeKey}/integrations` ); if (!res.ok) { throw new Error( `Failed to fetch integrations for write key ${writeKey}: HTTP ${ res.status } ${res.statusText}` ); } const destinations = await res.json(); // Rename creationName to id to abstract the weird data model for (const destination of destinations) { destination.id = destination.creationName; delete destination.creationName; } return destinations; } async function fetchDestinations(...writeKeys) { const destinationsRequests = []; for (const writeKey of writeKeys) { destinationsRequests.push(fetchDestinationForWriteKey(writeKey)); } let destinations = await Promise.all(destinationsRequests); // unique list of destination across all sources destinations = [ ...destinations .reduce((a, b) => a.concat(b), []) // flatten multi-d array .reduce((map, item) => { if (d.id === "Repeater") return map; // remove repeater map.has(item["id"]) || map.set(item["id"], item); return map; }, new Map()) // return object .values() ]; return destinations; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -129,7 +129,7 @@ function conditionallyLoadAnalytics({ // the user's new preferences can take affect if (window.analytics.initialized) { if (shouldReload) { window.location.reload(); } return; } -
sperand-io revised this gist
Jun 7, 2019 . 1 changed file with 1 addition and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -104,7 +104,6 @@ function conditionallyLoadAnalytics({ isConsentRequired, shouldReload = true // change if you dont want to reload on consent changes }) { let isAnythingEnabled = false; if (!destinationPreferences) { @@ -124,7 +123,6 @@ function conditionallyLoadAnalytics({ if (isEnabled) { isAnythingEnabled = true; } } // Reload the page if the trackers have already been initialised so that @@ -138,7 +136,7 @@ function conditionallyLoadAnalytics({ // Don't load a.js at all if nothing has been enabled if (isAnythingEnabled) { window.analytics.load(writeKey, { integrations: destinationPreferences }); } } -
sperand-io revised this gist
Jun 7, 2019 . 1 changed file with 1 addition and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -44,8 +44,7 @@ fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]).then( const destinationPreferences = destinations .map(function(dest) { if (consentDecision === 3) return { [dest.id]: true }; if (consentDecision === 2) return { [dest.id]: dest.category === "Advertising" ? false : true }; if (consentDecision === 1) return { [dest.id]: false }; if (consentDecision === 0) return { [dest.id]: !OPT_IN }; }) -
sperand-io revised this gist
Jun 7, 2019 . 1 changed file with 9 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -68,7 +68,7 @@ fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]).then( } ); // instructs trustarc to notify on changes to consent window.top.postMessage(JSON.stringify({ PrivacyManagerAPI: { action: "getConsent", @@ -77,9 +77,16 @@ window.top.postMessage(JSON.stringify({ } }), "*"); // registers listener for consent changes. // some care has been taken here to handle messages safely. // will reload window if they've "denied"... to reset tracking. window.addEventListener("message", function reload(e) { let data = e.data; if (typeof data === "string") { try { data = JSON.parse(data); } catch (e) { /* weird message, bail */} } if ( data && data.PrivacyManagerAPI && -
sperand-io revised this gist
Jun 7, 2019 . 1 changed file with 14 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -70,18 +70,23 @@ fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]).then( // registers a listener for consent changes, will reload window if they've denied to reset tracking window.top.postMessage(JSON.stringify({ PrivacyManagerAPI: { action: "getConsent", timestamp: new Date().getTime(), self: YOUR_DOMAIN } }), "*"); window.addEventListener("message", function reload(e) { let data = e.data; if (typeof data === "string") data = JSON.parse(data); if ( data && data.PrivacyManagerAPI && data.PrivacyManagerAPI.consent === "denied" ) { return window.location.reload(); } // otherwise approved... carry on! }, false); -
sperand-io revised this gist
Jun 7, 2019 . 2 changed files with 88 additions and 80 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -14,52 +14,67 @@ // import inEU from '@segment/in-eu' // CONFIG — EDIT ME! // CONFIG — EDIT ME! const OPT_IN = true; // false = only disable after opt out, can replace with a function such as inEU above const YOUR_DOMAIN = "domain.com"; // the hostname of your website const WEBSITE_WRITE_KEY = "sNOks4InIbuBaPcSQa76ny0neogp6yDf"; // your segment website source write key const SEGMENT_MAPPING = 1; const OTHER_WRITE_KEYS = []; // any other sources w destinations that you want to include in config // gets enabled destination configurations from across your source (and optionally other sources in workspace) fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]).then( destinations => { // KEY LOGIC HERE // eg. could instead use trustArc's other APIs such as getConsentCategories // and map between the returned vendor domains to specific Segment Integrationss, etc // review this logic carefully, and edit per your business requirements // // in this case, we go with all when customer provides full consent // remove Advertising tools when the customer provides functional // and mark Segment as required (for data to flow to warehouse etc— if you make that clear in your policy) // note that though Segment will receive data we wont forward (even server side) to other sources because we // decorate the calls with the full set of integrations you have enabled with "false." // // no preference here goes with all on — you may want to flip that! const consentDecision = truste.cma.callApi( "getConsentDecision", YOUR_DOMAIN ); const destinationPreferences = destinations .map(function(dest) { if (consentDecision === 3) return { [dest.id]: true }; if (consentDecision === 2) return { [dest.id]: dest.category === "Advertising" ? false : true }; if (consentDecision === 1) return { [dest.id]: false }; if (consentDecision === 0) return { [dest.id]: !OPT_IN }; }) .reduce( (acc, val) => { return { ...val, ...acc }; }, { "Segment.io": consentDecision >= SEGMENT_MAPPING } ); conditionallyLoadAnalytics({ writeKey: WEBSITE_WRITE_KEY, destinations, destinationPreferences, isConsentRequired: OPT_IN }); } ); // registers a listener for consent changes, will reload window if they've denied to reset tracking window.top.postMessage(JSON.stringify({ PrivacyManagerAPI: { action: "getConsent", timestamp: new Date().getTime(), self: YOUR_DOMAIN } }), "*"); window.addEventListener("message", function reload(e) { @@ -68,7 +83,7 @@ window.addEventListener("message", function reload(e) { return window.location.reload(); } // otherwise approved... carry on! }, false); // helper functions below... function conditionallyLoadAnalytics({ @@ -78,84 +93,87 @@ function conditionallyLoadAnalytics({ isConsentRequired, shouldReload = true // change if you dont want to reload on consent changes }) { const integrations = { "Segment.io": true }; let isAnythingEnabled = false; if (!destinationPreferences) { if (isConsentRequired) { return; } // Load a.js normally when consent isn't required and there's no preferences if (!window.analytics.initialized) { window.analytics.load(writeKey); } return; } for (const destination of destinationPreferences) { const isEnabled = destinationPreferences[destination]; if (isEnabled) { isAnythingEnabled = true; } integrations[destination.id] = isEnabled; } // Reload the page if the trackers have already been initialised so that // the user's new preferences can take affect if (window.analytics.initialized) { if (shouldReload) { // window.location.reload(); } return; } // Don't load a.js at all if nothing has been enabled if (isAnythingEnabled) { window.analytics.load(writeKey, { destinationPreferences }); } } async function fetchDestinationForWriteKey(writeKey) { const res = await window.fetch( `https://cdn.segment.com/v1/projects/${writeKey}/integrations` ); if (!res.ok) { throw new Error( `Failed to fetch integrations for write key ${writeKey}: HTTP ${ res.status } ${res.statusText}` ); } const destinations = await res.json(); // Rename creationName to id to abstract the weird data model for (const destination of destinations) { destination.id = destination.creationName; delete destination.creationName; } return destinations; } async function fetchDestinations(...writeKeys) { const destinationsRequests = []; for (const writeKey of writeKeys) { destinationsRequests.push(fetchDestinationForWriteKey(writeKey)); } let destinations = await Promise.all(destinationsRequests); // unique list of destination across all sources destinations = [ ...destinations .reduce((a, b) => a.concat(b), []) // flatten multi-d array .filter(d => d.id !== "Repeater") // remove repeater .reduce((map, item) => { map.has(item["id"]) || map.set(item["id"], item); return map; }, new Map()) .values() ]; return destinations; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,10 +0,0 @@ -
sperand-io revised this gist
Jun 5, 2019 . 1 changed file with 32 additions and 28 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -14,39 +14,43 @@ // import inEU from '@segment/in-eu' // CONFIG — EDIT ME! const REQUIRE_CONSENT = false // false = only disable after opt out, can replace with a function such as inEU above const YOUR_DOMAIN = 'domain.com' // the hostname of your website const WEBSITE_WRITE_KEY = 'writeKey' // your segment website source write key const OTHER_WRITE_KEYS = [] // any other sources w destinations that you want to include in config const SEGMENT_MAPPING = 1 // functional // gets enabled destination configurations from across your source (and optionally other sources in workspace) fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]).then((destinations) => { // KEY LOGIC HERE // eg. could instead use trustArc's other APIs such as getConsentCategories // and map between the returned vendor domains to specific Segment Integrationss, etc // review this logic carefully, and edit per your business requirements // // in this case, we go with all when customer provides full consent // remove Advertising tools when the customer provides functional // and mark Segment as required (for data to flow to warehouse etc— if you make that clear in your policy) // note that though Segment will receive data we wont forward (even server side) to other sources because we // decorate the calls with the full set of integrations you have enabled with "false." // // "no preference" here goes with all on — you may want to flip that! const { consentDecision } = truste.cma.callApi("getConsentDecision", YOUR_DOMAIN); const destinationPreferences = destinations.map(function (dest) { if (consentDecision === 3) return { [dest.id]: true } if (consentDecision === 2) return { [dest.id]: dest.categories === 'Advertising' ? false : true } if (consentDecision === 1) return { [dest.id]: false } if (consentDecision === 0) return { [dest.id]: true } // IMPORTANT - this is default value for "no preference set" }).reduce((acc, val) => ({ ...val, ...acc }), { 'Segment.io': consentDecision >= SEGMENT_MAPPING }) conditionallyLoadAnalytics({ WEBSITE_WRITE_KEY, destinations, destinationPreferences, OPT_IN }) }) // registers a listener for consent changes, will reload window if they've denied to reset tracking -
sperand-io revised this gist
Jun 5, 2019 . 2 changed files with 2 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -40,12 +40,7 @@ const destinationPreferences = destinations.map(function (dest) { if (consentDecision === 2) return { [dest.id]: dest.categories.includes('Advertising') ? false : true }; if (consentDecision === 1) return { [dest.id]: dest.name === 'Segment.io' ? true : false }; // segment only for require (warehouses etc, note messages will still be de) if (consentDecision === 0) return { [dest.id]: true } // IMPORTANT - this is default value for "no preference set" }) conditionallyLoadAnalytics({ WEBSITE_WRITE_KEY, This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,5 +6,5 @@ const truste = { cma: { callApi: (x, _) => truste[x]() }, getConsentDecision: () => {consentDecision: 3} } -
sperand-io revised this gist
Jun 5, 2019 . 2 changed files with 7 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -40,7 +40,12 @@ const destinationPreferences = destinations.map(function (dest) { if (consentDecision === 2) return { [dest.id]: dest.categories.includes('Advertising') ? false : true }; if (consentDecision === 1) return { [dest.id]: dest.name === 'Segment.io' ? true : false }; // segment only for require (warehouses etc, note messages will still be de) if (consentDecision === 0) return { [dest.id]: true } // IMPORTANT - this is default value for "no preference set" }).reduce((acc, val) => { return { ...val, ...acc } }, {}) conditionallyLoadAnalytics({ WEBSITE_WRITE_KEY, This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,5 +6,5 @@ const truste = { cma: { callApi: (x, _) => truste[x]() }, getConsentDecision: () => ({ consentDecision: 3 }) } -
sperand-io revised this gist
Jun 5, 2019 . 1 changed file with 10 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,10 @@ // test stub // truste.cma.callApi("getConsentDecision", YOUR_DOMAIN); const truste = { cma: { callApi: (x, _) => truste[x]() }, getConsentDecision: () => 3 } -
sperand-io revised this gist
Jun 3, 2019 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,9 +1,9 @@ // To get started, make sure you're using the latest version of the analytics.js snippet (4.1.0 or above) // and remove the `analytics.load("YOUR_WRITE_KEY")` call. then drop this script below the snippet. // this is a standalone script for modern browsers. if you'd like to target older browsers, include // a fetch polyfill and use that instead of window.fetch. This would require that you build and package the // script somehow (rollup, webpack, browserify, etc). You may also want to transpile it to ES5 w eg Babel. // This script is configured to make all collection opt-in (rather than opt-out) for all users. // If you want to conditionally require whether or not to block data collection before affirmative consent, use something -
sperand-io revised this gist
Jun 3, 2019 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -15,9 +15,9 @@ // CONFIG — EDIT ME! const OPT_IN = true // false = only disable after opt out, can replace with a function such as inEU above const YOUR_DOMAIN = 'domain.com' // the hostname of your website const WEBSITE_WRITE_KEY = 'writeKey' // your segment website source write key const OTHER_WRITE_KEYS = [] // any other sources w destinations that you want to include in config // gets enabled destination configurations from across your source (and optionally other sources in workspace) const destinations = await fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]) -
sperand-io revised this gist
Jun 3, 2019 . 1 changed file with 6 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,12 +5,16 @@ // a fetch polyfill and use that instead of window.fetch. This would require that you build and package the // script somehow (rollup, webpack, browserify, etc) // This script is configured to make all collection opt-in (rather than opt-out) for all users. // If you want to conditionally require whether or not to block data collection before affirmative consent, use something // like inEU below and pass that function into `conditionallyLoadAnalytics`. If you want to collect data until the user // opts out, just change OPT_IN to false. // import fetch from 'isomorphic-fetch' // import inEU from '@segment/in-eu' // CONFIG — EDIT ME! const OPT_IN = true // false = only disable after opt out, can replace with a function such as inEU above const YOUR_DOMAIN = 'domain.com' const WEBSITE_WRITE_KEY = 'writeKey' const OTHER_WRITE_KEYS = [] // any other ones you want to load integrations from @@ -42,7 +46,7 @@ conditionallyLoadAnalytics({ WEBSITE_WRITE_KEY, destinations, destinationPreferences, OPT_IN }) // registers a listener for consent changes, will reload window if they've denied to reset tracking -
sperand-io revised this gist
Jun 3, 2019 . 1 changed file with 25 additions and 18 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,12 +1,14 @@ // To get started, make sure you're using the latest version of the analytics.js snippet (4.1.0 or above) // and remove the `analytics.load("YOUR_WRITE_KEY")` call // this is a standalone script for modern browsers. if you'd like to target older browsers, include // a fetch polyfill and use that instead of window.fetch. This would require that you build and package the // script somehow (rollup, webpack, browserify, etc) // if you want to conditionally require // import fetch from 'isomorphic-fetch' // import inEU from '@segment/in-eu' // CONFIG — EDIT ME! const YOUR_DOMAIN = 'domain.com' @@ -30,10 +32,10 @@ const destinations = await fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_ // no preference here goes with all on — you may want to flip that! const { consentDecision } = truste.cma.callApi("getConsentDecision", YOUR_DOMAIN); const destinationPreferences = destinations.map(function (dest) { if (consentDecision === 3) return { [dest.id]: true }; if (consentDecision === 2) return { [dest.id]: dest.categories.includes('Advertising') ? false : true }; if (consentDecision === 1) return { [dest.id]: dest.name === 'Segment.io' ? true : false }; // segment only for require (warehouses etc, note messages will still be de) if (consentDecision === 0) return { [dest.id]: true } // IMPORTANT - this is default value for "no preference set" }) conditionallyLoadAnalytics({ @@ -107,7 +109,7 @@ function conditionallyLoadAnalytics({ } async function fetchDestinationForWriteKey(writeKey) { const res = await window.fetch( `https://cdn.segment.com/v1/projects/${writeKey}/integrations` ) @@ -130,17 +132,22 @@ async function fetchDestinationForWriteKey(writeKey) { return destinations } async function fetchDestinations(...writeKeys) { const destinationsRequests = [] for (const writeKey of writeKeys) { destinationsRequests.push(fetchDestinationForWriteKey(writeKey)) } let destinations = await Promise.all(destinationsRequests) // unique list of destination across all sources destinations = [...destinations .reduce((a, b) => a.concat(b), []) // flatten multi-d array .filter(d => d.id !== 'Repeater') // remove repeater .reduce((map, item) => { map.has(item['id']) || map.set(item['id'], item) return map; }, new Map()).values()] return destinations } -
sperand-io revised this gist
Jun 1, 2019 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -60,6 +60,7 @@ window.addEventListener("message", function reload(e) { // otherwise approved... carry on! }, false); // helper functions below... function conditionallyLoadAnalytics({ writeKey, destinations, -
sperand-io revised this gist
Jun 1, 2019 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,8 @@ // To get started, make sure you're using the latest version of the analytics.js snippet (4.1.0 or above) // and remove the analytics.load("YOUR_WRITE_KEY"); call (so that you can conditionally manage the loading process). // then "build" this file with something like rollup, parcel, browserify etc targeting whatever browsers you need // and include just below the snippet import fetch from 'isomorphic-fetch' import {flatten, sortedUniqBy, sortBy} from 'lodash' -
sperand-io revised this gist
Jun 1, 2019 . 1 changed file with 16 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,29 +1,37 @@ // To get started, make sure you're using the latest version of the analytics.js snippet (4.1.0 or above) // and remove the analytics.load("YOUR_WRITE_KEY"); call (so that you can conditionally manage the loading process). // then "build" this file with something like rollup, parcel, browserify etc targeting whatever browsers you need! import fetch from 'isomorphic-fetch' import {flatten, sortedUniqBy, sortBy} from 'lodash' import inEU from '@segment/in-eu' // CONFIG — EDIT ME! const YOUR_DOMAIN = 'domain.com' const WEBSITE_WRITE_KEY = 'writeKey' const OTHER_WRITE_KEYS = [] // any other ones you want to load integrations from // gets enabled destination configurations from across your source (and optionally other sources in workspace) const destinations = await fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]) // KEY LOGIC HERE // eg. could instead use trustArc's other APIs such as getConsentCategories // and map between the returned vendor domains to specific Segment Integrationss, etc // review this logic carefully, and edit per your business requirements // // in this case, we go with all when customer provides full consent // remove Advertising tools when the customer provides functional // and mark Segment as required (for data to flow to warehouse etc— if you make that clear in your policy) // note that though Segment will receive data we wont forward (even server side) to other sources because we // decorate the calls with the full set of integrations you have enabled with "false." // // no preference here goes with all on — you may want to flip that! const { consentDecision } = truste.cma.callApi("getConsentDecision", YOUR_DOMAIN); const destinationPreferences = destinations.map(function (dest) { if (consentDecision === 3) return { [dest.name]: true }; if (consentDecision === 2) return { [dest.name]: dest.categories.includes('Advertising') ? false : true }; if (consentDecision === 1) return { [dest.name]: dest.name === 'Segment.io' ? true : false }; // segment only for require (warehouses etc, note messages will still be de) if (consentDecision === 0) return { [dest.name]: true } // IMPORTANT - this is default value for "no preference set" }) @@ -34,7 +42,7 @@ conditionallyLoadAnalytics({ true // or replace with custom isConsentRequired func, eg. inEU() from above! }) // registers a listener for consent changes, will reload window if they've denied to reset tracking window.top.postMessage(JSON.stringify({ PrivacyManagerAPI: { action: "getConsent", -
sperand-io revised this gist
Jun 1, 2019 . 2 changed files with 132 additions and 57 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,17 +1,136 @@ // To get started, make sure you're using the latest version of the analytics.js snippet (4.1.0 or above) // and remove the analytics.load("YOUR_WRITE_KEY"); call (so that you can conditionally manage the loading process). // optional, requires build step: import fetch from 'isomorphic-fetch' import {flatten, sortedUniqBy, sortBy} from 'lodash' import inEU from '@segment/in-eu' // CONFIG const YOUR_DOMAIN = 'domain.com' const WEBSITE_WRITE_KEY = 'writeKey' const OTHER_WRITE_KEYS = [] // any other ones you want to load integrations from // gets enabled destinations across your source (and other sources in workspace) const destinations = await fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]) // KEY LOGIC HERE // eg. could instead use getConsentCategories and map between the cookie domains to specific Segment Ints, etc // review this logic carefully, and edit per your business requirements const { consentDecision } = truste.cma.callApi("getConsentDecision", YOUR_DOMAIN); const destinationPreferences = destinations.map(function (dest) { if (consentDecision === 3) return { [dest.name]: true }; if (consentDecision === 2) return { [dest.name]: dest.categories.includes('Advertising') ? false : true }; // non-advertising only if (consentDecision === 1) return { [dest.name]: dest.name === 'Segment.io' ? true : false }; // segment only if (consentDecision === 0) return { [dest.name]: true } // IMPORTANT - this is default value for "no preference set" }) conditionallyLoadAnalytics({ WEBSITE_WRITE_KEY, destinations, destinationPreferences, true // or replace with custom isConsentRequired func, eg. inEU() from above! }) // registers a listener for consent changes, will reload too window.top.postMessage(JSON.stringify({ PrivacyManagerAPI: { action: "getConsent", timestamp: new Date().getTime(), self: YOUR_DOMAIN } }), "*"); window.addEventListener("message", function reload(e) { const response = JSON.parse(e.data) if (response.PrivacyManagerAPI.consent === "denied") { return window.location.reload(); } // otherwise approved... carry on! }, false); function conditionallyLoadAnalytics({ writeKey, destinations, destinationPreferences, isConsentRequired, shouldReload = true // change if you dont want to reload on consent changes }) { const integrations = {All: false, 'Segment.io': true} let isAnythingEnabled = false if (!destinationPreferences) { if (isConsentRequired) { return } // Load a.js normally when consent isn't required and there's no preferences if (!window.analytics.initialized) { window.analytics.load(writeKey) } return } for (const destination of destinations) { const isEnabled = Boolean(destinationPreferences[destination.id]) if (isEnabled) { isAnythingEnabled = true } integrations[destination.id] = isEnabled } // Reload the page if the trackers have already been initialised so that // the user's new preferences can take affect if (window.analytics.initialized) { if (shouldReload) { window.location.reload() } return } // Don't load a.js at all if nothing has been enabled if (isAnythingEnabled) { window.analytics.load(writeKey, {integrations}) } } async function fetchDestinationForWriteKey(writeKey) { const res = await fetch( `https://cdn.segment.com/v1/projects/${writeKey}/integrations` ) if (!res.ok) { throw new Error( `Failed to fetch integrations for write key ${writeKey}: HTTP ${ res.status } ${res.statusText}` ) } const destinations = await res.json() // Rename creationName to id to abstract the weird data model for (const destination of destinations) { destination.id = destination.creationName delete destination.creationName } return destinations } async function fetchDestinations(writeKeys) { const destinationsRequests = [] for (const writeKey of writeKeys) { destinationsRequests.push(fetchDestinationForWriteKey(writeKey)) } let destinations = flatten(await Promise.all(destinationsRequests)) // Remove the dummy Repeater destination destinations = destinations.filter(d => d.id !== 'Repeater') destinations = sortBy(destinations, ['id']) destinations = sortedUniqBy(destinations, 'id') return destinations } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,44 +0,0 @@ -
sperand-io created this gist
Jun 1, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,17 @@ const [isConsentRequired, destinations] = await Promise.all([ shouldRequireConsent(), fetchDestinations([writeKey, ...otherWriteKeys]) ]) const newDestinations = getNewDestinations( destinations, destinationPreferences ) conditionallyLoadAnalytics({ writeKey, destinations, destinationPreferences, isConsentRequired }) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,44 @@ export default function conditionallyLoadAnalytics({ writeKey, destinations, destinationPreferences, isConsentRequired, shouldReload = true }) { const integrations = {All: false, 'Segment.io': true} let isAnythingEnabled = false if (!destinationPreferences) { if (isConsentRequired) { return } // Load a.js normally when consent isn't required and there's no preferences if (!window.analytics.initialized) { window.analytics.load(writeKey) } return } for (const destination of destinations) { const isEnabled = Boolean(destinationPreferences[destination.id]) if (isEnabled) { isAnythingEnabled = true } integrations[destination.id] = isEnabled } // Reload the page if the trackers have already been initialised so that // the user's new preferences can take affect if (window.analytics.initialized) { if (shouldReload) { window.location.reload() } return } // Don't load a.js at all if nothing has been enabled if (isAnythingEnabled) { window.analytics.load(writeKey, {integrations}) } }