mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 01:31:39 -05:00
Move UTM parameter tracking logic into a concern
This commit is contained in:
parent
6787273713
commit
8e35b8e09f
4 changed files with 218 additions and 25 deletions
|
|
@ -1 +1 @@
|
||||||
0.34.0
|
0.34.1
|
||||||
|
|
|
||||||
33
app/controllers/concerns/utm_trackable.rb
Normal file
33
app/controllers/concerns/utm_trackable.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
UTM_PARAMS = %w[utm_source utm_medium utm_campaign utm_term utm_content].freeze
|
||||||
|
|
||||||
|
def store_utm_params
|
||||||
|
UTM_PARAMS.each do |param|
|
||||||
|
session[param] = params[param] if params[param].present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign_utm_params(record)
|
||||||
|
utm_data = extract_utm_data_from_session
|
||||||
|
|
||||||
|
return unless utm_data.any?
|
||||||
|
|
||||||
|
record.update_columns(utm_data)
|
||||||
|
clear_utm_session
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def extract_utm_data_from_session
|
||||||
|
UTM_PARAMS.each_with_object({}) do |param, hash|
|
||||||
|
hash[param] = session[param] if session[param].present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear_utm_session
|
||||||
|
UTM_PARAMS.each { |param| session.delete(param) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Users::RegistrationsController < Devise::RegistrationsController
|
class Users::RegistrationsController < Devise::RegistrationsController
|
||||||
|
include UtmTrackable
|
||||||
|
|
||||||
before_action :set_invitation, only: %i[new create]
|
before_action :set_invitation, only: %i[new create]
|
||||||
before_action :check_registration_allowed, only: %i[new create]
|
before_action :check_registration_allowed, only: %i[new create]
|
||||||
before_action :store_utm_params, only: %i[new]
|
before_action :store_utm_params, only: %i[new], unless: -> { DawarichSettings.self_hosted? }
|
||||||
|
|
||||||
def new
|
def new
|
||||||
build_resource({})
|
build_resource({})
|
||||||
|
|
@ -82,33 +84,16 @@ class Users::RegistrationsController < Devise::RegistrationsController
|
||||||
if service.call
|
if service.call
|
||||||
flash[:notice] = "Welcome to #{@invitation.family.name}! You're now part of the family."
|
flash[:notice] = "Welcome to #{@invitation.family.name}! You're now part of the family."
|
||||||
else
|
else
|
||||||
flash[:alert] = "Account created successfully, but there was an issue accepting the invitation: #{service.error_message}"
|
flash[:alert] =
|
||||||
|
"Account created successfully, but there was an issue accepting the invitation: #{service.error_message}"
|
||||||
end
|
end
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
Rails.logger.error "Error accepting invitation during registration: #{e.message}"
|
Rails.logger.error "Error accepting invitation during registration: #{e.message}"
|
||||||
flash[:alert] = "Account created successfully, but there was an issue accepting the invitation. Please try accepting it again."
|
flash[:alert] =
|
||||||
|
'Account created successfully, but there was an issue accepting the invitation. Please try accepting it again.'
|
||||||
end
|
end
|
||||||
|
|
||||||
def sign_up_params
|
def sign_up_params
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
def store_utm_params
|
|
||||||
utm_params = %w[utm_source utm_medium utm_campaign utm_term utm_content]
|
|
||||||
utm_params.each do |param|
|
|
||||||
session[param] = params[param] if params[param].present?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def assign_utm_params(user)
|
|
||||||
utm_params = %w[utm_source utm_medium utm_campaign utm_term utm_content]
|
|
||||||
utm_data = {}
|
|
||||||
|
|
||||||
utm_params.each do |param|
|
|
||||||
utm_data[param] = session[param] if session[param].present?
|
|
||||||
session.delete(param) # Clean up session after assignment
|
|
||||||
end
|
|
||||||
|
|
||||||
user.update_columns(utm_data) if utm_data.any?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -325,4 +325,179 @@ RSpec.describe 'Users::Registrations', type: :request do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'UTM Parameter Tracking' do
|
||||||
|
let(:utm_params) do
|
||||||
|
{
|
||||||
|
utm_source: 'google',
|
||||||
|
utm_medium: 'cpc',
|
||||||
|
utm_campaign: 'winter_2025',
|
||||||
|
utm_term: 'location_tracking',
|
||||||
|
utm_content: 'banner_ad'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when self-hosted mode is disabled' do
|
||||||
|
before do
|
||||||
|
allow(DawarichSettings).to receive(:self_hosted?).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'captures UTM parameters from registration page URL' do
|
||||||
|
get new_user_registration_path, params: utm_params
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(session[:utm_source]).to eq('google')
|
||||||
|
expect(session[:utm_medium]).to eq('cpc')
|
||||||
|
expect(session[:utm_campaign]).to eq('winter_2025')
|
||||||
|
expect(session[:utm_term]).to eq('location_tracking')
|
||||||
|
expect(session[:utm_content]).to eq('banner_ad')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'stores UTM parameters in user record after registration' do
|
||||||
|
# Visit registration page with UTM params
|
||||||
|
get new_user_registration_path, params: utm_params
|
||||||
|
|
||||||
|
# Create account
|
||||||
|
unique_email = "utm-user-#{Time.current.to_i}@example.com"
|
||||||
|
post user_registration_path, params: {
|
||||||
|
user: {
|
||||||
|
email: unique_email,
|
||||||
|
password: 'password123',
|
||||||
|
password_confirmation: 'password123'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify UTM params were saved to user
|
||||||
|
user = User.find_by(email: unique_email)
|
||||||
|
expect(user.utm_source).to eq('google')
|
||||||
|
expect(user.utm_medium).to eq('cpc')
|
||||||
|
expect(user.utm_campaign).to eq('winter_2025')
|
||||||
|
expect(user.utm_term).to eq('location_tracking')
|
||||||
|
expect(user.utm_content).to eq('banner_ad')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'clears UTM parameters from session after registration' do
|
||||||
|
# Visit registration page with UTM params
|
||||||
|
get new_user_registration_path, params: utm_params
|
||||||
|
|
||||||
|
# Create account
|
||||||
|
unique_email = "utm-cleanup-#{Time.current.to_i}@example.com"
|
||||||
|
post user_registration_path, params: {
|
||||||
|
user: {
|
||||||
|
email: unique_email,
|
||||||
|
password: 'password123',
|
||||||
|
password_confirmation: 'password123'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify session was cleaned up
|
||||||
|
expect(session[:utm_source]).to be_nil
|
||||||
|
expect(session[:utm_medium]).to be_nil
|
||||||
|
expect(session[:utm_campaign]).to be_nil
|
||||||
|
expect(session[:utm_term]).to be_nil
|
||||||
|
expect(session[:utm_content]).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'handles partial UTM parameters' do
|
||||||
|
partial_utm = { utm_source: 'twitter', utm_campaign: 'spring_promo' }
|
||||||
|
|
||||||
|
get new_user_registration_path, params: partial_utm
|
||||||
|
|
||||||
|
unique_email = "partial-utm-#{Time.current.to_i}@example.com"
|
||||||
|
post user_registration_path, params: {
|
||||||
|
user: {
|
||||||
|
email: unique_email,
|
||||||
|
password: 'password123',
|
||||||
|
password_confirmation: 'password123'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user = User.find_by(email: unique_email)
|
||||||
|
expect(user.utm_source).to eq('twitter')
|
||||||
|
expect(user.utm_campaign).to eq('spring_promo')
|
||||||
|
expect(user.utm_medium).to be_nil
|
||||||
|
expect(user.utm_term).to be_nil
|
||||||
|
expect(user.utm_content).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not store empty UTM parameters' do
|
||||||
|
empty_utm = {
|
||||||
|
utm_source: '',
|
||||||
|
utm_medium: '',
|
||||||
|
utm_campaign: 'campaign_only'
|
||||||
|
}
|
||||||
|
|
||||||
|
get new_user_registration_path, params: empty_utm
|
||||||
|
|
||||||
|
unique_email = "empty-utm-#{Time.current.to_i}@example.com"
|
||||||
|
post user_registration_path, params: {
|
||||||
|
user: {
|
||||||
|
email: unique_email,
|
||||||
|
password: 'password123',
|
||||||
|
password_confirmation: 'password123'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user = User.find_by(email: unique_email)
|
||||||
|
expect(user.utm_source).to be_nil
|
||||||
|
expect(user.utm_medium).to be_nil
|
||||||
|
expect(user.utm_campaign).to eq('campaign_only')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'works with family invitations' do
|
||||||
|
get new_user_registration_path, params: utm_params.merge(invitation_token: invitation.token)
|
||||||
|
|
||||||
|
post user_registration_path, params: {
|
||||||
|
user: {
|
||||||
|
email: invitation.email,
|
||||||
|
password: 'password123',
|
||||||
|
password_confirmation: 'password123'
|
||||||
|
},
|
||||||
|
invitation_token: invitation.token
|
||||||
|
}
|
||||||
|
|
||||||
|
user = User.find_by(email: invitation.email)
|
||||||
|
expect(user.utm_source).to eq('google')
|
||||||
|
expect(user.utm_campaign).to eq('winter_2025')
|
||||||
|
expect(user.family).to eq(family)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when self-hosted mode is enabled' do
|
||||||
|
before do
|
||||||
|
allow(ENV).to receive(:[]).and_call_original
|
||||||
|
allow(ENV).to receive(:[]).with('SELF_HOSTED').and_return('true')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not capture UTM parameters' do
|
||||||
|
# With valid invitation to allow registration in self-hosted mode
|
||||||
|
get new_user_registration_path, params: utm_params.merge(invitation_token: invitation.token)
|
||||||
|
|
||||||
|
expect(session[:utm_source]).to be_nil
|
||||||
|
expect(session[:utm_medium]).to be_nil
|
||||||
|
expect(session[:utm_campaign]).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not store UTM parameters in user record' do
|
||||||
|
# With valid invitation to allow registration in self-hosted mode
|
||||||
|
get new_user_registration_path, params: utm_params.merge(invitation_token: invitation.token)
|
||||||
|
|
||||||
|
post user_registration_path, params: {
|
||||||
|
user: {
|
||||||
|
email: invitation.email,
|
||||||
|
password: 'password123',
|
||||||
|
password_confirmation: 'password123'
|
||||||
|
},
|
||||||
|
invitation_token: invitation.token
|
||||||
|
}
|
||||||
|
|
||||||
|
user = User.find_by(email: invitation.email)
|
||||||
|
expect(user.utm_source).to be_nil
|
||||||
|
expect(user.utm_medium).to be_nil
|
||||||
|
expect(user.utm_campaign).to be_nil
|
||||||
|
expect(user.utm_term).to be_nil
|
||||||
|
expect(user.utm_content).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue