Commit ca2b8c27 by Matt Drayer

Merge pull request #10988 from edx/saleem-latif/SOL-1493

SOL-1493: Web Certificates: Platform Branding integration
parents 05b96f8c 3967d809
......@@ -64,28 +64,3 @@ def get_university_for_request():
if no university was specified
"""
return microsite.get_value('university')
def get_logo_url():
"""
Return the url for the branded logo image to be used
"""
# if the MicrositeConfiguration has a value for the logo_image_url
# let's use that
image_url = microsite.get_value('logo_image_url')
if image_url:
return '{static_url}{image_url}'.format(
static_url=settings.STATIC_URL,
image_url=image_url
)
# otherwise, use the legacy means to configure this
university = microsite.get_value('university')
if university is None and settings.FEATURES.get('IS_EDX_DOMAIN', False):
return staticfiles_storage.url('images/edx-theme/edx-logo-77x36.png')
elif university:
return staticfiles_storage.url('images/{uni}-on-edx-logo.png'.format(uni=university))
else:
return staticfiles_storage.url('images/logo.png')
......@@ -25,6 +25,7 @@ from branding.models import BrandingApiConfig
log = logging.getLogger("edx.footer")
EMPTY_URL = '#'
def is_enabled():
......@@ -329,3 +330,89 @@ 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_logo_url():
"""
Return the url for the branded logo image to be used
"""
# if the MicrositeConfiguration has a value for the logo_image_url
# let's use that
image_url = microsite.get_value('logo_image_url')
if image_url:
return '{static_url}{image_url}'.format(
static_url=settings.STATIC_URL,
image_url=image_url
)
# otherwise, use the legacy means to configure this
university = microsite.get_value('university')
if university is None and settings.FEATURES.get('IS_EDX_DOMAIN', False):
return staticfiles_storage.url('images/edx-theme/edx-logo-77x36.png')
elif university:
return staticfiles_storage.url('images/{uni}-on-edx-logo.png'.format(uni=university))
else:
return staticfiles_storage.url('images/logo.png')
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
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=branding_api.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
"""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",
......
......@@ -9,7 +9,7 @@ from microsite_configuration import microsite
from microsite_configuration.templatetags.microsite import platform_name
# App that handles subdomain specific branding
import branding
from branding import api as branding_api
# app that handles site status messages
from status.status import get_site_status_msg
%>
......@@ -41,7 +41,7 @@ site_status_msg = get_site_status_msg(course_id)
<h1 class="logo" itemscope="" itemtype="http://schema.org/Organization">
<a href="${marketing_link('ROOT')}" itemprop="url">
<%block name="navigation_logo">
<img src="${static.url(branding.get_logo_url())}" alt="${_("{platform_name} Home Page").format(platform_name=platform_name())}" itemprop="logo" />
<img src="${static.url(branding_api.get_logo_url())}" alt="${_("{platform_name} Home Page").format(platform_name=platform_name())}" itemprop="logo" />
</%block>
</a>
</h1>
......
......@@ -10,7 +10,7 @@ from microsite_configuration.templatetags.microsite import platform_name
from lms.djangoapps.ccx.overrides import get_current_ccx
# App that handles subdomain specific branding
import branding
from branding import api as branding_api
# app that handles site status messages
from status.status import get_site_status_msg
%>
......@@ -42,7 +42,7 @@ site_status_msg = get_site_status_msg(course_id)
<h1 class="logo">
<a href="${marketing_link('ROOT')}">
<%block name="navigation_logo">
<img src="${static.url(branding.get_logo_url())}" alt="${_("{platform_name} Home Page").format(platform_name=platform_name())}"/>
<img src="${static.url(branding_api.get_logo_url())}" alt="${_("{platform_name} Home Page").format(platform_name=platform_name())}"/>
</%block>
</a>
</h1>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment