Web Certificates: Platform Branding integration
This commit is contained in:
@@ -25,6 +25,7 @@ from branding.models import BrandingApiConfig
|
||||
|
||||
|
||||
log = logging.getLogger("edx.footer")
|
||||
EMPTY_URL = '#'
|
||||
|
||||
|
||||
def is_enabled():
|
||||
@@ -329,3 +330,64 @@ def _absolute_url_staticfile(is_secure, name):
|
||||
# For local development, the returned URL will be relative,
|
||||
# so we need to make it absolute.
|
||||
return _absolute_url(is_secure, url_path)
|
||||
|
||||
|
||||
def get_microsite_url(name):
|
||||
"""
|
||||
Look up and return the value for given url name in microsite configuration.
|
||||
URLs are saved in "urls" dictionary inside Microsite Configuration.
|
||||
|
||||
Return 'EMPTY_URL' if given url name is not defined in microsite configuration urls.
|
||||
"""
|
||||
urls = microsite.get_value("urls", default={})
|
||||
return urls.get(name) or EMPTY_URL
|
||||
|
||||
|
||||
def get_url(name):
|
||||
"""
|
||||
Lookup and return page url, lookup is performed in the following order
|
||||
|
||||
1. get microsite url, If microsite URL override exists, return it
|
||||
2. Otherwise return the marketing URL.
|
||||
|
||||
:return: string containing page url.
|
||||
"""
|
||||
# If a microsite URL override exists, return it. Otherwise return the marketing URL.
|
||||
microsite_url = get_microsite_url(name)
|
||||
if microsite_url != EMPTY_URL:
|
||||
return microsite_url
|
||||
|
||||
# get marketing link, if marketing is disabled then platform url will be used instead.
|
||||
url = marketing_link(name)
|
||||
|
||||
return url or EMPTY_URL
|
||||
|
||||
|
||||
def get_base_url(is_secure):
|
||||
"""
|
||||
Return Base URL for site/microsite.
|
||||
Arguments:
|
||||
is_secure (bool): If true, use HTTPS as the protocol.
|
||||
"""
|
||||
return _absolute_url(is_secure=is_secure, url_path="")
|
||||
|
||||
|
||||
def get_tos_and_honor_code_url():
|
||||
"""
|
||||
Lookup and return terms of services page url
|
||||
"""
|
||||
return get_url("TOS_AND_HONOR")
|
||||
|
||||
|
||||
def get_privacy_url():
|
||||
"""
|
||||
Lookup and return privacy policies page url
|
||||
"""
|
||||
return get_url("PRIVACY")
|
||||
|
||||
|
||||
def get_about_url():
|
||||
"""
|
||||
Lookup and return About page url
|
||||
"""
|
||||
return get_url("ABOUT")
|
||||
|
||||
@@ -29,7 +29,7 @@ from certificates.models import (
|
||||
CertificateTemplateAsset,
|
||||
)
|
||||
from certificates.queue import XQueueCertInterface
|
||||
|
||||
from branding import api as branding_api, get_logo_url
|
||||
|
||||
log = logging.getLogger("edx.certificate")
|
||||
|
||||
@@ -491,3 +491,41 @@ def get_asset_url_by_slug(asset_slug):
|
||||
except CertificateTemplateAsset.DoesNotExist:
|
||||
pass
|
||||
return asset_url
|
||||
|
||||
|
||||
def get_certificate_header_context(is_secure=True):
|
||||
"""
|
||||
Return data to be used in Certificate Header,
|
||||
data returned should be customized according to the microsite settings
|
||||
"""
|
||||
data = dict(
|
||||
logo_src=get_logo_url(),
|
||||
logo_url=branding_api.get_base_url(is_secure),
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_certificate_footer_context():
|
||||
"""
|
||||
Return data to be used in Certificate Footer,
|
||||
data returned should be customized according to the microsite settings
|
||||
"""
|
||||
data = dict()
|
||||
|
||||
# get Terms of Service and Honor Code page url
|
||||
terms_of_service_and_honor_code = branding_api.get_tos_and_honor_code_url()
|
||||
if terms_of_service_and_honor_code != branding_api.EMPTY_URL:
|
||||
data.update({'company_tos_url': terms_of_service_and_honor_code})
|
||||
|
||||
# get Privacy Policy page url
|
||||
privacy_policy = branding_api.get_privacy_url()
|
||||
if privacy_policy != branding_api.EMPTY_URL:
|
||||
data.update({'company_privacy_url': privacy_policy})
|
||||
|
||||
# get About page url
|
||||
about = branding_api.get_about_url()
|
||||
if about != branding_api.EMPTY_URL:
|
||||
data.update({'company_about_url': about})
|
||||
|
||||
return data
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Tests for the certificates Python API. """
|
||||
from contextlib import contextmanager
|
||||
import ddt
|
||||
from functools import wraps
|
||||
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
@@ -29,6 +30,8 @@ from certificates.models import (
|
||||
from certificates.queue import XQueueCertInterface, XQueueAddToQueueError
|
||||
from certificates.tests.factories import GeneratedCertificateFactory
|
||||
|
||||
from microsite_configuration import microsite
|
||||
|
||||
FEATURES_WITH_CERTS_ENABLED = settings.FEATURES.copy()
|
||||
FEATURES_WITH_CERTS_ENABLED['CERTIFICATES_HTML_VIEW'] = True
|
||||
|
||||
@@ -405,3 +408,94 @@ class GenerateExampleCertificatesTest(TestCase):
|
||||
"""Check the example certificate status. """
|
||||
actual_status = certs_api.example_certificates_status(self.COURSE_KEY)
|
||||
self.assertEqual(list(expected_statuses), actual_status)
|
||||
|
||||
|
||||
def set_microsite(domain):
|
||||
"""
|
||||
returns a decorator that can be used on a test_case to set a specific microsite for the current test case.
|
||||
:param domain: Domain of the new microsite
|
||||
"""
|
||||
def decorator(func):
|
||||
"""
|
||||
Decorator to set current microsite according to domain
|
||||
"""
|
||||
@wraps(func)
|
||||
def inner(request, *args, **kwargs):
|
||||
"""
|
||||
Execute the function after setting up the microsite.
|
||||
"""
|
||||
microsite.set_by_domain(domain)
|
||||
return func(request, *args, **kwargs)
|
||||
return inner
|
||||
return decorator
|
||||
|
||||
|
||||
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
|
||||
@attr('shard_1')
|
||||
class CertificatesBrandingTest(TestCase):
|
||||
"""Test certificates branding. """
|
||||
|
||||
COURSE_KEY = CourseLocator(org='test', course='test', run='test')
|
||||
|
||||
def setUp(self):
|
||||
super(CertificatesBrandingTest, self).setUp()
|
||||
|
||||
@set_microsite(settings.MICROSITE_CONFIGURATION['test_microsite']['domain_prefix'])
|
||||
def test_certificate_header_data(self):
|
||||
"""
|
||||
Test that get_certificate_header_context from certificates api
|
||||
returns data customized according to site branding.
|
||||
"""
|
||||
# Generate certificates for the course
|
||||
CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug=CourseMode.HONOR)
|
||||
data = certs_api.get_certificate_header_context(is_secure=True)
|
||||
|
||||
# Make sure there are not unexpected keys in dict returned by 'get_certificate_header_context'
|
||||
self.assertItemsEqual(
|
||||
data.keys(),
|
||||
['logo_src', 'logo_url']
|
||||
)
|
||||
self.assertIn(
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite']['logo_image_url'],
|
||||
data['logo_src']
|
||||
)
|
||||
|
||||
self.assertIn(
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite']['SITE_NAME'],
|
||||
data['logo_url']
|
||||
)
|
||||
|
||||
@set_microsite(settings.MICROSITE_CONFIGURATION['test_microsite']['domain_prefix'])
|
||||
def test_certificate_footer_data(self):
|
||||
"""
|
||||
Test that get_certificate_footer_context from certificates api returns
|
||||
data customized according to site branding.
|
||||
"""
|
||||
# Generate certificates for the course
|
||||
CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug=CourseMode.HONOR)
|
||||
data = certs_api.get_certificate_footer_context()
|
||||
|
||||
# Make sure there are not unexpected keys in dict returned by 'get_certificate_footer_context'
|
||||
self.assertItemsEqual(
|
||||
data.keys(),
|
||||
['company_about_url', 'company_privacy_url', 'company_tos_url']
|
||||
)
|
||||
|
||||
# ABOUT is present in MICROSITE_CONFIGURATION['test_microsite']["urls"] so web certificate will use that url
|
||||
self.assertIn(
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite']["urls"]['ABOUT'],
|
||||
data['company_about_url']
|
||||
)
|
||||
|
||||
# PRIVACY is present in MICROSITE_CONFIGURATION['test_microsite']["urls"] so web certificate will use that url
|
||||
self.assertIn(
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite']["urls"]['PRIVACY'],
|
||||
data['company_privacy_url']
|
||||
)
|
||||
|
||||
# TOS_AND_HONOR is present in MICROSITE_CONFIGURATION['test_microsite']["urls"],
|
||||
# so web certificate will use that url
|
||||
self.assertIn(
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite']["urls"]['TOS_AND_HONOR'],
|
||||
data['company_tos_url']
|
||||
)
|
||||
|
||||
@@ -305,7 +305,9 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase):
|
||||
self._add_course_certificates(count=1, signatory_count=2)
|
||||
response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
|
||||
self.assertIn('platform_microsite', response.content)
|
||||
self.assertIn('http://www.microsite.org', response.content)
|
||||
|
||||
# logo url is taken from microsite configuration setting
|
||||
self.assertIn('http://test_microsite.localhost', response.content)
|
||||
self.assertIn('This is special microsite aware company_about_description content', response.content)
|
||||
self.assertIn('Microsite title', response.content)
|
||||
|
||||
|
||||
@@ -224,6 +224,21 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
|
||||
"CERTIFICATE_TWITTER": True,
|
||||
"CERTIFICATE_FACEBOOK": True,
|
||||
})
|
||||
@patch.dict("django.conf.settings.MICROSITE_CONFIGURATION", {
|
||||
"test_microsite": dict(
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite'],
|
||||
urls=dict(
|
||||
ABOUT=None,
|
||||
PRIVACY=None,
|
||||
TOS_AND_HONOR=None,
|
||||
),
|
||||
)
|
||||
})
|
||||
@patch.dict("django.conf.settings.MKTG_URL_LINK_MAP", {
|
||||
'ABOUT': None,
|
||||
'PRIVACY': None,
|
||||
'TOS_AND_HONOR': None,
|
||||
})
|
||||
def test_rendering_maximum_data(self):
|
||||
"""
|
||||
Tests at least one data item from different context update methods to
|
||||
@@ -268,7 +283,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
|
||||
)
|
||||
# Test an item from html cert configuration
|
||||
self.assertIn(
|
||||
'<a class="logo" href="http://www.edx.org/honor_logo.png">',
|
||||
'<a class="logo" href="http://test_microsite.localhost">',
|
||||
response.content
|
||||
)
|
||||
# Test an item from course info
|
||||
@@ -862,3 +877,138 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
|
||||
settings.MEDIA_URL
|
||||
)
|
||||
)
|
||||
|
||||
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
|
||||
def test_certificate_branding(self):
|
||||
"""
|
||||
Test that link urls in certificate web view are customized according to site branding and
|
||||
microsite configuration.
|
||||
"""
|
||||
self._add_course_certificates(count=1, signatory_count=1, is_active=True)
|
||||
|
||||
self.course.save()
|
||||
self.store.update_item(self.course, self.user.id)
|
||||
|
||||
test_url = get_certificate_url(
|
||||
user_id=self.user.id,
|
||||
course_id=unicode(self.course.id)
|
||||
)
|
||||
response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
|
||||
# logo_image_url Tis present in MICROSITE_CONFIGURATION['test_microsite']["urls"],
|
||||
# so web certificate will use that.
|
||||
self.assertContains(
|
||||
response,
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite']['logo_image_url'],
|
||||
)
|
||||
# ABOUT is present in MICROSITE_CONFIGURATION['test_microsite']["urls"] so web certificate will use that url.
|
||||
self.assertContains(
|
||||
response,
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite']["urls"]['ABOUT'],
|
||||
)
|
||||
# PRIVACY is present in MICROSITE_CONFIGURATION['test_microsite']["urls"] so web certificate will use that url.
|
||||
self.assertContains(
|
||||
response,
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite']["urls"]['PRIVACY'],
|
||||
)
|
||||
# TOS_AND_HONOR is present in MICROSITE_CONFIGURATION['test_microsite']["urls"],
|
||||
# so web certificate will use that url.
|
||||
self.assertContains(
|
||||
response,
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite']["urls"]['TOS_AND_HONOR'],
|
||||
)
|
||||
|
||||
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
|
||||
@patch.dict("django.conf.settings.MICROSITE_CONFIGURATION", {
|
||||
"test_microsite": dict(
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite'],
|
||||
urls=dict(
|
||||
ABOUT=None,
|
||||
PRIVACY=None,
|
||||
TOS_AND_HONOR=None,
|
||||
),
|
||||
)
|
||||
})
|
||||
def test_certificate_branding_without_microsite_urls(self):
|
||||
"""
|
||||
Test that links from MKTG_URL_LINK_MAP setting are used if corresponding microsite urls are not present.
|
||||
microsite configuration.
|
||||
"""
|
||||
self._add_course_certificates(count=1, signatory_count=1, is_active=True)
|
||||
self.course.save()
|
||||
self.store.update_item(self.course, self.user.id)
|
||||
configuration = CertificateHtmlViewConfiguration.get_config()
|
||||
test_url = get_certificate_url(
|
||||
user_id=self.user.id,
|
||||
course_id=unicode(self.course.id)
|
||||
)
|
||||
response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
|
||||
# ABOUT is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"],
|
||||
# so web certificate will use MKTG_URL_LINK_MAP['ABOUT'] url.
|
||||
self.assertContains(
|
||||
response,
|
||||
settings.MKTG_URL_LINK_MAP['ABOUT'],
|
||||
)
|
||||
# PRIVACY is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"],
|
||||
# so web certificate will use MKTG_URL_LINK_MAP['PRIVACY'] url.
|
||||
self.assertContains(
|
||||
response,
|
||||
settings.MKTG_URL_LINK_MAP['PRIVACY'],
|
||||
)
|
||||
# TOS_AND_HONOR is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"] or MKTG_URL_LINK_MAP,
|
||||
# so web certificate will use CertificateHtmlViewConfiguration url.
|
||||
self.assertContains(
|
||||
response,
|
||||
configuration['microsites']['testmicrosite']['company_tos_url'],
|
||||
)
|
||||
|
||||
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
|
||||
@patch.dict("django.conf.settings.MICROSITE_CONFIGURATION", {
|
||||
"test_microsite": dict(
|
||||
settings.MICROSITE_CONFIGURATION['test_microsite'],
|
||||
urls=dict(
|
||||
ABOUT=None,
|
||||
PRIVACY=None,
|
||||
TOS_AND_HONOR=None,
|
||||
),
|
||||
)
|
||||
})
|
||||
@patch.dict("django.conf.settings.MKTG_URL_LINK_MAP", {
|
||||
'ABOUT': None,
|
||||
'PRIVACY': None,
|
||||
'TOS_AND_HONOR': None,
|
||||
})
|
||||
def test_certificate_without_branding_urls(self):
|
||||
"""
|
||||
Test that links from CertificateHtmlViewConfiguration are used if
|
||||
corresponding microsite or marketing urls are not present.
|
||||
"""
|
||||
self._add_course_certificates(count=1, signatory_count=1, is_active=True)
|
||||
|
||||
self.course.save()
|
||||
self.store.update_item(self.course, self.user.id)
|
||||
configuration = CertificateHtmlViewConfiguration.get_config()
|
||||
|
||||
test_url = get_certificate_url(
|
||||
user_id=self.user.id,
|
||||
course_id=unicode(self.course.id)
|
||||
)
|
||||
response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME)
|
||||
|
||||
# ABOUT is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"] or MKTG_URL_LINK_MAP,
|
||||
# so web certificate will use CertificateHtmlViewConfiguration url.
|
||||
self.assertContains(
|
||||
response,
|
||||
configuration['microsites']['testmicrosite']['company_about_url'],
|
||||
)
|
||||
# PRIVACY is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"] or MKTG_URL_LINK_MAP,
|
||||
# so web certificate will use CertificateHtmlViewConfiguration url.
|
||||
self.assertContains(
|
||||
response,
|
||||
configuration['microsites']['testmicrosite']['company_privacy_url'],
|
||||
)
|
||||
# TOS_AND_HONOR is not present in MICROSITE_CONFIGURATION['test_microsite']["urls"] or MKTG_URL_LINK_MAP,
|
||||
# so web certificate will use CertificateHtmlViewConfiguration url.
|
||||
self.assertContains(
|
||||
response,
|
||||
configuration['microsites']['testmicrosite']['company_tos_url'],
|
||||
)
|
||||
|
||||
@@ -34,7 +34,9 @@ from certificates.api import (
|
||||
get_certificate_url,
|
||||
emit_certificate_event,
|
||||
has_html_certificates_enabled,
|
||||
get_certificate_template
|
||||
get_certificate_template,
|
||||
get_certificate_header_context,
|
||||
get_certificate_footer_context,
|
||||
)
|
||||
from certificates.models import (
|
||||
GeneratedCertificate,
|
||||
@@ -540,6 +542,10 @@ def render_html_view(request, user_id, course_id):
|
||||
# Append microsite overrides
|
||||
_update_microsite_context(context, configuration)
|
||||
|
||||
# Add certificate header/footer data to current context
|
||||
context.update(get_certificate_header_context(is_secure=request.is_secure()))
|
||||
context.update(get_certificate_footer_context())
|
||||
|
||||
# Append/Override the existing view context values with any course-specific static values from Advanced Settings
|
||||
context.update(course.cert_html_view_overrides)
|
||||
|
||||
|
||||
@@ -446,6 +446,11 @@ MICROSITE_CONFIGURATION = {
|
||||
"ENABLE_SHOPPING_CART": True,
|
||||
"ENABLE_PAID_COURSE_REGISTRATION": True,
|
||||
"SESSION_COOKIE_DOMAIN": "test_microsite.localhost",
|
||||
"urls": {
|
||||
'ABOUT': 'testmicrosite/about',
|
||||
'PRIVACY': 'testmicrosite/privacy',
|
||||
'TOS_AND_HONOR': 'testmicrosite/tos-and-honor',
|
||||
},
|
||||
},
|
||||
"microsite_with_logistration": {
|
||||
"domain_prefix": "logistration",
|
||||
|
||||
Reference in New Issue
Block a user