From 475429c0a635ce1aeeba4f760109b82b11abb397 Mon Sep 17 00:00:00 2001 From: Saleem Latif Date: Fri, 11 Dec 2015 11:35:28 +0500 Subject: [PATCH] Web Certificates: Platform Branding integration --- lms/djangoapps/branding/api.py | 62 +++++++ lms/djangoapps/certificates/api.py | 40 ++++- lms/djangoapps/certificates/tests/test_api.py | 94 +++++++++++ .../certificates/tests/test_views.py | 4 +- .../certificates/tests/test_webview_views.py | 152 +++++++++++++++++- lms/djangoapps/certificates/views/webview.py | 8 +- lms/envs/test.py | 5 + 7 files changed, 361 insertions(+), 4 deletions(-) diff --git a/lms/djangoapps/branding/api.py b/lms/djangoapps/branding/api.py index 3f51d489fa..d590ffc264 100644 --- a/lms/djangoapps/branding/api.py +++ b/lms/djangoapps/branding/api.py @@ -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") diff --git a/lms/djangoapps/certificates/api.py b/lms/djangoapps/certificates/api.py index 6e27b96dd2..3ea089c690 100644 --- a/lms/djangoapps/certificates/api.py +++ b/lms/djangoapps/certificates/api.py @@ -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 diff --git a/lms/djangoapps/certificates/tests/test_api.py b/lms/djangoapps/certificates/tests/test_api.py index 02973fa7a1..640ab68578 100644 --- a/lms/djangoapps/certificates/tests/test_api.py +++ b/lms/djangoapps/certificates/tests/test_api.py @@ -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'] + ) diff --git a/lms/djangoapps/certificates/tests/test_views.py b/lms/djangoapps/certificates/tests/test_views.py index 5fb3c5cf5b..19ad99239f 100644 --- a/lms/djangoapps/certificates/tests/test_views.py +++ b/lms/djangoapps/certificates/tests/test_views.py @@ -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) diff --git a/lms/djangoapps/certificates/tests/test_webview_views.py b/lms/djangoapps/certificates/tests/test_webview_views.py index 0c8056ab4d..d3055b5e70 100644 --- a/lms/djangoapps/certificates/tests/test_webview_views.py +++ b/lms/djangoapps/certificates/tests/test_webview_views.py @@ -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( - '