chore: Refactor UTM params to stay compliant with standards (#12312)

We were using UTM params on various branding urls which weren't
compliant to standard utm params and hence were ignored by analytics
tooling. this PR ensures that the params stays compliant with defined
standard

ref: https://en.wikipedia.org/wiki/UTM_parameters

## Changes 

- updated utm tags on widget and survey urls
- added utm on helpcenter branding

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Sojan Jose
2025-08-29 20:46:52 +02:00
committed by GitHub
parent 99997a701a
commit 9145658597
4 changed files with 60 additions and 11 deletions

View File

@@ -74,6 +74,17 @@ module PortalHelper
end
end
def generate_portal_brand_url(brand_url, referer)
url = URI.parse(brand_url.to_s)
query_params = Rack::Utils.parse_query(url.query)
query_params['utm_medium'] = 'helpcenter'
query_params['utm_campaign'] = 'branding'
query_params['utm_source'] = URI.parse(referer).host if referer.present? && referer.match?(URI::DEFAULT_PARSER.make_regexp)
url.query = query_params.to_query
url.to_s
end
def render_category_content(content)
ChatwootMarkdownRenderer.new(content).render_markdown_to_plain_text
end

View File

@@ -33,13 +33,15 @@ export default {
brandRedirectURL() {
try {
const referrerHost = this.$store.getters['appConfig/getReferrerHost'];
const baseURL = `${this.globalConfig.widgetBrandURL}?utm_source=${
referrerHost ? 'widget_branding' : 'survey_branding'
}`;
const url = new URL(this.globalConfig.widgetBrandURL);
if (referrerHost) {
return `${baseURL}&utm_referrer=${referrerHost}`;
url.searchParams.set('utm_source', referrerHost);
url.searchParams.set('utm_medium', 'widget');
} else {
url.searchParams.set('utm_medium', 'survey');
}
return baseURL;
url.searchParams.set('utm_campaign', 'branding');
return url.toString();
} catch (e) {
// Suppressing the error as getter is not defined in some cases
}

View File

@@ -8,12 +8,12 @@
alt="<%= @global_config['BRAND_NAME'] %>"
src="<%= @global_config['LOGO_THUMBNAIL'] %>"
/>
<p class="text-slate-700 dark:text-slate-300 text-sm font-medium text-center">
<%= I18n.t('public_portal.footer.made_with') %>
<a class="hover:underline" href="<%= @global_config['BRAND_URL'] %>" target="_blank" rel="noopener noreferrer nofoll/ow"><%= @global_config['BRAND_NAME'] %></a>
</p>
<p class="text-slate-700 dark:text-slate-300 text-sm font-medium text-center">
<%= I18n.t('public_portal.footer.made_with') %>
<a class="hover:underline" href="<%= generate_portal_brand_url(@global_config['BRAND_URL'], request.referer) %>" target="_blank" rel="noopener noreferrer nofollow"><%= @global_config['BRAND_NAME'] %></a>
</p>
</div>
</div>
</footer>

View File

@@ -291,4 +291,40 @@ describe PortalHelper do
end
end
end
describe '#generate_portal_brand_url' do
it 'builds URL with UTM params and referer host as source (happy path)' do
result = helper.generate_portal_brand_url('https://brand.example.com', 'https://app.chatwoot.com/some/page')
uri = URI.parse(result)
params = Rack::Utils.parse_query(uri.query)
expect(uri.scheme).to eq('https')
expect(uri.host).to eq('brand.example.com')
expect(params['utm_medium']).to eq('helpcenter')
expect(params['utm_campaign']).to eq('branding')
expect(params['utm_source']).to eq('app.chatwoot.com')
end
it 'returns utm string when brand_url is nil or empty' do
expect(helper.generate_portal_brand_url(nil,
'https://app.chatwoot.com')).to eq(
'?utm_campaign=branding&utm_medium=helpcenter&utm_source=app.chatwoot.com'
)
expect(helper.generate_portal_brand_url('',
'https://app.chatwoot.com')).to eq(
'?utm_campaign=branding&utm_medium=helpcenter&utm_source=app.chatwoot.com'
)
end
it 'omits utm_source when referer is nil or invalid' do
r1 = helper.generate_portal_brand_url('https://brand.example.com', nil)
p1 = Rack::Utils.parse_query(URI.parse(r1).query)
expect(p1.key?('utm_source')).to be(false)
r2 = helper.generate_portal_brand_url('https://brand.example.com', '::not-a-valid-url')
p2 = Rack::Utils.parse_query(URI.parse(r2).query)
expect(p2.key?('utm_source')).to be(false)
expect(p2['utm_medium']).to eq('helpcenter')
expect(p2['utm_campaign']).to eq('branding')
end
end
end