diff --git a/lib/plausible_web/controllers/api/external_controller.ex b/lib/plausible_web/controllers/api/external_controller.ex index 20e790e3a..d5d253fe5 100644 --- a/lib/plausible_web/controllers/api/external_controller.ex +++ b/lib/plausible_web/controllers/api/external_controller.ex @@ -59,7 +59,6 @@ defmodule PlausibleWeb.Api.ExternalController do "name" => params["n"] || params["name"], "url" => params["u"] || params["url"], "referrer" => params["r"] || params["referrer"], - "source" => params["s"] || params["source"], "domain" => params["d"] || params["domain"], "screen_width" => params["w"] || params["screen_width"], } @@ -78,7 +77,6 @@ defmodule PlausibleWeb.Api.ExternalController do ref = parse_referrer(uri, params["referrer"]) country_code = visitor_country(conn) salts = Plausible.Session.Salts.fetch() - referrer_source = if params["source"], do: URI.decode(params["source"]), else: get_referrer_source(ref) event_attrs = %{ timestamp: NaiveDateTime.utc_now(), @@ -90,7 +88,7 @@ defmodule PlausibleWeb.Api.ExternalController do country_code: country_code, operating_system: ua && os_name(ua), browser: ua && browser_name(ua), - referrer_source: referrer_source, + referrer_source: get_referrer_source(uri, ref), referrer: clean_referrer(ref), screen_size: calculate_screen_size(params["screen_width"]) } @@ -186,9 +184,15 @@ defmodule PlausibleWeb.Api.ExternalController do end end - defp get_referrer_source(nil), do: nil + defp get_referrer_source(uri, ref) do + query = if uri && uri.query, do: URI.decode_query(uri.query), else: %{} + source = query["utm_source"] || query["source"] || query["ref"] + source || get_source_from_referrer(ref) + end - defp get_referrer_source(ref) do + defp get_source_from_referrer(nil), do: nil + + defp get_source_from_referrer(ref) do case ref.source do :unknown -> clean_uri(ref.referer) diff --git a/priv/tracker/js/analytics.js b/priv/tracker/js/analytics.js index ce63bcdc3..a81c593ab 100644 --- a/priv/tracker/js/analytics.js +++ b/priv/tracker/js/analytics.js @@ -1 +1 @@ -!function(i,o){"use strict";var s=i.location,l=i.document,e=l.querySelector('[src*="'+o+'"]'),u={domain:e&&e.getAttribute("data-domain")||s.hostname};function c(e){console.warn("[Plausible] Ignore event: "+e)}function t(e,t){if(/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/.test(s.hostname)||"file:"===s.protocol)return c("running locally");if("prerender"===l.visibilityState)return c("prerendering");var n,a={};a.n=e,a.u=s.href,a.d=u.domain,a.r=l.referrer||null,a.s=(n=s.search.match(/[?&](ref|source|utm_source)=([^?&]+)/))?n[2]:null,a.w=i.innerWidth;var r=new XMLHttpRequest;r.open("POST",o+"/api/event",!0),r.setRequestHeader("Content-Type","text/plain"),r.send(JSON.stringify(a)),r.onreadystatechange=function(){4==r.readyState&&t&&t.callback&&t.callback()}}function n(){t("pageview")}try{var a,r=i.history;r.pushState&&(a=r.pushState,r.pushState=function(){a.apply(this,arguments),n()},i.addEventListener("popstate",n));var p=i.plausible&&i.plausible.q||[];i.plausible=t;for(var d=0;d"); \ No newline at end of file +!function(r,i){"use strict";var o=r.location,s=r.document,e=s.querySelector('[src*="'+i+'"]'),l={domain:e&&e.getAttribute("data-domain")||o.hostname};function u(e){console.warn("[Plausible] Ignore event: "+e)}function t(e,t){if(/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/.test(o.hostname)||"file:"===o.protocol)return u("running locally");if("prerender"===s.visibilityState)return u("prerendering");var n={};n.n=e,n.u=o.href,n.d=l.domain,n.r=s.referrer||null,n.w=r.innerWidth;var a=new XMLHttpRequest;a.open("POST",i+"/api/event",!0),a.setRequestHeader("Content-Type","text/plain"),a.send(JSON.stringify(n)),a.onreadystatechange=function(){4==a.readyState&&t&&t.callback&&t.callback()}}function n(){t("pageview")}try{var a,c=r.history;c.pushState&&(a=c.pushState,c.pushState=function(){a.apply(this,arguments),n()},r.addEventListener("popstate",n));var p=r.plausible&&r.plausible.q||[];r.plausible=t;for(var d=0;d"); \ No newline at end of file diff --git a/priv/tracker/js/p.js b/priv/tracker/js/p.js index 7f6ff00f9..4869d875f 100644 --- a/priv/tracker/js/p.js +++ b/priv/tracker/js/p.js @@ -1 +1 @@ -!function(r,o){"use strict";function a(e){console.warn("[Plausible] Ignoring event because "+e)}function c(){var e=r.location.search.match(/[?&](ref|source|utm_source)=([^?&]+)/);return e?e[2]:null}function l(){var e,t,n=JSON.parse((e="plausible_user",(t=document.cookie.match(new RegExp("(?:^|; )"+e.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g,"\\$1")+"=([^;]*)")))?decodeURIComponent(t[1]):null));return n?{initial_referrer:n.initial_referrer&&decodeURIComponent(n.initial_referrer),initial_source:n.initial_source&&decodeURIComponent(n.initial_source)}:(n={initial_referrer:r.document.referrer||null,initial_source:c()},function(e,t){var n=new Date;n.setTime(n.getTime()+94608e6);var i="; expires="+n.toUTCString();document.cookie=e+"="+(t||"")+i+"; samesite=strict; path=/"}("plausible_user",JSON.stringify({initial_referrer:n.initial_referrer&&encodeURIComponent(n.initial_referrer),initial_source:n.initial_source&&encodeURIComponent(n.initial_source)})),n)}function t(e,t){if(/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/.test(r.location.hostname))return a("website is running locally");if("file:"===r.location.protocol)return a("website is running locally");if("prerender"===r.document.visibilityState)return a("document is prerendering");var n=s.trackAcquisition?l():{};n.n=e,n.u=r.location.protocol+"//"+r.location.hostname+r.location.pathname+r.location.search,n.d=s.domain,n.r=r.document.referrer||null,n.s=c(),n.w=r.innerWidth;var i=new XMLHttpRequest;i.open("POST",o+"/api/event",!0),i.setRequestHeader("Content-Type","text/plain"),i.send(JSON.stringify(n)),i.onreadystatechange=function(){i.readyState==XMLHttpRequest.DONE&&t&&t.callback&&t.callback()}}function n(e){t("pageview",e)}try{var s={domain:r.location.hostname},i={page:n,trigger:t,trackPushState:function(){var e,t=r.history;t.pushState&&(e=t.pushState,t.pushState=function(){e.apply(this,arguments),n()}),r.addEventListener("popstate",n)},configure:function(e,t){s[e]=t}},e=r.plausible.q||[];r.plausible=function(){var e=[].slice.call(arguments),t=e.shift();i[t].apply(this,e)};for(var u=0;u"); \ No newline at end of file +!function(r,a){"use strict";function o(e){console.warn("[Plausible] Ignoring event because "+e)}function l(){var e,i,t=JSON.parse((e="plausible_user",(i=document.cookie.match(new RegExp("(?:^|; )"+e.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g,"\\$1")+"=([^;]*)")))?decodeURIComponent(i[1]):null));return t?{initial_referrer:t.initial_referrer&&decodeURIComponent(t.initial_referrer),initial_source:t.initial_source&&decodeURIComponent(t.initial_source)}:(t={initial_referrer:r.document.referrer||null,initial_source:getSourceFromQueryParam()},function(e,i){var t=new Date;t.setTime(t.getTime()+94608e6);var n="; expires="+t.toUTCString();document.cookie=e+"="+(i||"")+n+"; samesite=strict; path=/"}("plausible_user",JSON.stringify({initial_referrer:t.initial_referrer&&encodeURIComponent(t.initial_referrer),initial_source:t.initial_source&&encodeURIComponent(t.initial_source)})),t)}function i(e,i){if(/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/.test(r.location.hostname))return o("website is running locally");if("file:"===r.location.protocol)return o("website is running locally");if("prerender"===r.document.visibilityState)return o("document is prerendering");var t=s.trackAcquisition?l():{};t.n=e,t.u=r.location.href,t.d=s.domain,t.r=r.document.referrer||null,t.w=r.innerWidth;var n=new XMLHttpRequest;n.open("POST",a+"/api/event",!0),n.setRequestHeader("Content-Type","text/plain"),n.send(JSON.stringify(t)),n.onreadystatechange=function(){n.readyState==XMLHttpRequest.DONE&&i&&i.callback&&i.callback()}}function t(e){i("pageview",e)}try{var s={domain:r.location.hostname},n={page:t,trigger:i,trackPushState:function(){var e,i=r.history;i.pushState&&(e=i.pushState,i.pushState=function(){e.apply(this,arguments),t()}),r.addEventListener("popstate",t)},configure:function(e,i){s[e]=i}},e=r.plausible.q||[];r.plausible=function(){var e=[].slice.call(arguments),i=e.shift();n[i].apply(this,e)};for(var c=0;c"); \ No newline at end of file diff --git a/priv/tracker/js/plausible.js b/priv/tracker/js/plausible.js index ce63bcdc3..a81c593ab 100644 --- a/priv/tracker/js/plausible.js +++ b/priv/tracker/js/plausible.js @@ -1 +1 @@ -!function(i,o){"use strict";var s=i.location,l=i.document,e=l.querySelector('[src*="'+o+'"]'),u={domain:e&&e.getAttribute("data-domain")||s.hostname};function c(e){console.warn("[Plausible] Ignore event: "+e)}function t(e,t){if(/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/.test(s.hostname)||"file:"===s.protocol)return c("running locally");if("prerender"===l.visibilityState)return c("prerendering");var n,a={};a.n=e,a.u=s.href,a.d=u.domain,a.r=l.referrer||null,a.s=(n=s.search.match(/[?&](ref|source|utm_source)=([^?&]+)/))?n[2]:null,a.w=i.innerWidth;var r=new XMLHttpRequest;r.open("POST",o+"/api/event",!0),r.setRequestHeader("Content-Type","text/plain"),r.send(JSON.stringify(a)),r.onreadystatechange=function(){4==r.readyState&&t&&t.callback&&t.callback()}}function n(){t("pageview")}try{var a,r=i.history;r.pushState&&(a=r.pushState,r.pushState=function(){a.apply(this,arguments),n()},i.addEventListener("popstate",n));var p=i.plausible&&i.plausible.q||[];i.plausible=t;for(var d=0;d"); \ No newline at end of file +!function(r,i){"use strict";var o=r.location,s=r.document,e=s.querySelector('[src*="'+i+'"]'),l={domain:e&&e.getAttribute("data-domain")||o.hostname};function u(e){console.warn("[Plausible] Ignore event: "+e)}function t(e,t){if(/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/.test(o.hostname)||"file:"===o.protocol)return u("running locally");if("prerender"===s.visibilityState)return u("prerendering");var n={};n.n=e,n.u=o.href,n.d=l.domain,n.r=s.referrer||null,n.w=r.innerWidth;var a=new XMLHttpRequest;a.open("POST",i+"/api/event",!0),a.setRequestHeader("Content-Type","text/plain"),a.send(JSON.stringify(n)),a.onreadystatechange=function(){4==a.readyState&&t&&t.callback&&t.callback()}}function n(){t("pageview")}try{var a,c=r.history;c.pushState&&(a=c.pushState,c.pushState=function(){a.apply(this,arguments),n()},r.addEventListener("popstate",n));var p=r.plausible&&r.plausible.q||[];r.plausible=t;for(var d=0;d"); \ No newline at end of file diff --git a/test/plausible_web/controllers/api/external_controller_test.exs b/test/plausible_web/controllers/api/external_controller_test.exs index 204c2ef2b..0e438399c 100644 --- a/test/plausible_web/controllers/api/external_controller_test.exs +++ b/test/plausible_web/controllers/api/external_controller_test.exs @@ -243,12 +243,11 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do assert pageview["referrer"] == "indiehackers.com/page" end - test "source param controls the referrer source", %{conn: conn} do + test "utm_source overrides referrer source", %{conn: conn} do params = %{ name: "pageview", - url: "http://www.example.com/", + url: "http://www.example.com/?utm_source=betalist", referrer: "https://betalist.com/my-produxct", - source: "betalist", domain: "external-controller-test-13.com" } @@ -398,11 +397,10 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do assert pageview["country_code"] == "US" end - test "URL and source are decoded", %{conn: conn} do + test "URL is decoded", %{conn: conn} do params = %{ name: "pageview", url: "http://www.example.com/opportunity/category/%D8%AC%D9%88%D8%A7%D8%A6%D8%B2-%D9%88%D9%85%D8%B3%D8%A7%D8%A8%D9%82%D8%A7%D8%AA", - source: "Hello%20World", domain: "external-controller-test-21.com" } @@ -413,14 +411,12 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do pageview = get_event("external-controller-test-21.com") assert pageview["pathname"] == "/opportunity/category/جوائز-ومسابقات" - assert pageview["referrer_source"] == "Hello World" end test "accepts shorthand map keys", %{conn: conn} do params = %{ n: "pageview", u: "http://www.example.com/opportunity", - s: "Hello%20World", d: "external-controller-test-22.com", r: "https://facebook.com/page", w: 300 @@ -433,7 +429,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do pageview = get_event("external-controller-test-22.com") assert pageview["pathname"] == "/opportunity" - assert pageview["referrer_source"] == "Hello World" + assert pageview["referrer_source"] == "Facebook" assert pageview["referrer"] == "facebook.com/page" assert pageview["screen_size"] == "Mobile" end diff --git a/tracker/src/p.js b/tracker/src/p.js index 4077e81a7..0dde6d1e8 100644 --- a/tracker/src/p.js +++ b/tracker/src/p.js @@ -19,15 +19,6 @@ console.warn('[Plausible] Ignoring event because ' + reason); } - function getUrl() { - return window.location.protocol + '//' + window.location.hostname + window.location.pathname + window.location.search; - } - - function getSourceFromQueryParam() { - var result = window.location.search.match(/[?&](ref|source|utm_source)=([^?&]+)/); - return result ? result[2] : null - } - function getUserData() { var userData = JSON.parse(getCookie('plausible_user')) @@ -58,10 +49,9 @@ var payload = CONFIG['trackAcquisition'] ? getUserData() : {} payload.n = eventName - payload.u = getUrl() + payload.u = window.location.href payload.d = CONFIG['domain'] payload.r = window.document.referrer || null - payload.s = getSourceFromQueryParam() payload.w = window.innerWidth var request = new XMLHttpRequest(); diff --git a/tracker/src/plausible.js b/tracker/src/plausible.js index a12f6cd6a..b6def3312 100644 --- a/tracker/src/plausible.js +++ b/tracker/src/plausible.js @@ -12,11 +12,6 @@ console.warn('[Plausible] Ignore event: ' + reason); } - function getSourceFromQueryParam() { - var result = location.search.match(/[?&](ref|source|utm_source)=([^?&]+)/); - return result ? result[2] : null - } - function trigger(eventName, options) { if (/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/.test(location.hostname) || location.protocol === 'file:') return ignore('running locally'); if (document.visibilityState === 'prerender') return ignore('prerendering'); @@ -26,7 +21,6 @@ payload.u = location.href payload.d = CONFIG['domain'] payload.r = document.referrer || null - payload.s = getSourceFromQueryParam() payload.w = window.innerWidth var request = new XMLHttpRequest();