diff --git a/common/djangoapps/student/tests/test_certificates.py b/common/djangoapps/student/tests/test_certificates.py index dea4192a90..03d5b66e00 100644 --- a/common/djangoapps/student/tests/test_certificates.py +++ b/common/djangoapps/student/tests/test_certificates.py @@ -59,7 +59,8 @@ class CertificateDisplayTest(ModuleStoreTestCase): def test_linked_student_to_web_view_credential(self, enrollment_mode): test_url = get_certificate_url( user_id=self.user.id, - course_id=unicode(self.course.id) + course_id=unicode(self.course.id), + verify_uuid='abcdefg12345678' ) self._create_certificate(enrollment_mode) diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index 17a6bebf5d..b71f309248 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -307,11 +307,14 @@ def _cert_info(user, course, cert_status, course_mode): # showing the certificate web view button if certificate is ready state and feature flags are enabled. if settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False): if get_active_web_certificate(course) is not None: + certificate_url = get_certificate_url( + user_id=user.id, + course_id=unicode(course.id), + verify_uuid=None + ) status_dict.update({ 'show_cert_web_view': True, - 'cert_web_view_url': u'{url}'.format( - url=get_certificate_url(user_id=user.id, course_id=unicode(course.id)) - ) + 'cert_web_view_url': u'{url}'.format(url=certificate_url) }) else: # don't show download certificate button if we don't have an active certificate for course diff --git a/lms/djangoapps/certificates/api.py b/lms/djangoapps/certificates/api.py index 311801dd0f..58b2b48a9f 100644 --- a/lms/djangoapps/certificates/api.py +++ b/lms/djangoapps/certificates/api.py @@ -253,15 +253,18 @@ def example_certificates_status(course_key): # pylint: disable=no-member -def get_certificate_url(user_id, course_id): +def get_certificate_url(user_id, course_id, verify_uuid): """ :return certificate url """ - url = u'{url}'.format(url=reverse('cert_html_view', - kwargs=dict( - user_id=str(user_id), - course_id=unicode(course_id)))) - return url + if settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False): + return u'{url}'.format( + url=reverse( + 'cert_html_view', + kwargs=dict(user_id=str(user_id), course_id=unicode(course_id)) + ) + ) + return '{url}{uuid}'.format(url=settings.CERTIFICATES_STATIC_VERIFY_URL, uuid=verify_uuid) def get_active_web_certificate(course, is_preview_mode=None): @@ -290,7 +293,7 @@ def emit_certificate_event(event_name, user, course_id, course=None, event_data= data = { 'user_id': user.id, 'course_id': unicode(course_id), - 'certificate_url': get_certificate_url(user.id, course_id) + 'certificate_url': get_certificate_url(user.id, course_id, event_data['certificate_id']) } event_data = event_data or {} event_data.update(data) diff --git a/lms/djangoapps/certificates/tests/test_api.py b/lms/djangoapps/certificates/tests/test_api.py index bd77a2ff94..043bdf6639 100644 --- a/lms/djangoapps/certificates/tests/test_api.py +++ b/lms/djangoapps/certificates/tests/test_api.py @@ -148,7 +148,7 @@ class GenerateUserCertificatesTest(EventTestMixin, ModuleStoreTestCase): 'edx.certificate.created', user_id=self.student.id, course_id=unicode(self.course.id), - certificate_url=certs_api.get_certificate_url(self.student.id, self.course.id), + certificate_url=certs_api.get_certificate_url(self.student.id, self.course.id, cert.verify_uuid), certificate_id=cert.verify_uuid, enrollment_mode=cert.mode, generation_mode='batch' @@ -164,7 +164,7 @@ class GenerateUserCertificatesTest(EventTestMixin, ModuleStoreTestCase): self.assertEqual(cert.status, 'error') self.assertIn(self.ERROR_REASON, cert.error_reason) - @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + @patch.dict(settings.FEATURES, {'CERTIFICATES_HTML_VIEW': True}) def test_new_cert_requests_returns_generating_for_html_certificate(self): """ Test no message sent to Xqueue if HTML certificate view is enabled diff --git a/lms/djangoapps/certificates/tests/test_views.py b/lms/djangoapps/certificates/tests/test_views.py index 979603a82a..e4f3eb7b80 100644 --- a/lms/djangoapps/certificates/tests/test_views.py +++ b/lms/djangoapps/certificates/tests/test_views.py @@ -308,7 +308,8 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase): self.assertEquals(config.configuration, test_configuration_string) test_url = get_certificate_url( user_id=self.user.id, - course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url) @@ -341,7 +342,8 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase): self.assertEquals(config.configuration, test_configuration_string) test_url = get_certificate_url( user_id=self.user.id, - course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url) @@ -427,7 +429,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): def test_render_html_view_valid_certificate(self): test_url = get_certificate_url( user_id=self.user.id, - course_id=unicode(self.course.id) # pylint: disable=no-member + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url) @@ -449,7 +452,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): def test_render_html_view_with_valid_signatories(self): test_url = get_certificate_url( user_id=self.user.id, - course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) self._add_course_certificates(count=1, signatory_count=2) response = self.client.get(test_url) @@ -465,7 +469,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): # if certificate in descriptor has not course_title then course name should not be overridden with this title. test_url = get_certificate_url( user_id=self.user.id, - course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) test_certificates = [ { @@ -488,7 +493,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): def test_certificate_view_without_org_logo(self): test_url = get_certificate_url( user_id=self.user.id, - course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) test_certificates = [ { @@ -510,7 +516,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): def test_render_html_view_without_signatories(self): test_url = get_certificate_url( user_id=self.user.id, - course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member + course_id=unicode(self.course), + verify_uuid=self.cert.verify_uuid ) self._add_course_certificates(count=1, signatory_count=0) response = self.client.get(test_url) @@ -518,19 +525,20 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): self.assertNotIn('Signatory_Title 0', response.content) @override_settings(FEATURES=FEATURES_WITH_CERTS_DISABLED) - def test_render_html_view_invalid_feature_flag(self): + def test_render_html_view_disabled_feature_flag_returns_static_url(self): test_url = get_certificate_url( user_id=self.user.id, - course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) - response = self.client.get(test_url) - self.assertIn('invalid', response.content) + self.assertIn(str(self.cert.verify_uuid), test_url) @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) def test_render_html_view_invalid_course_id(self): test_url = get_certificate_url( user_id=self.user.id, - course_id='az/23423/4vs' + course_id='az/23423/4vs', + verify_uuid=self.cert.verify_uuid ) response = self.client.get(test_url) @@ -540,7 +548,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): def test_render_html_view_invalid_course(self): test_url = get_certificate_url( user_id=self.user.id, - course_id='missing/course/key' + course_id='missing/course/key', + verify_uuid=self.cert.verify_uuid ) response = self.client.get(test_url) self.assertIn('invalid', response.content) @@ -549,7 +558,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): def test_render_html_view_invalid_user(self): test_url = get_certificate_url( user_id=111, - course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) response = self.client.get(test_url) self.assertIn('invalid', response.content) @@ -560,7 +570,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): self.assertEqual(len(GeneratedCertificate.objects.all()), 0) test_url = get_certificate_url( user_id=self.user.id, - course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) response = self.client.get(test_url) self.assertIn('invalid', response.content) @@ -576,7 +587,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): self._add_course_certificates(count=1, signatory_count=2) test_url = get_certificate_url( user_id=self.user.id, - course_id=self.course.id.to_deprecated_string() # pylint: disable=no-member + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) response = self.client.get(test_url + '?preview=honor') self.assertNotIn(self.course.display_name, response.content) @@ -594,7 +606,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): def test_render_html_view_invalid_certificate_configuration(self): test_url = get_certificate_url( user_id=self.user.id, - course_id=unicode(self.course.id) + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) response = self.client.get(test_url) self.assertIn("Invalid Certificate", response.content) @@ -606,7 +619,8 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): self.recreate_tracker() test_url = get_certificate_url( user_id=self.user.id, - course_id=unicode(self.course.id) + course_id=unicode(self.course.id), + verify_uuid=self.cert.verify_uuid ) response = self.client.get(test_url) self.assertEqual(response.status_code, 200) @@ -626,7 +640,12 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) def test_evidence_event_sent(self): - test_url = get_certificate_url(user_id=self.user.id, course_id=self.course_id) + '?evidence_visit=1' + cert_url = get_certificate_url( + user_id=self.user.id, + course_id=self.course_id, + verify_uuid=self.cert.verify_uuid + ) + test_url = '{}?evidence_visit=1'.format(cert_url) self.recreate_tracker() assertion = BadgeAssertion( user=self.user, course_id=self.course_id, mode='honor', diff --git a/lms/djangoapps/certificates/views.py b/lms/djangoapps/certificates/views.py index 963ce40304..7d0c7bf40a 100644 --- a/lms/djangoapps/certificates/views.py +++ b/lms/djangoapps/certificates/views.py @@ -439,7 +439,8 @@ def _update_certificate_context(context, course, user, user_certificate): user_certificate.mode, get_certificate_url( user_id=user.id, - course_id=course.id.to_deprecated_string() + course_id=unicode(course.id), + verify_uuid=user_certificate.verify_uuid ) ) diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 5e511086af..217da31f53 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -824,7 +824,7 @@ class ProgressPageTests(ModuleStoreTestCase): If certificate web view is enabled then certificate web view button should appear for user who certificate is available/generated """ - GeneratedCertificateFactory.create( + certificate = GeneratedCertificateFactory.create( user=self.user, course_id=self.course.id, status=CertificateStatuses.downloadable, @@ -859,7 +859,12 @@ class ProgressPageTests(ModuleStoreTestCase): resp = views.progress(self.request, course_id=unicode(self.course.id)) self.assertContains(resp, u"View Certificate") self.assertContains(resp, u"You can now view your certificate") - self.assertContains(resp, certs_api.get_certificate_url(user_id=self.user.id, course_id=self.course.id)) + cert_url = certs_api.get_certificate_url( + user_id=self.user.id, + course_id=self.course.id, + verify_uuid=certificate.verify_uuid + ) + self.assertContains(resp, cert_url) # when course certificate is not active certificates[0]['is_active'] = False diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index f9e5fc3e0b..f9db7beb9e 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -1100,7 +1100,11 @@ def _progress(request, course_key, student_id): context.update({ 'show_cert_web_view': True, 'cert_web_view_url': u'{url}'.format( - url=certs_api.get_certificate_url(user_id=student.id, course_id=unicode(course.id)) + url=certs_api.get_certificate_url( + user_id=student.id, + course_id=unicode(course.id), + verify_uuid=None + ) ) }) else: diff --git a/lms/envs/aws.py b/lms/envs/aws.py index 1f113305a6..23163c3c96 100644 --- a/lms/envs/aws.py +++ b/lms/envs/aws.py @@ -640,3 +640,7 @@ EDXNOTES_INTERNAL_API = ENV_TOKENS.get('EDXNOTES_INTERNAL_API', EDXNOTES_INTERNA ##### Credit Provider Integration ##### CREDIT_PROVIDER_SECRET_KEYS = AUTH_TOKENS.get("CREDIT_PROVIDER_SECRET_KEYS", {}) + + +############ CERTIFICATE VERIFICATION URL (STATIC FILES) ########### +ENV_TOKENS.get('CERTIFICATES_STATIC_VERIFY_URL', CERTIFICATES_STATIC_VERIFY_URL) diff --git a/lms/envs/common.py b/lms/envs/common.py index d2fedc6fed..c8eb0f0f4c 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -2067,6 +2067,9 @@ REGISTRATION_EXTRA_FIELDS = { CERT_NAME_SHORT = "Certificate" CERT_NAME_LONG = "Certificate of Achievement" +############ CERTIFICATE VERIFICATION URL (STATIC FILES) ########### +CERTIFICATES_STATIC_VERIFY_URL = "https://verify-test.edx.org/cert/" + #################### Badgr OpenBadges generation ####################### # Be sure to set up images for course modes using the BadgeImageConfiguration model in the certificates app. BADGR_API_TOKEN = None diff --git a/lms/envs/test.py b/lms/envs/test.py index 5a81e3cca2..ffc2013c47 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -472,9 +472,6 @@ FACEBOOK_APP_SECRET = "Test" FACEBOOK_APP_ID = "Test" FACEBOOK_API_VERSION = "v2.2" -# Certificates Views -FEATURES['CERTIFICATES_HTML_VIEW'] = True - ######### custom courses ######### INSTALLED_APPS += ('ccx',) FEATURES['CUSTOM_COURSES_EDX'] = True diff --git a/lms/urls.py b/lms/urls.py index d1bf250754..ed6bfffe44 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -663,11 +663,10 @@ if settings.FEATURES.get('ENABLE_OAUTH2_PROVIDER'): ) # Certificates Web/HTML View -if settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False): - urlpatterns += ( - url(r'^certificates/user/(?P[^/]*)/course/{course_id}'.format(course_id=settings.COURSE_ID_PATTERN), - 'certificates.views.render_html_view', name='cert_html_view'), - ) +urlpatterns += ( + url(r'^certificates/user/(?P[^/]*)/course/{course_id}'.format(course_id=settings.COURSE_ID_PATTERN), + 'certificates.views.render_html_view', name='cert_html_view'), +) BADGE_SHARE_TRACKER_URL = url( r'^certificates/badge_share_tracker/{}/(?P[^/]+)/(?P[^/]+)/$'.format(