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();