Skip to content

Commit

Permalink
Setup wagtail-ab-testing tracking script (#11889)
Browse files Browse the repository at this point in the history
* Custom wagtail_ab_testing_script template tag

* Add a/b testing scripts to base template

* Custom a/b testing script with nonces

* Docs on custom wagtail_ab_testing implementation

* Handle optional values in JS
  • Loading branch information
jhonatan-lopes authored Feb 20, 2024
1 parent c0b9f1c commit 4abf839
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 1 deletion.
7 changes: 7 additions & 0 deletions docs/upgrading.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,10 @@ A pages types usage report has been added on `networkapi.reports.views`, which w
### Localisation utility

The `localize_queryset` utility function on `networkapi.wagtailpages.utils` has been incorporated [into core](https://github.com/wagtail/wagtail/pull/11274). Once that has been released, the implementation here should be removed in favour of the core one.


## wagtail-ab-testing

In order to make `wagtail-ab-testing` compatible with our CSP, the main script and template tag had to be overwritten. The script on `templates/wagtail_ab_testing/script.html` was modified to pass values through a `json_script` tag (as [Django recommends](https://docs.djangoproject.com/en/4.2/ref/templates/builtins/#json-script)) and a `nonce` was added to the scripts.

The `wagtail_ab_testing_script` template tag was rewritten at `networkapi.wagtailcustomization.templatetags.wagtailcustom_tags` to pass the request to the script so that it can pick up the `nonce` value from `request` (`request.csp_nonce`).
4 changes: 3 additions & 1 deletion network-api/networkapi/templates/pages/base.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% load wagtailmetadata_tags i18n localization settings_value wagtailuserbar static mofo_common %}
{% load wagtailmetadata_tags i18n localization settings_value wagtailuserbar static mofo_common wagtailcustom_tags %}

{% get_current_language as lang_code %}

Expand Down Expand Up @@ -58,6 +58,8 @@
})(window,document,'script','FundraiseUp','ADCYPWMX');</script>
{% endblock %}

{% wagtail_ab_testing_script %}

{% block commento_meta %}{% endblock %}

{% block stylesheets %}
Expand Down
67 changes: 67 additions & 0 deletions network-api/networkapi/templates/wagtail_ab_testing/script.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{% load static %}

{% if track %}
{# Is this a Wagtail Page? #}
{% if page %}
{# This value is used to check if a goal has been reached #}
{{ page.id|json_script:"abtesting-page-id" }}
{% endif %}

{# Is there a test running on the current page? #}
{% if test and version %}
{{ test.id|json_script:"abtesting-test-id" }}
{{ version|json_script:"abtesting-version" }}
{{ test.goal_event|json_script:"abtesting-goal-event" }}
{% if test.goal_page %}
{{ test.goal_page.id|json_script:"abtesting-goal-page-id" }}
{% else %}
{{ None|json_script:"abtesting-goal-page-id" }}
{% endif %}
{% endif %}

{# URLs #}
{% url 'wagtail_ab_testing:register_participant' as register_participant_url %}
{{ register_participant_url|json_script:"abtesting-register-participant-url" }}
{% url 'wagtail_ab_testing:goal_reached' as goal_reached_url %}
{{ goal_reached_url|json_script:"abtesting-goal-reached-url" }}

<script nonce="{{ request.csp_nonce }}">
// Read values from the DOM
const registerParticipantUrl = JSON.parse(document.getElementById('abtesting-register-participant-url').textContent);
const goalReachedUrl = JSON.parse(document.getElementById('abtesting-goal-reached-url').textContent);

window.wagtailAbTesting = {
urls :{
registerParticipant: registerParticipantUrl,
goalReached: goalReachedUrl
}
};

if (document.getElementById('abtesting-page-id')) {
const pageId = JSON.parse(document.getElementById('abtesting-page-id').textContent);
window.wagtailAbTesting.pageId = pageId;
}

let testId = null;
let version = null;

if (document.getElementById('abtesting-test-id')) {
testId = JSON.parse(document.getElementById('abtesting-test-id').textContent);
}

if (document.getElementById('abtesting-version')) {
version = JSON.parse(document.getElementById('abtesting-version').textContent);
}

if (testId && version) {
const goalEvent = JSON.parse(document.getElementById('abtesting-goal-event').textContent);
const goalPageId = JSON.parse(document.getElementById('abtesting-goal-page-id').textContent);
window.wagtailAbTesting.testId = testId;
window.wagtailAbTesting.version = version;
window.wagtailAbTesting.goalEvent = goalEvent;
window.wagtailAbTesting.goalPageId = goalPageId;
}
</script>

<script nonce="{{ request.csp_nonce }}" src="{% static 'wagtail_ab_testing/js/tracker.js' %}" defer async></script>
{% endif %}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from django.utils.safestring import SafeText
from django.utils.text import slugify
from wagtail.rich_text import RichText
from wagtail_ab_testing.models import AbTest
from wagtail_ab_testing.utils import request_is_trackable

# We don't actually register any tags: the idea is to tap into
# the richtext filter, but that won't let us change _all_ the
Expand Down Expand Up @@ -85,6 +87,20 @@ def with_heading_ids(self):
return re.sub(heading_re, add_id_attribute, html)


@register.inclusion_tag("wagtail_ab_testing/script.html", takes_context=True)
def wagtail_ab_testing_script(context):
request = context["request"]
serving_variant = getattr(request, "wagtail_ab_testing_serving_variant", False)

return {
"request": request,
"track": request_is_trackable(request),
"page": context.get("page", None),
"test": getattr(request, "wagtail_ab_testing_test", None),
"version": AbTest.VERSION_VARIANT if serving_variant else AbTest.VERSION_CONTROL,
}


# Rebind the RichText's html serialization function such that
# the output is still entirely functional as far as wagtail
# can tell, except with headings enriched with fragment ids.
Expand Down

0 comments on commit 4abf839

Please sign in to comment.