diff --git a/lms/djangoapps/discussion/tasks.py b/lms/djangoapps/discussion/tasks.py index 7b13112b30..1cc5e0eec6 100644 --- a/lms/djangoapps/discussion/tasks.py +++ b/lms/djangoapps/discussion/tasks.py @@ -101,10 +101,9 @@ def _get_course_language(course_id): def _build_message_context(context): - message_context = get_base_template_context(Site.objects.get(id=context['site_id'])) + message_context = get_base_template_context(context['site']) message_context.update(_deserialize_context_dates(context)) message_context['post_link'] = _get_thread_url(context) - message_context['ga_tracking_pixel_url'] = _generate_ga_pixel_url(context) return message_context @@ -122,25 +121,3 @@ def _get_thread_url(context): 'id': context['thread_id'], } return urljoin(context['site'].domain, permalink(thread_content)) - - -def _generate_ga_pixel_url(context): - # used for analytics - query_params = { - 'v': '1', # version, required for GA - 't': 'event', # - 'ec': 'email', # event category - 'ea': 'edx.bi.email.opened', # event action: in this case, the user opened the email - 'tid': get_value("GOOGLE_ANALYTICS_TRACKING_ID", getattr(settings, "GOOGLE_ANALYTICS_TRACKING_ID", None)), # tracking ID to associate this link with our GA instance - 'uid': context['thread_author_id'], - 'cs': 'sailthru', # Campaign source - what sent the email - 'cm': 'email', # Campaign medium - how the content is being delivered - 'cn': 'triggered_discussionnotification', # Campaign name - human-readable name for this particular class of message - 'dp': '/email/ace/discussions/responsenotification/{0}/'.format(context['course_id']), # document path, used for drilling down into specific events - 'dt': 'Reply to {0} at {1}'.format(context['thread_title'], context['comment_created_at']), # document title, should match the title of the email - } - - return u"{url}?{params}".format( - url="https://www.google-analytics.com/collect", - params=urlencode(query_params) - ) diff --git a/lms/djangoapps/discussion/templates/discussion/edx_ace/response_notification/email/body.html b/lms/djangoapps/discussion/templates/discussion/edx_ace/response_notification/email/body.html deleted file mode 100644 index 1b3ba81124..0000000000 --- a/lms/djangoapps/discussion/templates/discussion/edx_ace/response_notification/email/body.html +++ /dev/null @@ -1,62 +0,0 @@ -{% load i18n %} -{% load static %} - -{% block preview_text %} - {% blocktrans trimmed %} - Hi {{ thread_username }} - {% endblocktrans %} -{% endblock %} - -{% block content %} -
- {% blocktrans trimmed %}
- - Hi {{ thread_username }}, -- -- {{ comment_username }} made the following reply to {{ thread_title }} at {{ comment_created_at }}. - - -- {{ comment_body }} - - - - {% endblocktrans %} - -- {# email client support for style sheets is pretty spotty, so we have to inline all of these styles #} - - - - |
-
|
+ {% blocktrans trimmed %}
+ + Hi {{ thread_username }}, + + ++ {{ comment_username }} made the following reply to {{ thread_title }} at {{ comment_created_at }}. + + ++ {{ comment_body }} + + {% endblocktrans %} + + {% trans "View the discussion" as course_cta_text %} + {% include "schedules/edx_ace/common/return_to_course_cta.html" with course_cta_text=course_cta_text course_cta_url=post_link%} + |
+
{% endif %}
{% if mobile_store_urls.google %}
-
+
diff --git a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/common/return_to_course_cta.html b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/common/return_to_course_cta.html
index eb6e6b0c0b..fcf93ea3af 100644
--- a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/common/return_to_course_cta.html
+++ b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/common/return_to_course_cta.html
@@ -1,12 +1,17 @@
{% load i18n %}
+{% load ace %}
{# email client support for style sheets is pretty spotty, so we have to inline all of these styles #}
1 %}
- href="{{ dashboard_url }}"
+ {% if course_cta_url %}
+ href="{% with_link_tracking course_cta_url %}"
{% else %}
- href="{{ course_url }}"
+ {%if course_ids|length > 1 %}
+ href="{% with_link_tracking dashboard_url %}"
+ {% else %}
+ href="{% with_link_tracking course_url %}"
+ {% endif %}
{% endif %}
style="
color: #ffffff;
diff --git a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/common/upsell_cta.html b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/common/upsell_cta.html
index b134529583..913a9a58d0 100644
--- a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/common/upsell_cta.html
+++ b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/common/upsell_cta.html
@@ -1,4 +1,5 @@
{% load i18n %}
+{% load ace %}
{% if show_upsell %}
@@ -8,7 +9,7 @@
{% endblocktrans %}
-
{% endif %}
diff --git a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/body.html b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/body.html
index decd420ccb..4be7dcd00e 100644
--- a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/body.html
+++ b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/courseupdate/email/body.html
@@ -1,6 +1,5 @@
{% extends 'schedules/edx_ace/common/base_body.html' %}
{% load i18n %}
-{% load static %}
{% block preview_text %}
{% blocktrans trimmed %}
diff --git a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/recurringnudge_day10/email/body.txt b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/recurringnudge_day10/email/body.txt
index 9a6e8cf8eb..584470c3f4 100644
--- a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/recurringnudge_day10/email/body.txt
+++ b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/recurringnudge_day10/email/body.txt
@@ -1,15 +1,16 @@
{% load i18n %}
+{% load ace %}
{% if course_ids|length > 1 %}
{% blocktrans trimmed %}
Many edX learners are completing more problems every week, and
participating in the discussion forums. What do you want to do to keep learning?
{% endblocktrans %}
- {% trans "Keep learning" %} <{{dashboard_url}}>
+ {% trans "Keep learning" %} <{% with_link_tracking dashboard_url %}>
{% else %}
{% blocktrans trimmed %}
Many edX learners in {{course_name}} are completing more problems every week, and
participating in the discussion forums. What do you want to do to keep learning?
{% endblocktrans %}
- {% trans "Keep learning" %} <{{course_url}}>
+ {% trans "Keep learning" %} <{% with_link_tracking course_url %}>
{% endif %}
{% include "schedules/edx_ace/common/upsell_cta.txt"%}
diff --git a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/recurringnudge_day3/email/body.txt b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/recurringnudge_day3/email/body.txt
index efcfeba1bf..910f9e14ba 100644
--- a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/recurringnudge_day3/email/body.txt
+++ b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/recurringnudge_day3/email/body.txt
@@ -1,18 +1,18 @@
{% load i18n %}
-
+{% load ace %}
{% if course_ids|length > 1 %}
{% blocktrans trimmed %}
Remember when you enrolled in {{ course_name }}, and other courses on edX.org? We do, and we’re glad
to have you! Come see what everyone is learning.
{% endblocktrans %}
-{% trans "Start learning now" %} <{{ dashboard_url }}>
+{% trans "Start learning now" %} <{% with_link_tracking dashboard_url %}>
{% else %}
{% blocktrans trimmed %}
Remember when you enrolled in {{ course_name }} on edX.org? We do, and we’re glad
to have you! Come see what everyone is learning.
{% endblocktrans %}
-{% trans "Start learning now" %} <{{ course_url }}>
+{% trans "Start learning now" %} <{% with_link_tracking course_url %}>
{% endif %}
{% include "schedules/edx_ace/common/upsell_cta.txt"%}
diff --git a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/upgradereminder/email/body.html b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/upgradereminder/email/body.html
index b14570d5fe..5c1af75a7a 100644
--- a/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/upgradereminder/email/body.html
+++ b/openedx/core/djangoapps/schedules/templates/schedules/edx_ace/upgradereminder/email/body.html
@@ -1,6 +1,7 @@
{% extends 'schedules/edx_ace/common/base_body.html' %}
{% load i18n %}
{% load static %}
+{% load ace %}
{% block preview_text %}
{% if course_ids|length > 1 %}
@@ -56,7 +57,7 @@
{% for course_link in course_links %}
@@ -82,9 +83,9 @@
{# email client support for style sheets is pretty spotty, so we have to inline all of these styles #}
+{% trans "Upgrade now at" %} <{% with_link_tracking dashboard_url %}>
{% else %}
{% blocktrans trimmed %}
We hope you are enjoying learning with us so far in {{ first_course_name }}! A verified certificate
@@ -25,5 +25,5 @@ official and easily shareable.
Upgrade by {{ user_schedule_upgrade_deadline_time }}.
{% endblocktrans %}
-{% trans "Upgrade now at" %} <{{ upsell_link }}>
+{% trans "Upgrade now at" %} <{% with_link_tracking upsell_link %}>
{% endif %}
diff --git a/openedx/core/djangoapps/schedules/templatetags/__init__.py b/openedx/core/djangoapps/schedules/templatetags/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/openedx/core/djangoapps/schedules/templatetags/ace.py b/openedx/core/djangoapps/schedules/templatetags/ace.py
new file mode 100644
index 0000000000..aff2ad45eb
--- /dev/null
+++ b/openedx/core/djangoapps/schedules/templatetags/ace.py
@@ -0,0 +1,150 @@
+from urlparse import urlparse, parse_qs
+
+from crum import get_current_request
+from django import template
+from django.utils.safestring import mark_safe
+
+from openedx.core.djangoapps.schedules.tracking import CampaignTrackingInfo, GoogleAnalyticsTrackingPixel
+from openedx.core.djangolib.markup import HTML
+
+register = template.Library()
+
+
+@register.simple_tag(takes_context=True)
+def with_link_tracking(context, url):
+ """
+ Modifies the provided URL to ensure it is safe for usage in an email template and adds UTM parameters to it.
+
+ The provided URL can be relative or absolute. If it is relative, it will be made absolute.
+
+ All URLs will be augmented to include UTM parameters so that clicks can be tracked.
+
+ Args:
+ context (dict): The template context. Must include a "request" and "message".
+ url (str): The url to rewrite.
+
+ Returns:
+ str: The URL as an absolute URL with appropriate query string parameters that allow clicks to be tracked.
+
+ """
+ site, _user, message = _get_variables_from_context(context, 'with_link_tracking')
+
+ campaign = CampaignTrackingInfo(
+ source=message.app_label,
+ campaign=message.name,
+ content=message.uuid,
+ )
+ course_ids = context.get('course_ids')
+ if course_ids is not None and len(course_ids) > 0:
+ campaign.term = course_ids[0]
+
+ return mark_safe(
+ modify_url_to_track_clicks(
+ ensure_url_is_absolute(site, url),
+ campaign=campaign
+ )
+ )
+
+
+def _get_variables_from_context(context, tag_name):
+ if 'request' in context:
+ request = context['request']
+ else:
+ request = get_current_request()
+
+ if request is None:
+ raise template.VariableDoesNotExist(
+ 'The {0} template tag requires a "request" to be present in the template context. Consider using '
+ '"emulate_http_request" if you are rendering the template in a celery task.'.format(tag_name)
+ )
+
+ message = context.get('message')
+ if message is None:
+ raise template.VariableDoesNotExist(
+ 'The {0} template tag requires a "message" to be present in the template context.'.format(tag_name)
+ )
+
+ return request.site, request.user, message
+
+
+@register.simple_tag(takes_context=True)
+def google_analytics_tracking_pixel(context):
+ """
+ If configured, inject a google analytics tracking pixel into the template.
+
+ This tracking pixel will allow email open events to be tracked.
+
+ Args:
+ context (dict): The template context. Must include a "request" and "message".
+
+ Returns:
+ str: A string containing an HTML image tag that implements the GA measurement protocol or an empty string if
+ GA is not configured. For this to work, the site or settings must include the GA tracking ID.
+ """
+ image_url = _get_google_analytics_tracking_url(context)
+ if image_url is not None:
+ return mark_safe(
+ HTML('').format(HTML(image_url))
+ )
+ else:
+ return ''
+
+
+def _get_google_analytics_tracking_url(context):
+ site, user, message = _get_variables_from_context(context, 'google_analytics_tracking_pixel')
+
+ pixel = GoogleAnalyticsTrackingPixel(
+ site=site,
+ user_id=user.id,
+ campaign_source=message.app_label,
+ campaign_name=message.name,
+ campaign_content=message.uuid,
+ document_path='/email/{0}/{1}/{2}/{3}'.format(
+ message.app_label,
+ message.name,
+ message.send_uuid,
+ message.uuid,
+ ),
+ )
+ course_ids = context.get('course_ids')
+ if course_ids is not None and len(course_ids) > 0:
+ pixel.course_id = course_ids[0]
+
+ return pixel.image_url
+
+
+def modify_url_to_track_clicks(url, campaign=None):
+ """
+ Given a URL, this method modifies the query string parameters to include UTM tracking parameters.
+
+ These UTM codes are used to by Google Analytics to identify the source of traffic. This will help us better
+ understand how users behave when they come to the site by clicking a link in this email.
+
+ Arguments:
+ url (str): pass
+ campaign (CampaignTrackingInfo): pass
+
+ Returns:
+ str: The url with appropriate query string parameters.
+ """
+ parsed_url = urlparse(url)
+ if campaign is None:
+ campaign = CampaignTrackingInfo()
+ modified_url = parsed_url._replace(query=campaign.to_query_string(parsed_url.query))
+ return modified_url.geturl()
+
+
+def ensure_url_is_absolute(site, relative_path):
+ """
+ Add site.domain to the beginning of the given relative path.
+
+ If the given URL is already absolute (has a netloc part), then it is just returned.
+ """
+ if bool(urlparse(relative_path).netloc):
+ # Given URL is already absolute
+ url = relative_path
+ else:
+ root = site.domain.rstrip('/')
+ relative_path = relative_path.lstrip('/')
+ url = u'https://{root}/{path}'.format(root=root, path=relative_path)
+ return url
diff --git a/openedx/core/djangoapps/schedules/tests/mixins.py b/openedx/core/djangoapps/schedules/tests/mixins.py
new file mode 100644
index 0000000000..e738743b94
--- /dev/null
+++ b/openedx/core/djangoapps/schedules/tests/mixins.py
@@ -0,0 +1,53 @@
+from urlparse import parse_qs, urlparse
+
+
+class QueryStringAssertionMixin(object):
+
+ def assert_query_string_equal(self, expected_qs, actual_qs):
+ """
+ Compares two query strings to see if they are equivalent. Note that order of parameters is not significant.
+
+ Args:
+ expected_qs (str): The expected query string.
+ actual_qs (str): The actual query string.
+
+ Raises:
+ AssertionError: If the two query strings are not equal.
+ """
+ self.assertDictEqual(parse_qs(expected_qs), parse_qs(actual_qs))
+
+ def assert_url_components_equal(self, url, **kwargs):
+ """
+ Assert that the provided URL has the expected components with the expected values.
+
+ Args:
+ url (str): The URL to parse and make assertions about.
+ **kwargs: The expected component values. For example: scheme='https' would assert that the URL scheme was
+ https.
+
+ Raises:
+ AssertionError: If any of the expected components do not match.
+ """
+ parsed_url = urlparse(url)
+ for expected_component, expected_value in kwargs.items():
+ if expected_component == 'query':
+ self.assert_query_string_equal(expected_value, parsed_url.query)
+ else:
+ self.assertEqual(expected_value, getattr(parsed_url, expected_component))
+
+ def assert_query_string_parameters_equal(self, url, **kwargs):
+ """
+ Assert that the provided URL has query string paramters that match the kwargs.
+
+ Args:
+ url (str): The URL to parse and make assertions about.
+ **kwargs: The expected query string parameter values. For example: foo='bar' would assert that foo=bar
+ appeared in the query string.
+
+ Raises:
+ AssertionError: If any of the expected parameters values do not match.
+ """
+ parsed_url = urlparse(url)
+ parsed_qs = parse_qs(parsed_url.query)
+ for expected_key, expected_value in kwargs.items():
+ self.assertEqual(parsed_qs[expected_key], [str(expected_value)])
diff --git a/openedx/core/djangoapps/schedules/tests/test_template_context.py b/openedx/core/djangoapps/schedules/tests/test_template_context.py
deleted file mode 100644
index d116530c79..0000000000
--- a/openedx/core/djangoapps/schedules/tests/test_template_context.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from openedx.core.djangoapps.schedules.template_context import absolute_url
-from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
-from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
-
-
-@skip_unless_lms
-class TestTemplateContext(CacheIsolationTestCase):
- def setUp(self):
- self.site = SiteFactory.create()
- self.site.domain = 'example.com'
-
- def test_absolute_url(self):
- absolute = absolute_url(self.site, '/foo/bar')
- self.assertEqual(absolute, 'https://example.com/foo/bar')
-
- def test_absolute_url_domain_lstrip(self):
- self.site.domain = 'example.com/'
- absolute = absolute_url(self.site, 'foo/bar')
- self.assertEqual(absolute, 'https://example.com/foo/bar')
-
- def test_absolute_url_already_absolute(self):
- absolute = absolute_url(self.site, 'https://some-cdn.com/foo/bar')
- self.assertEqual(absolute, 'https://some-cdn.com/foo/bar')
diff --git a/openedx/core/djangoapps/schedules/tests/test_templatetags.py b/openedx/core/djangoapps/schedules/tests/test_templatetags.py
new file mode 100644
index 0000000000..f223fc76c6
--- /dev/null
+++ b/openedx/core/djangoapps/schedules/tests/test_templatetags.py
@@ -0,0 +1,172 @@
+import uuid
+
+from django.http import HttpRequest
+from django.template import VariableDoesNotExist
+from django.test import override_settings
+from mock import patch
+
+from edx_ace import Message, Recipient
+from openedx.core.djangoapps.schedules.templatetags.ace import (
+ ensure_url_is_absolute,
+ with_link_tracking,
+ google_analytics_tracking_pixel,
+ _get_google_analytics_tracking_url
+)
+from openedx.core.djangoapps.schedules.tests.mixins import QueryStringAssertionMixin
+from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
+from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
+from student.tests.factories import UserFactory
+
+
+@skip_unless_lms
+class TestAbsoluteUrl(CacheIsolationTestCase):
+ def setUp(self):
+ self.site = SiteFactory.create()
+ self.site.domain = 'example.com'
+
+ def test_absolute_url(self):
+ absolute = ensure_url_is_absolute(self.site, '/foo/bar')
+ self.assertEqual(absolute, 'https://example.com/foo/bar')
+
+ def test_absolute_url_domain_lstrip(self):
+ self.site.domain = 'example.com/'
+ absolute = ensure_url_is_absolute(self.site, 'foo/bar')
+ self.assertEqual(absolute, 'https://example.com/foo/bar')
+
+ def test_absolute_url_already_absolute(self):
+ absolute = ensure_url_is_absolute(self.site, 'https://some-cdn.com/foo/bar')
+ self.assertEqual(absolute, 'https://some-cdn.com/foo/bar')
+
+
+class EmailTemplateTagMixin(object):
+
+ def setUp(self):
+ patcher = patch('openedx.core.djangoapps.schedules.templatetags.ace.get_current_request')
+ self.mock_get_current_request = patcher.start()
+ self.addCleanup(patcher.stop)
+
+ self.fake_request = HttpRequest()
+ self.fake_request.user = UserFactory.create()
+ self.fake_request.site = SiteFactory.create()
+ self.fake_request.site.domain = 'example.com'
+ self.mock_get_current_request.return_value = self.fake_request
+
+ self.message = Message(
+ app_label='test_app_label',
+ name='test_name',
+ recipient=Recipient(username='test_user'),
+ context={},
+ send_uuid=uuid.uuid4(),
+ )
+ self.context = {
+ 'message': self.message
+ }
+
+
+@skip_unless_lms
+class TestLinkTrackingTag(QueryStringAssertionMixin, EmailTemplateTagMixin, CacheIsolationTestCase):
+
+ def test_default(self):
+ result_url = str(with_link_tracking(self.context, 'http://example.com/foo'))
+ self.assert_url_components_equal(
+ result_url,
+ scheme='http',
+ netloc='example.com',
+ path='/foo',
+ query='utm_source=test_app_label&utm_campaign=test_name&utm_medium=email&utm_content={uuid}'.format(
+ uuid=self.message.uuid
+ )
+ )
+
+ def test_missing_request(self):
+ self.mock_get_current_request.return_value = None
+
+ with self.assertRaises(VariableDoesNotExist):
+ with_link_tracking(self.context, 'http://example.com/foo')
+
+ def test_missing_message(self):
+ del self.context['message']
+
+ with self.assertRaises(VariableDoesNotExist):
+ with_link_tracking(self.context, 'http://example.com/foo')
+
+ def test_course_id(self):
+ self.context['course_ids'] = ['foo/bar/baz']
+ result_url = str(with_link_tracking(self.context, 'http://example.com/foo'))
+ self.assert_query_string_parameters_equal(
+ result_url,
+ utm_term='foo/bar/baz',
+ )
+
+ def test_multiple_course_ids(self):
+ self.context['course_ids'] = ['foo/bar/baz', 'course-v1:FooX+bar+baz']
+ result_url = str(with_link_tracking(self.context, 'http://example.com/foo'))
+ self.assert_query_string_parameters_equal(
+ result_url,
+ utm_term='foo/bar/baz',
+ )
+
+ def test_relative_url(self):
+ result_url = str(with_link_tracking(self.context, '/foobar'))
+ self.assert_url_components_equal(
+ result_url,
+ scheme='https',
+ netloc='example.com',
+ path='/foobar'
+ )
+
+
+@skip_unless_lms
+@override_settings(GOOGLE_ANALYTICS_TRACKING_ID='UA-123456-1')
+class TestGoogleAnalyticsPixelTag(QueryStringAssertionMixin, EmailTemplateTagMixin, CacheIsolationTestCase):
+
+ def test_default(self):
+ result_url = _get_google_analytics_tracking_url(self.context)
+ self.assert_query_string_parameters_equal(
+ result_url,
+ uid=self.fake_request.user.id,
+ cs=self.message.app_label,
+ cn=self.message.name,
+ cc=self.message.uuid,
+ dp='/email/test_app_label/test_name/{send_uuid}/{uuid}'.format(
+ send_uuid=self.message.send_uuid,
+ uuid=self.message.uuid,
+ )
+ )
+
+ def test_missing_request(self):
+ self.mock_get_current_request.return_value = None
+
+ with self.assertRaises(VariableDoesNotExist):
+ google_analytics_tracking_pixel(self.context)
+
+ def test_missing_message(self):
+ del self.context['message']
+
+ with self.assertRaises(VariableDoesNotExist):
+ google_analytics_tracking_pixel(self.context)
+
+ def test_course_id(self):
+ self.context['course_ids'] = ['foo/bar/baz']
+ result_url = _get_google_analytics_tracking_url(self.context)
+ self.assert_query_string_parameters_equal(
+ result_url,
+ el='foo/bar/baz',
+ )
+
+ def test_multiple_course_ids(self):
+ self.context['course_ids'] = ['foo/bar/baz', 'course-v1:FooX+bar+baz']
+ result_url = _get_google_analytics_tracking_url(self.context)
+ self.assert_query_string_parameters_equal(
+ result_url,
+ el='foo/bar/baz',
+ )
+
+ def test_html_emitted(self):
+ result_html = google_analytics_tracking_pixel(self.context)
+ self.assertIn('
+{% google_analytics_tracking_pixel %}
+
@@ -92,7 +95,7 @@
-
- {% trans "Sign In" %}
+ {% trans "Sign In" %}
{% if social_media_urls.linkedin %}
-
+
@@ -100,7 +103,7 @@
{% endif %}
{% if social_media_urls.twitter %}
-
+
@@ -108,7 +111,7 @@
{% endif %}
{% if social_media_urls.facebook %}
-
+
@@ -116,7 +119,7 @@
{% endif %}
{% if social_media_urls.google_plus %}
-
+
@@ -124,7 +127,7 @@
{% endif %}
{% if social_media_urls.reddit %}
-
+
@@ -138,14 +141,14 @@
{% if mobile_store_urls.apple %}
-
+
{% endif %}
{% if mobile_store_urls.google %}
-
+
diff --git a/themes/red-theme/lms/templates/schedules/edx_ace/common/return_to_course_cta.html b/themes/red-theme/lms/templates/schedules/edx_ace/common/return_to_course_cta.html
index 4251c14cbd..ef4bfa5160 100644
--- a/themes/red-theme/lms/templates/schedules/edx_ace/common/return_to_course_cta.html
+++ b/themes/red-theme/lms/templates/schedules/edx_ace/common/return_to_course_cta.html
@@ -1,12 +1,17 @@
{% load i18n %}
+{% load ace %}