diff --git a/adserver/api/mixins.py b/adserver/api/mixins.py
index 110345aa..bb2c0a70 100644
--- a/adserver/api/mixins.py
+++ b/adserver/api/mixins.py
@@ -57,5 +57,8 @@ def finalize_response(self, request, response, *args, **kwargs):
response["X-Adserver-Country"] = str(request.geo.country)
response["X-Adserver-Region"] = str(request.geo.region)
response["X-Adserver-Metro"] = str(request.geo.metro)
+ response["X-Adserver-Continent"] = str(request.geo.continent)
+ response["X-Adserver-Latitude"] = str(request.geo.lat)
+ response["X-Adserver-Longitude"] = str(request.geo.lng)
return response
diff --git a/adserver/middleware.py b/adserver/middleware.py
index e3fe3aab..4b31802f 100644
--- a/adserver/middleware.py
+++ b/adserver/middleware.py
@@ -180,9 +180,12 @@ class CloudflareGeoIpMiddleware(GeoIpMiddleware):
COUNTRY_HEADER = "CF-IPCountry"
# These fields will require a custom transform rule
- # https://developers.cloudflare.com/rules/transform/
+ # https://developers.cloudflare.com/rules/transform/managed-transforms/reference/#add-visitor-location-headers
REGION_HEADER = "X-Cloudflare-Geo-Region" # ip.src.region_code
METRO_HEADER = "X-Cloudflare-Geo-Metro" # ip.src.metro_code
+ CONTINENT_HEADER = "X-Cloudflare-Geo-Continent" # ip.src.continent
+ LATITUDE_HEADER = "X-Cloudflare-Geo-Lat" # ip.src.lat
+ LONGITUDE_HEADER = "X-Cloudflare-Geo-Lon" # ip.src.lon
def get_geoip(self, request):
geo = super().get_geoip(request)
@@ -194,8 +197,13 @@ def get_geoip(self, request):
country_code = None
geo.country = country_code
+ # Region is the state/province within a country (not wider region like EU)
+ # See "continent"
geo.region = request.headers.get(self.REGION_HEADER, None)
geo.metro = request.headers.get(self.METRO_HEADER, None)
+ geo.continent = request.headers.get(self.CONTINENT_HEADER, None)
+ geo.lat = request.headers.get(self.LATITUDE_HEADER, None)
+ geo.lng = request.headers.get(self.LONGITUDE_HEADER, None)
return geo
diff --git a/adserver/migrations/0099_link_advertiser_guide.py b/adserver/migrations/0099_link_advertiser_guide.py
new file mode 100644
index 00000000..632604d7
--- /dev/null
+++ b/adserver/migrations/0099_link_advertiser_guide.py
@@ -0,0 +1,23 @@
+# Generated by Django 5.0.9 on 2024-11-27 00:34
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('adserver', '0098_rotation_aggregation'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='advertisement',
+ name='link',
+ field=models.URLField(help_text="URL of your landing page. This may contain UTM parameters so you know the traffic came from us. The publisher will be added in the 'ea-publisher' query parameter. Additional variable substitutions are available. See the advertiser guide. ", max_length=1024, verbose_name='Link URL'),
+ ),
+ migrations.AlterField(
+ model_name='historicaladvertisement',
+ name='link',
+ field=models.URLField(help_text="URL of your landing page. This may contain UTM parameters so you know the traffic came from us. The publisher will be added in the 'ea-publisher' query parameter. Additional variable substitutions are available. See the advertiser guide. ", max_length=1024, verbose_name='Link URL'),
+ ),
+ ]
diff --git a/adserver/models.py b/adserver/models.py
index 714fb145..fe69f013 100644
--- a/adserver/models.py
+++ b/adserver/models.py
@@ -1613,7 +1613,9 @@ class Advertisement(TimeStampedModel, IndestructibleModel):
help_text=_(
"URL of your landing page. "
"This may contain UTM parameters so you know the traffic came from us. "
- "The publisher will be added in the 'ea-publisher' query parameter."
+ "The publisher will be added in the 'ea-publisher' query parameter. "
+ "Additional variable substitutions are available. "
+ "See the advertiser guide. "
),
)
image = models.ImageField(
diff --git a/adserver/utils.py b/adserver/utils.py
index 1c7e2d61..15068b7c 100644
--- a/adserver/utils.py
+++ b/adserver/utils.py
@@ -53,6 +53,7 @@ class GeolocationData:
metro: int = None
lat: float = None
lng: float = None
+ continent: str = None
def get_ad_day():
diff --git a/adserver/views.py b/adserver/views.py
index 3d849eaa..bef9bc84 100644
--- a/adserver/views.py
+++ b/adserver/views.py
@@ -1207,6 +1207,11 @@ def get_response(self, request, advertisement, publisher):
advertisement=advertisement.slug,
advertisement_slug=advertisement.slug,
advertisement_name=advertisement.name,
+ flight=advertisement.flight.slug,
+ # For privacy, don't reveal more than country/continent to advertisers
+ country=str(request.geo.country) if request.geo else "None",
+ # request.geo.region is a state/province/region inside a country
+ continent=str(request.geo.continent) if request.geo else "None",
)
# Append a query string param ?ea-publisher=${publisher}