From c5912ea36791e907874d0d9f0137a92b1f08ba5c Mon Sep 17 00:00:00 2001 From: Zia Fazal Date: Thu, 12 Nov 2015 19:30:42 +0500 Subject: [PATCH] refactored code as described in SOL-1386 further code refactoring and added test fixed tests and internationalisation issues added translators comment and pulled text to same line changes based on feedback updated doctoring to make it more unstandable further optimizations --- .../certificates/tests/factories.py | 15 +- .../certificates/tests/test_views.py | 42 +- .../certificates/tests/test_webview_views.py | 113 +++- lms/djangoapps/certificates/views/webview.py | 628 ++++++++++-------- 4 files changed, 475 insertions(+), 323 deletions(-) diff --git a/lms/djangoapps/certificates/tests/factories.py b/lms/djangoapps/certificates/tests/factories.py index 599d97e5f5..eb185c50e4 100644 --- a/lms/djangoapps/certificates/tests/factories.py +++ b/lms/djangoapps/certificates/tests/factories.py @@ -38,6 +38,11 @@ class BadgeAssertionFactory(DjangoModelFactory): model = BadgeAssertion mode = 'honor' + data = { + 'image': 'http://www.example.com/image.png', + 'json': {'id': 'http://www.example.com/assertion.json'}, + 'issuer': 'http://www.example.com/issuer.json', + } class BadgeImageConfigurationFactory(DjangoModelFactory): @@ -75,7 +80,8 @@ class CertificateHtmlViewConfigurationFactory(DjangoModelFactory): }, "honor": { "certificate_type": "Honor Code", - "certificate_title": "Certificate of Achievement" + "certificate_title": "Certificate of Achievement", + "logo_url": "http://www.edx.org/honor_logo.png" }, "verified": { "certificate_type": "Verified", @@ -84,6 +90,13 @@ class CertificateHtmlViewConfigurationFactory(DjangoModelFactory): "xseries": { "certificate_title": "XSeries Certificate of Achievement", "certificate_type": "XSeries" + }, + "microsites": { + "testmicrosite": { + "company_about_url": "http://www.testmicrosite.org/about-us", + "company_privacy_url": "http://www.testmicrosite.org/edx-privacy-policy", + "company_tos_url": "http://www.testmicrosite.org/edx-terms-service" + } } }""" diff --git a/lms/djangoapps/certificates/tests/test_views.py b/lms/djangoapps/certificates/tests/test_views.py index d513975735..7c1c73f118 100644 --- a/lms/djangoapps/certificates/tests/test_views.py +++ b/lms/djangoapps/certificates/tests/test_views.py @@ -187,16 +187,6 @@ class UpdateExampleCertificateViewTest(TestCase): self.assertEqual(content['return_code'], 0) -def fakemicrosite(name, default=None): - """ - This is a test mocking function to return a microsite configuration - """ - if name == 'microsite_config_key': - return 'test_microsite' - else: - return default - - @attr('shard_1') class MicrositeCertificatesViewsTests(ModuleStoreTestCase): """ @@ -270,7 +260,6 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase): self.course.save() self.store.update_item(self.course, self.user.id) - @patch("microsite_configuration.microsite.get_value", fakemicrosite) @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) def test_html_view_for_microsite(self): test_configuration_string = """{ @@ -285,18 +274,20 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase): "logo_src": "/static/certificates/images/logo-edx.svg", "logo_url": "http://www.edx.org" }, - "test_microsite": { - "accomplishment_class_append": "accomplishment-certificate", - "platform_name": "platform_microsite", - "company_about_url": "http://www.microsite.org/about-us", - "company_privacy_url": "http://www.microsite.org/edx-privacy-policy", - "company_tos_url": "http://www.microsite.org/microsite-terms-service", - "company_verified_certificate_url": "http://www.microsite.org/verified-certificate", - "document_stylesheet_url_application": "/static/certificates/sass/main-ltr.css", - "logo_src": "/static/certificates/images/logo-microsite.svg", - "logo_url": "http://www.microsite.org", - "company_about_description": "This is special microsite aware company_about_description content", - "company_about_title": "Microsite title" + "microsites": { + "testmicrosite": { + "accomplishment_class_append": "accomplishment-certificate", + "platform_name": "platform_microsite", + "company_about_url": "http://www.microsite.org/about-us", + "company_privacy_url": "http://www.microsite.org/edx-privacy-policy", + "company_tos_url": "http://www.microsite.org/microsite-terms-service", + "company_verified_certificate_url": "http://www.microsite.org/verified-certificate", + "document_stylesheet_url_application": "/static/certificates/sass/main-ltr.css", + "logo_src": "/static/certificates/images/logo-microsite.svg", + "logo_url": "http://www.microsite.org", + "company_about_description": "This is special microsite aware company_about_description content", + "company_about_title": "Microsite title" + } }, "honor": { "certificate_type": "Honor Code" @@ -310,13 +301,12 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase): course_id=unicode(self.course.id) ) self._add_course_certificates(count=1, signatory_count=2) - response = self.client.get(test_url) + 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) self.assertIn('This is special microsite aware company_about_description content', response.content) self.assertIn('Microsite title', response.content) - @patch("microsite_configuration.microsite.get_value", fakemicrosite) @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) def test_html_view_microsite_configuration_missing(self): test_configuration_string = """{ @@ -343,7 +333,7 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase): course_id=unicode(self.course.id) ) self._add_course_certificates(count=1, signatory_count=2) - response = self.client.get(test_url) + response = self.client.get(test_url, HTTP_HOST=settings.MICROSITE_TEST_HOSTNAME) self.assertIn('edX', response.content) self.assertNotIn('platform_microsite', response.content) self.assertNotIn('http://www.microsite.org', response.content) diff --git a/lms/djangoapps/certificates/tests/test_webview_views.py b/lms/djangoapps/certificates/tests/test_webview_views.py index 362122e276..76d799cbec 100644 --- a/lms/djangoapps/certificates/tests/test_webview_views.py +++ b/lms/djangoapps/certificates/tests/test_webview_views.py @@ -23,7 +23,6 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from certificates.api import get_certificate_url from certificates.models import ( GeneratedCertificate, - BadgeAssertion, CertificateStatuses, CertificateSocialNetworks, CertificateTemplate, @@ -33,6 +32,7 @@ from certificates.models import ( from certificates.tests.factories import ( CertificateHtmlViewConfigurationFactory, LinkedInAddToProfileConfigurationFactory, + BadgeAssertionFactory, ) from util import organizations_helpers as organizations_api from django.test.client import RequestFactory @@ -221,6 +221,104 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): ) self.assertIn('logo_test1.png', response.content) + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + @patch.dict("django.conf.settings.SOCIAL_SHARING_SETTINGS", { + "CERTIFICATE_TWITTER": True, + "CERTIFICATE_FACEBOOK": True, + }) + def test_rendering_maximum_data(self): + """ + Tests at least one data item from different context update methods to + make sure every context update method is invoked while rendering certificate template. + """ + long_org_name = 'Long org name' + short_org_name = 'short_org_name' + test_organization_data = { + 'name': long_org_name, + 'short_name': short_org_name, + 'description': 'Test Organization Description', + 'active': True, + 'logo': '/logo_test1.png' + } + test_org = organizations_api.add_organization(organization_data=test_organization_data) + organizations_api.add_organization_course(organization_data=test_org, course_id=unicode(self.course.id)) + self._add_course_certificates(count=1, signatory_count=1, is_active=True) + BadgeAssertionFactory.create( + user=self.user, course_id=self.course_id, + ) + self.course.cert_html_view_overrides = { + "logo_src": "/static/certificates/images/course_override_logo.png" + } + + 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) + + # Test an item from basic info + self.assertIn( + 'Terms of Service & Honor Code', + response.content + ) + self.assertIn( + 'Certificate ID Number', + response.content + ) + # Test an item from html cert configuration + self.assertIn( + ' verifying your identity.").format( platform_name=platform_name, tos_url=context.get('company_tos_url'), - verified_cert_url=context.get('company_verified_certificate_url') + verified_cert_url=context.get('company_verified_certificate_url')) + + +def _update_context_with_basic_info(context, course_id, platform_name, configuration): + """ + Updates context dictionary with basic info required before rendering simplest + certificate templates. + """ + context['platform_name'] = platform_name + context['course_id'] = course_id + + # Update the view context with the default ConfigurationModel settings + context.update(configuration.get('default', {})) + + # Translators: 'All rights reserved' is a legal term used in copyrighting to protect published content + reserved = _("All rights reserved") + context['copyright_text'] = '© {year} {platform_name}. {reserved}.'.format( + year=settings.COPYRIGHT_YEAR, + platform_name=platform_name, + reserved=reserved + ) + + # Translators: This text is bound to the HTML 'title' element of the page and appears + # in the browser title bar when a requested certificate is not found or recognized + context['document_title'] = _("Invalid Certificate") + + # Translators: The & characters represent an ampersand character and can be ignored + context['company_tos_urltext'] = _("Terms of Service & Honor Code") + + # Translators: A 'Privacy Policy' is a legal document/statement describing a website's use of personal information + context['company_privacy_urltext'] = _("Privacy Policy") + + # Translators: This line appears as a byline to a header image and describes the purpose of the page + context['logo_subtitle'] = _("Certificate Validation") + + # Translators: Accomplishments describe the awards/certifications obtained by students on this platform + context['accomplishment_copy_about'] = _('About {platform_name} Accomplishments').format( + platform_name=platform_name + ) + + # Translators: This line appears on the page just before the generation date for the certificate + context['certificate_date_issued_title'] = _("Issued On:") + + # Translators: The Certificate ID Number is an alphanumeric value unique to each individual certificate + context['certificate_id_number_title'] = _('Certificate ID Number') + + context['certificate_info_title'] = _('About {platform_name} Certificates').format( + platform_name=platform_name ) context['certificate_verify_title'] = _("How {platform_name} Validates Student Certificates").format( @@ -218,8 +204,7 @@ def _update_certificate_context(context, course, user, user_certificate): "world's best universities, including MIT, Harvard, Berkeley, University " "of Texas, and many others. {platform_name} is a non-profit online " "initiative created by founding partners Harvard and MIT.").format( - platform_name=platform_name - ) + platform_name=platform_name) context['company_about_title'] = _("About {platform_name}").format(platform_name=platform_name) @@ -236,187 +221,59 @@ def _update_certificate_context(context, course, user, user_certificate): platform_name=platform_name ) - # Translators: This text represents the verification of the certificate - context['document_meta_description'] = _('This is a valid {platform_name} certificate for {user_name}, ' - 'who participated in {partner_short_name} {course_number}').format( - platform_name=platform_name, - user_name=user_fullname, - partner_short_name=partner_short_name, - course_number=course_number - ) - # Translators: This text is bound to the HTML 'title' element of the page and appears in the browser title bar - context['document_title'] = _("{partner_short_name} {course_number} Certificate | {platform_name}").format( - partner_short_name=partner_short_name, - course_number=course_number, - platform_name=platform_name - ) - - # Translators: This text fragment appears after the student's name (displayed in a large font) on the certificate - # screen. The text describes the accomplishment represented by the certificate information displayed to the user - context['accomplishment_copy_description_full'] = _("successfully completed, received a passing grade, and was " - "awarded a {platform_name} {certificate_type} " - "Certificate of Completion in ").format( - platform_name=platform_name, - certificate_type=context.get("certificate_type") - ) - - certificate_type_description = get_certificate_description(user_certificate.mode, certificate_type, platform_name) - if certificate_type_description: - context['certificate_type_description'] = certificate_type_description - - # Translators: This line is displayed to a user who has completed a course and achieved a certification - context['accomplishment_banner_opening'] = _("{fullname}, you've earned a certificate!").format( - fullname=user_fullname - ) - - # Translators: This line congratulates the user and instructs them to share their accomplishment on social networks - context['accomplishment_banner_congrats'] = _("Congratulations! This page summarizes all of the details of what " - "you've accomplished. Show it off to family, friends, and colleagues " - "in your social and professional networks.") - - # Translators: This line leads the reader to understand more about the certificate that a student has been awarded - context['accomplishment_copy_more_about'] = _("More about {fullname}'s accomplishment").format( - fullname=user_fullname - ) - - -@handle_500( - template_path="certificates/server-error.html", - test_func=lambda request: request.GET.get('preview', None) -) -def render_html_view(request, user_id, course_id): +def _update_course_context(request, context, course, platform_name): """ - This public view generates an HTML representation of the specified student's certificate - If a certificate is not available, we display a "Sorry!" screen instead + Updates context dictionary with course info. """ - - # Create the initial view context, bootstrapping with Django settings and passed-in values - context = {} - context['platform_name'] = microsite.get_value("platform_name", settings.PLATFORM_NAME) - context['course_id'] = course_id - preview_mode = request.GET.get('preview', None) - - # Update the view context with the default ConfigurationModel settings - configuration = CertificateHtmlViewConfiguration.get_config() - # if we are in a microsite, then let's first see if there is an override - # section in our config - config_key = microsite.get_value('microsite_config_key', 'default') - # if there is no special microsite override, then let's use default - if config_key not in configuration: - config_key = 'default' - context.update(configuration.get(config_key, {})) - - # Translators: 'All rights reserved' is a legal term used in copyrighting to protect published content - reserved = _("All rights reserved") - context['copyright_text'] = '© {year} {platform_name}. {reserved}.'.format( - year=settings.COPYRIGHT_YEAR, - platform_name=context.get('platform_name'), - reserved=reserved - ) - - # Translators: This text is bound to the HTML 'title' element of the page and appears - # in the browser title bar when a requested certificate is not found or recognized - context['document_title'] = _("Invalid Certificate") - - # Translators: The & characters represent an ampersand character and can be ignored - context['company_tos_urltext'] = _("Terms of Service & Honor Code") - - # Translators: A 'Privacy Policy' is a legal document/statement describing a website's use of personal information - context['company_privacy_urltext'] = _("Privacy Policy") - - # Translators: This line appears as a byline to a header image and describes the purpose of the page - context['logo_subtitle'] = _("Certificate Validation") - invalid_template_path = 'certificates/invalid.html' - - # Kick the user back to the "Invalid" screen if the feature is disabled - if not has_html_certificates_enabled(course_id): - return render_to_response(invalid_template_path, context) - - # Load the core building blocks for the view context - try: - course_key = CourseKey.from_string(course_id) - user = User.objects.get(id=user_id) - course = modulestore().get_course(course_key) - - if not course: - raise CourseDoesNotExist - - # Attempt to load the user's generated certificate data - if preview_mode: - user_certificate = GeneratedCertificate.objects.get( - user=user, - course_id=course_key, - mode=preview_mode - ) - else: - user_certificate = GeneratedCertificate.objects.get( - user=user, - course_id=course_key - ) - - # If there's no generated certificate data for this user, we need to see if we're in 'preview' mode... - # If we are, we'll need to create a mock version of the user_certificate container for previewing - except GeneratedCertificate.DoesNotExist: - if preview_mode and ( - has_access(request.user, 'instructor', course) - or has_access(request.user, 'staff', course) - ): - user_certificate = GeneratedCertificate( - mode=preview_mode, - verify_uuid=unicode(uuid4().hex), - modified_date=datetime.now().date() - ) - else: - return render_to_response(invalid_template_path, context) - - # For any other expected exceptions, kick the user back to the "Invalid" screen - except (InvalidKeyError, CourseDoesNotExist, User.DoesNotExist): - return render_to_response(invalid_template_path, context) - - # Badge Request Event Tracking Logic - if 'evidence_visit' in request.GET: - try: - badge = BadgeAssertion.objects.get(user=user, course_id=course_key) - tracker.emit( - 'edx.badge.assertion.evidence_visited', - { - 'user_id': user.id, - 'course_id': unicode(course_key), - 'enrollment_mode': badge.mode, - 'assertion_id': badge.id, - 'assertion_image_url': badge.data['image'], - 'assertion_json_url': badge.data['json']['id'], - 'issuer': badge.data['issuer'], - } - ) - except BadgeAssertion.DoesNotExist: - log.warn( - "Could not find badge for %s on course %s.", - user.id, - course_key, - ) - - # Okay, now we have all of the pieces, time to put everything together - - # Get the active certificate configuration for this course - # If we do not have an active certificate, we'll need to send the user to the "Invalid" screen - # Passing in the 'preview' parameter, if specified, will return a configuration, if defined - active_configuration = get_active_web_certificate(course, preview_mode) - if active_configuration is None: - return render_to_response(invalid_template_path, context) + context['full_course_image_url'] = request.build_absolute_uri(course_image_url(course)) + course_title_from_cert = context['certificate_data'].get('course_title', '') + accomplishment_copy_course_name = course_title_from_cert if course_title_from_cert else course.display_name + context['accomplishment_copy_course_name'] = accomplishment_copy_course_name + course_number = course.display_coursenumber if course.display_coursenumber else course.number + context['course_number'] = course_number + if context['organization_long_name']: + # Translators: This text represents the description of course + context['accomplishment_copy_course_description'] = _('a course of study offered by {partner_short_name}, ' + 'an online learning initiative of {partner_long_name} ' + 'through {platform_name}.').format( + partner_short_name=context['organization_short_name'], + partner_long_name=context['organization_long_name'], + platform_name=platform_name) else: - context['certificate_data'] = active_configuration + # Translators: This text represents the description of course + context['accomplishment_copy_course_description'] = _('a course of study offered by {partner_short_name}, ' + 'through {platform_name}.').format( + partner_short_name=context['organization_short_name'], + platform_name=platform_name) - # Append/Override the existing view context values with any mode-specific ConfigurationModel values - context.update(configuration.get(user_certificate.mode, {})) - # Append/Override the existing view context values with request-time values - _update_certificate_context(context, course, user, user_certificate) +def _update_social_context(request, context, course, user, user_certificate, platform_name): + """ + Updates context dictionary with info required for social sharing. + """ + share_settings = getattr(settings, 'SOCIAL_SHARING_SETTINGS', {}) + context['facebook_share_enabled'] = share_settings.get('CERTIFICATE_FACEBOOK', False) + context['facebook_app_id'] = getattr(settings, "FACEBOOK_APP_ID", None) + context['facebook_share_text'] = share_settings.get( + 'CERTIFICATE_FACEBOOK_TEXT', + _("I completed the {course_title} course on {platform_name}.").format( + course_title=context['accomplishment_copy_course_name'], + platform_name=platform_name + ) + ) + context['twitter_share_enabled'] = share_settings.get('CERTIFICATE_TWITTER', False) + context['twitter_share_text'] = share_settings.get( + 'CERTIFICATE_TWITTER_TEXT', + _("I completed a course on {platform_name}. Take a look at my certificate.").format( + platform_name=platform_name + ) + ) + share_url = request.build_absolute_uri( reverse( 'certificates:html_view', - kwargs=dict(user_id=str(user_id), course_id=unicode(course_id)) + kwargs=dict(user_id=str(user.id), course_id=unicode(course.id)) ) ) context['share_url'] = share_url @@ -427,8 +284,7 @@ def render_html_view(request, user_id, course_id): share_url=urllib.quote_plus(smart_str(share_url)) ) context['twitter_url'] = twitter_url - context['full_course_image_url'] = request.build_absolute_uri(course_image_url(course)) - + context['linked_in_url'] = None # If enabled, show the LinkedIn "add to profile" button # Clicking this button sends the user to LinkedIn where they # can add the certificate information to their profile. @@ -446,39 +302,110 @@ def render_html_view(request, user_id, course_id): course_id=unicode(course.id) ))) ) - else: - context['linked_in_url'] = None - # Microsites will need to be able to override any hard coded - # content that was put into the context in the - # _update_certificate_context() call above. For example the - # 'company_about_description' talks about edX, which we most likely - # do not want to keep in a microsite - # - # So we need to re-apply any configuration/content that - # we are sourceing from the database. This is somewhat duplicative of - # the code at the beginning of this method, but we - # need the configuration at the top as some error code paths - # require that to be set up early on in the pipeline - # - microsite_config_key = microsite.get_value('microsite_config_key') - if microsite_config_key: - context.update(configuration.get(microsite_config_key, {})) + +def _update_context_with_user_info(context, user, user_certificate): + """ + Updates context dictionary with user related info. + """ + user_fullname = user.profile.name + context['username'] = user.username + context['course_mode'] = user_certificate.mode + context['accomplishment_user_id'] = user.id + context['accomplishment_copy_name'] = user_fullname + context['accomplishment_copy_username'] = user.username + + context['accomplishment_more_title'] = _("More Information About {user_name}'s Certificate:").format( + user_name=user_fullname + ) + # Translators: This line is displayed to a user who has completed a course and achieved a certification + context['accomplishment_banner_opening'] = _("{fullname}, you've earned a certificate!").format( + fullname=user_fullname + ) + + # Translators: This line congratulates the user and instructs them to share their accomplishment on social networks + context['accomplishment_banner_congrats'] = _("Congratulations! This page summarizes all of the details of what " + "you've accomplished. Show it off to family, friends, and colleagues " + "in your social and professional networks.") + + # Translators: This line leads the reader to understand more about the certificate that a student has been awarded + context['accomplishment_copy_more_about'] = _("More about {fullname}'s accomplishment").format( + fullname=user_fullname + ) + + +def _get_user_certificate(request, user, course_key, course, preview_mode=None): + """ + Retrieves user's certificate from db. Creates one in case of preview mode. + Returns None if there is no certificate generated for given user + otherwise returns `GeneratedCertificate` instance. + """ + try: + # Attempt to load the user's generated certificate data + if preview_mode: + user_certificate = GeneratedCertificate.objects.get( + user=user, + course_id=course_key, + mode=preview_mode + ) + else: + user_certificate = GeneratedCertificate.objects.get( + user=user, + course_id=course_key + ) + + # If there's no generated certificate data for this user, we need to see if we're in 'preview' mode... + # If we are, we'll need to create a mock version of the user_certificate container for previewing + except GeneratedCertificate.DoesNotExist: + if preview_mode and ( + has_access(request.user, 'instructor', course) or + has_access(request.user, 'staff', course)): + user_certificate = GeneratedCertificate( + mode=preview_mode, + verify_uuid=unicode(uuid4().hex), + modified_date=datetime.now().date() + ) + else: + return None + + return user_certificate + + +def _track_certificate_events(request, context, course, user, user_certificate): + """ + Tracks web certificate view related events. + """ + badge = context['badge'] + # Badge Request Event Tracking Logic + if 'evidence_visit' in request.GET and badge: + tracker.emit( + 'edx.badge.assertion.evidence_visited', + { + 'user_id': user.id, + 'course_id': unicode(course.id), + 'enrollment_mode': badge.mode, + 'assertion_id': badge.id, + 'assertion_image_url': badge.data['image'], + 'assertion_json_url': badge.data['json']['id'], + 'issuer': badge.data['issuer'], + } + ) # track certificate evidence_visited event for analytics when certificate_user and accessing_user are different if request.user and request.user.id != user.id: - emit_certificate_event('evidence_visited', user, course_id, course, { + emit_certificate_event('evidence_visited', user, unicode(course.id), course, { 'certificate_id': user_certificate.verify_uuid, 'enrollment_mode': user_certificate.mode, 'social_network': CertificateSocialNetworks.linkedin }) - # Append/Override the existing view context values with any course-specific static values from Advanced Settings - context.update(course.cert_html_view_overrides) - # FINALLY, generate and send the output the client +def _render_certificate_template(request, context, course, user_certificate): + """ + Picks appropriate certificate templates and renders it. + """ if settings.FEATURES.get('CUSTOM_CERTIFICATE_TEMPLATES_ENABLED', False): - custom_template = get_certificate_template(course_key, user_certificate.mode) + custom_template = get_certificate_template(course.id, user_certificate.mode) if custom_template: template = Template( custom_template, @@ -491,3 +418,134 @@ def render_html_view(request, user_id, course_id): return HttpResponse(template.render(context)) return render_to_response("certificates/valid.html", context) + + +def _update_microsite_context(context, configuration): + """ + Updates context with microsites data. + Microsites will need to be able to override any hard coded + content that was put into the context in the + _update_certificate_context() call above. For example the + 'company_about_description' talks about edX, which we most likely + do not want to keep in a microsite + + So we need to re-apply any configuration/content that + we are sourcing from the database. This is somewhat duplicative of + the code at the beginning of this method, but we + need the configuration at the top as some error code paths + require that to be set up early on in the pipeline + """ + + microsite_config_key = microsite.get_value('domain_prefix') + microsites_config = configuration.get("microsites", {}) + if microsite_config_key and microsites_config: + context.update(microsites_config.get(microsite_config_key, {})) + + +def _update_badge_context(context, course, user): + """ + Updates context with badge info. + """ + try: + badge = BadgeAssertion.objects.get(user=user, course_id=course.location.course_key) + except BadgeAssertion.DoesNotExist: + badge = None + context['badge'] = badge + + +def _update_organization_context(context, course): + """ + Updates context with organization related info. + """ + partner_long_name, organization_logo = None, None + partner_short_name = course.display_organization if course.display_organization else course.org + organizations = organization_api.get_course_organizations(course_id=course.id) + if organizations: + #TODO Need to add support for multiple organizations, Currently we are interested in the first one. + organization = organizations[0] + partner_long_name = organization.get('name', partner_long_name) + partner_short_name = organization.get('short_name', partner_short_name) + organization_logo = organization.get('logo', None) + + context['organization_long_name'] = partner_long_name + context['organization_short_name'] = partner_short_name + context['accomplishment_copy_course_org'] = partner_short_name + context['organization_logo'] = organization_logo + + +@handle_500( + template_path="certificates/server-error.html", + test_func=lambda request: request.GET.get('preview', None) +) +def render_html_view(request, user_id, course_id): + """ + This public view generates an HTML representation of the specified student's certificate + If a certificate is not available, we display a "Sorry!" screen instead + """ + preview_mode = request.GET.get('preview', None) + platform_name = microsite.get_value("platform_name", settings.PLATFORM_NAME) + configuration = CertificateHtmlViewConfiguration.get_config() + # Create the initial view context, bootstrapping with Django settings and passed-in values + context = {} + _update_context_with_basic_info(context, course_id, platform_name, configuration) + invalid_template_path = 'certificates/invalid.html' + + # Kick the user back to the "Invalid" screen if the feature is disabled + if not has_html_certificates_enabled(course_id): + return render_to_response(invalid_template_path, context) + + # Load the course and user objects + try: + course_key = CourseKey.from_string(course_id) + user = User.objects.get(id=user_id) + course = modulestore().get_course(course_key) + + # For any other expected exceptions, kick the user back to the "Invalid" screen + except (InvalidKeyError, ItemNotFoundError, User.DoesNotExist): + return render_to_response(invalid_template_path, context) + + # Load user's certificate + user_certificate = _get_user_certificate(request, user, course_key, course, preview_mode) + if not user_certificate: + return render_to_response(invalid_template_path, context) + + # Get the active certificate configuration for this course + # If we do not have an active certificate, we'll need to send the user to the "Invalid" screen + # Passing in the 'preview' parameter, if specified, will return a configuration, if defined + active_configuration = get_active_web_certificate(course, preview_mode) + if active_configuration is None: + return render_to_response(invalid_template_path, context) + context['certificate_data'] = active_configuration + + # Append/Override the existing view context values with any mode-specific ConfigurationModel values + context.update(configuration.get(user_certificate.mode, {})) + + # Append organization info + _update_organization_context(context, course) + + # Append course info + _update_course_context(request, context, course, platform_name) + + # Append user info + _update_context_with_user_info(context, user, user_certificate) + + # Append social sharing info + _update_social_context(request, context, course, user, user_certificate, platform_name) + + # Append/Override the existing view context values with certificate specific values + _update_certificate_context(context, user_certificate, platform_name) + + # Append badge info + _update_badge_context(context, course, user) + + # Append microsite overrides + _update_microsite_context(context, configuration) + + # Append/Override the existing view context values with any course-specific static values from Advanced Settings + context.update(course.cert_html_view_overrides) + + # Track certificate view events + _track_certificate_events(request, context, course, user, user_certificate) + + # FINALLY, render appropriate certificate + return _render_certificate_template(request, context, course, user_certificate)