Merge pull request #24566 from edx/private_to_public_03835a5

Mergeback PR from private to public.
This commit is contained in:
edx-pipeline-bot
2020-07-22 17:24:11 +05:00
committed by GitHub
23 changed files with 257 additions and 199 deletions

View File

@@ -51,9 +51,8 @@ class LMSLinksTestCase(TestCase):
mode = 'professional'
self.assertEqual(
utils.get_lms_link_for_certificate_web_view(dummy_user, course_key, mode),
"//localhost:8000/certificates/user/{user_id}/course/{course_key}?preview={mode}".format(
user_id=dummy_user,
utils.get_lms_link_for_certificate_web_view(course_key, mode),
"//localhost:8000/certificates/course/{course_key}?preview={mode}".format(
course_key=course_key,
mode=mode
)
@@ -61,9 +60,8 @@ class LMSLinksTestCase(TestCase):
with with_site_configuration_context(configuration={"course_org_filter": "mitX", "LMS_BASE": "dummyhost:8000"}):
self.assertEqual(
utils.get_lms_link_for_certificate_web_view(dummy_user, course_key, mode),
"//dummyhost:8000/certificates/user/{user_id}/course/{course_key}?preview={mode}".format(
user_id=dummy_user,
utils.get_lms_link_for_certificate_web_view(course_key, mode),
"//dummyhost:8000/certificates/course/{course_key}?preview={mode}".format(
course_key=course_key,
mode=mode
)

View File

@@ -139,7 +139,7 @@ def get_lms_link_for_item(location, preview=False):
)
def get_lms_link_for_certificate_web_view(user_id, course_key, mode):
def get_lms_link_for_certificate_web_view(course_key, mode):
"""
Returns the url to the certificate web view.
"""
@@ -151,9 +151,8 @@ def get_lms_link_for_certificate_web_view(user_id, course_key, mode):
if lms_base is None:
return None
return u"//{certificate_web_base}/certificates/user/{user_id}/course/{course_id}?preview={mode}".format(
return u"//{certificate_web_base}/certificates/course/{course_id}?preview={mode}".format(
certificate_web_base=lms_base,
user_id=user_id,
course_id=six.text_type(course_key),
mode=mode
)

View File

@@ -411,7 +411,6 @@ def certificates_list_handler(request, course_key_string):
if has_certificate_modes:
certificate_web_view_url = get_lms_link_for_certificate_web_view(
user_id=request.user.id,
course_key=course_key,
mode=course_modes[0] # CourseMode.modes_for_course returns default mode if doesn't find anyone.
)

View File

@@ -263,7 +263,6 @@ class CertificatesListHandlerTestCase(
@override_settings(LMS_BASE=None)
def test_no_lms_base_for_certificate_web_view_link(self):
test_link = get_lms_link_for_certificate_web_view(
user_id=self.user.id,
course_key=self.course.id,
mode='honor'
)
@@ -271,10 +270,9 @@ class CertificatesListHandlerTestCase(
@override_settings(LMS_BASE="lms_base_url")
def test_lms_link_for_certificate_web_view(self):
test_url = "//lms_base_url/certificates/user/" \
+ str(self.user.id) + "/course/" + six.text_type(self.course.id) + '?preview=honor'
test_url = "//lms_base_url/certificates/" \
"course/" + six.text_type(self.course.id) + '?preview=honor'
link = get_lms_link_for_certificate_web_view(
user_id=self.user.id,
course_key=self.course.id,
mode='honor'
)

View File

@@ -1,9 +1,11 @@
define(
[
'jquery', 'backbone', 'underscore',
'js/views/video/transcripts/utils'
'js/views/video/transcripts/utils',
'edx-ui-toolkit/js/utils/html-utils'
],
function($, Backbone, _, TranscriptUtils) {
function($, Backbone, _, TranscriptUtils, HtmlUtils) {
'use strict';
var FileUploader = Backbone.View.extend({
invisibleClass: 'is-invisible',
@@ -37,9 +39,8 @@ function($, Backbone, _, TranscriptUtils) {
return;
}
this.template = _.template(tpl);
tplContainer.html(this.template({
this.template = HtmlUtils.template(tpl);
HtmlUtils.setHtml(tplContainer, this.template({
ext: this.validFileExtensions,
component_locator: this.options.component_locator
}));
@@ -126,11 +127,12 @@ function($, Backbone, _, TranscriptUtils) {
*
*/
checkExtValidity: function(file) {
var fileExtension;
if (!file.name) {
return void(0);
}
var fileExtension = file.name
fileExtension = file.name
.split('.')
.pop()
.toLowerCase();
@@ -153,7 +155,7 @@ function($, Backbone, _, TranscriptUtils) {
this.$progress
.width(percentVal)
.html(percentVal)
.text(percentVal)
.removeClass(this.invisibleClass);
},
@@ -177,7 +179,7 @@ function($, Backbone, _, TranscriptUtils) {
this.$progress
.width(percentVal)
.html(percentVal);
.text(percentVal);
},
/**

View File

@@ -21,7 +21,7 @@
<%block name="page_bundle">
<%static:webpack entry="js/factories/edit_tabs">
EditTabsFactory("${context_course.location | n, js_escaped_string}", "${reverse('tabs_handler', kwargs={'course_key_string': context_course.id})}");
EditTabsFactory("${context_course.location | n, js_escaped_string}", "${reverse('tabs_handler', kwargs={'course_key_string': context_course.id}) | n, js_escaped_string}");
</%static:webpack>
</%block>

View File

@@ -1,3 +1,5 @@
<%page expression_filter="h"/>
<%inherit file="base.html" />
<%!
from django.utils.translation import ugettext as _
@@ -110,7 +112,7 @@ from openedx.core.djangolib.js_utils import (
<%block name="requirejs">
require(["js/factories/manage_users_lib"], function(ManageLibraryUsersFactory) {
ManageLibraryUsersFactory(
"${context_library.display_name_with_default | h}",
"${context_library.display_name_with_default | n, js_escaped_string}",
${users | n, dump_js_escaped_json},
"${reverse('course_team_handler', kwargs={'course_key_string': library_key, 'email': '@@EMAIL@@'}) | n, js_escaped_string}",
${request.user.id | n, dump_js_escaped_json},

View File

@@ -7,6 +7,7 @@ import hashlib
import logging
import six
from django.urls import reverse
from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _
@@ -15,6 +16,7 @@ from badges.models import BadgeAssertion, BadgeClass, CourseCompleteImageConfigu
from badges.utils import requires_badges_enabled, site_prefix
from xmodule.modulestore.django import modulestore
LOGGER = logging.getLogger(__name__)
@@ -63,8 +65,11 @@ def evidence_url(user_id, course_key):
event.
"""
course_id = six.text_type(course_key)
# avoid circular import problems
from lms.djangoapps.certificates.models import GeneratedCertificate
cert = GeneratedCertificate.eligible_certificates.get(user__id=int(user_id), course_id=course_id)
return site_prefix() + reverse(
'certificates:html_view', kwargs={'user_id': user_id, 'course_id': course_id}) + '?evidence_visit=1'
'certificates:render_cert_by_uuid', kwargs={'certificate_uuid': cert.verify_uuid}) + '?evidence_visit=1'
def criteria(course_key):

View File

@@ -1,11 +1,11 @@
"""
Tests for the course completion helper functions.
"""
from datetime import datetime
from uuid import uuid4
from badges.events import course_complete
from lms.djangoapps.certificates.models import GeneratedCertificate
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -64,9 +64,19 @@ class CourseCompleteTestCase(ModuleStoreTestCase):
Make sure the evidence URL points to the right place.
"""
user = UserFactory.create()
cert = GeneratedCertificate.eligible_certificates.create(
user=user,
course_id=self.course_key,
download_uuid=uuid4(),
grade="0.95",
key='the_key',
distinction=True,
status='downloadable',
mode='honor',
name=user.profile.name,
verify_uuid=uuid4().hex
)
self.assertEqual(
'https://edx.org/certificates/user/{user_id}/course/{course_key}?evidence_visit=1'.format(
user_id=user.id, course_key=self.course_key
),
'https://edx.org/certificates/{}?evidence_visit=1'.format(cert.verify_uuid),
course_complete.evidence_url(user.id, self.course_key)
)

View File

@@ -312,7 +312,9 @@ def certificate_downloadable_status(student, course_key):
if current_status['status'] == CertificateStatuses.downloadable and may_view_certificate:
response_data['is_downloadable'] = True
response_data['download_url'] = current_status['download_url'] or get_certificate_url(student.id, course_key)
response_data['download_url'] = current_status['download_url'] or get_certificate_url(
student.id, course_key, current_status['uuid']
)
response_data['is_pdf_certificate'] = bool(current_status['download_url'])
response_data['uuid'] = current_status['uuid']
@@ -472,13 +474,13 @@ def _course_from_key(course_key):
return CourseOverview.get_from_id(_safe_course_key(course_key))
def _certificate_html_url(user_id, course_id, uuid):
if uuid:
return reverse('certificates:render_cert_by_uuid', kwargs={'certificate_uuid': uuid})
elif user_id and course_id:
kwargs = {"user_id": str(user_id), "course_id": six.text_type(course_id)}
return reverse('certificates:html_view', kwargs=kwargs)
return ''
def _certificate_html_url(uuid):
"""
Returns uuid based certificate URL.
"""
return reverse(
'certificates:render_cert_by_uuid', kwargs={'certificate_uuid': uuid}
) if uuid else ''
def _certificate_download_url(user_id, course_id, user_certificate=None):
@@ -515,7 +517,7 @@ def get_certificate_url(user_id=None, course_id=None, uuid=None, user_certificat
return url
if has_html_certificates_enabled(course):
url = _certificate_html_url(user_id, course_id, uuid)
url = _certificate_html_url(uuid)
else:
url = _certificate_download_url(user_id, course_id, user_certificate=user_certificate)
return url
@@ -631,10 +633,11 @@ def emit_certificate_event(event_name, user, course_id, course=None, event_data=
'org_id': course.org,
'course_id': six.text_type(course_id)
}
data = {
'user_id': user.id,
'course_id': six.text_type(course_id),
'certificate_url': get_certificate_url(user.id, course_id)
'certificate_url': get_certificate_url(user.id, course_id, uuid=event_data['certificate_id'])
}
event_data = event_data or {}
event_data.update(data)

View File

@@ -199,10 +199,7 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes
'is_downloadable': True,
'is_generating': False,
'is_unverified': False,
'download_url': '/certificates/user/{user_id}/course/{course_id}'.format(
user_id=self.student.id,
course_id=self.course.id,
),
'download_url': '/certificates/{uuid}'.format(uuid=cert_status['uuid']),
'is_pdf_certificate': False,
'uuid': cert_status['uuid']
}
@@ -496,16 +493,14 @@ class CertificateGetTests(SharedModuleStoreTestCase):
self.assertEqual(expected_url, cert_url)
expected_url = reverse(
'certificates:html_view',
kwargs={
"user_id": str(self.student.id),
"course_id": six.text_type(self.web_cert_course.id),
}
'certificates:render_cert_by_uuid',
kwargs=dict(certificate_uuid=self.uuid)
)
cert_url = certs_api.get_certificate_url(
user_id=self.student.id,
course_id=self.web_cert_course.id
course_id=self.web_cert_course.id,
uuid=self.uuid
)
self.assertEqual(expected_url, cert_url)

View File

@@ -4,9 +4,11 @@ Tests for certificate app views used by the support team.
import json
from uuid import uuid4
import ddt
import six
from django.conf import settings
from django.test.utils import override_settings
from django.urls import reverse
@@ -85,6 +87,7 @@ class CertificateSupportTestCase(ModuleStoreTestCase):
status=self.CERT_STATUS,
mode=self.CERT_MODE,
download_url=self.CERT_DOWNLOAD_URL,
verify_uuid=uuid4().hex
)
# Login as support staff
@@ -226,8 +229,8 @@ class CertificateSearchTests(CertificateSupportTestCase):
self.assertEqual(
retrieved_cert["download_url"],
reverse(
'certificates:html_view',
kwargs={"user_id": self.student.id, "course_id": self.course.id}
'certificates:render_cert_by_uuid',
kwargs={"certificate_uuid": self.cert.verify_uuid}
)
)
self.assertTrue(retrieved_cert["regenerate"])

View File

@@ -229,13 +229,14 @@ class CertificatesViewsSiteTests(ModuleStoreTestCase):
self.cert = GeneratedCertificate.eligible_certificates.create(
user=self.user,
course_id=self.course_id,
download_uuid=uuid4(),
download_uuid=uuid4().hex,
grade="0.95",
key='the_key',
distinction=True,
status='downloadable',
mode='honor',
name=self.user.profile.name,
verify_uuid=uuid4().hex
)
self._setup_configuration()
@@ -284,7 +285,8 @@ class CertificatesViewsSiteTests(ModuleStoreTestCase):
def test_html_view_for_site(self):
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
self._add_course_certificates(count=1, signatory_count=2)
response = self.client.get(test_url)
@@ -302,7 +304,8 @@ class CertificatesViewsSiteTests(ModuleStoreTestCase):
def test_html_view_site_configuration_missing(self):
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
self._add_course_certificates(count=1, signatory_count=2)
response = self.client.get(test_url)

View File

@@ -102,7 +102,7 @@ class CommonCertificatesTestCase(ModuleStoreTestCase):
self.cert = GeneratedCertificateFactory.create(
user=self.user,
course_id=self.course_id,
download_uuid=uuid4(),
download_uuid=uuid4().hex,
download_url="http://www.example.com/certificates/download",
grade="0.95",
key='the_key',
@@ -409,7 +409,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._add_course_certificates(count=1, signatory_count=1, is_active=True)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
self.assertContains(
@@ -489,7 +490,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url, HTTP_HOST='test.localhost')
@@ -527,7 +529,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._add_course_certificates(count=1, signatory_count=2)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
self.assertContains(response, str(self.cert.verify_uuid))
@@ -553,7 +556,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._add_course_certificates(count=1, signatory_count=2)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
# Validate certificate
@@ -564,9 +568,7 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self.cert.status = CertificateStatuses.generating
self.cert.save()
response = self.client.get(test_url)
self.assertContains(response, "Invalid Certificate")
self.assertContains(response, "Cannot Find Certificate")
self.assertContains(response, "We cannot find a certificate with this URL or ID number.")
self.assertEqual(response.status_code, 404)
@ddt.data(
(CertificateStatuses.downloadable, True),
@@ -588,27 +590,26 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._add_course_certificates(count=1, signatory_count=2)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
if eligible_for_certificate:
self.assertContains(response, str(self.cert.verify_uuid))
else:
self.assertContains(response, "Invalid Certificate")
self.assertContains(response, "Cannot Find Certificate")
self.assertContains(response, "We cannot find a certificate with this URL or ID number.")
self.assertNotContains(response, str(self.cert.verify_uuid))
self.assertEqual(response.status_code, 404)
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
def test_html_view_for_invalid_certificate(self):
def test_html_view_returns_404_for_invalid_certificate(self):
"""
Tests that Certificate HTML Web View returns "Cannot Find Certificate" if certificate has been invalidated.
Tests that Certificate HTML Web View successfully retrieves certificate only
if the certificate is not invalidated otherwise returns 404
"""
self._add_course_certificates(count=1, signatory_count=2)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
# Validate certificate
@@ -618,32 +619,7 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
# invalidate certificate and verify that "Cannot Find Certificate" is returned
self.cert.invalidate()
response = self.client.get(test_url)
self.assertContains(response, "Invalid Certificate")
self.assertContains(response, "Cannot Find Certificate")
self.assertContains(response, "We cannot find a certificate with this URL or ID number.")
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
def test_html_lang_attribute_is_dynamic_for_invalid_certificate_html_view(self):
"""
Tests that Certificate HTML Web View's lang attribute is based on user language.
"""
self._add_course_certificates(count=1, signatory_count=2)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
)
self.cert.invalidate()
user_language = 'fr'
self.client.cookies[settings.LANGUAGE_COOKIE] = user_language
response = self.client.get(test_url)
self.assertContains(response, '<html class="no-js" lang="fr">')
user_language = 'ar'
self.client.cookies[settings.LANGUAGE_COOKIE] = user_language
response = self.client.get(test_url)
self.assertContains(response, '<html class="no-js" lang="ar">')
self.assertEqual(response.status_code, 404)
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
def test_html_lang_attribute_is_dynamic_for_certificate_html_view(self):
@@ -653,7 +629,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._add_course_certificates(count=1, signatory_count=2)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
user_language = 'fr'
@@ -688,7 +665,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
self.assertContains(response, "Invalid Certificate")
@@ -700,7 +678,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._add_course_certificates(count=1, signatory_count=2)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
@@ -729,7 +708,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self.store.update_item(self.course, self.user.id)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
@@ -747,7 +727,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._add_course_certificates(count=1, signatory_count=2)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
self.course.display_coursenumber = "overridden_number"
@@ -776,7 +757,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
# make sure response html has only one organization logo container for edX
@@ -787,7 +769,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._add_course_certificates(count=1, signatory_count=0)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
self.assertNotContains(response, 'Signatory_Name 0')
@@ -815,7 +798,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
self.assertNotContains(response, '<script>')
@@ -825,7 +809,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
def test_render_html_view_disabled_feature_flag_returns_static_url(self):
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
self.assertIn(str(self.cert.download_url), test_url)
@@ -838,38 +823,19 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
response = self.client.get(test_url)
self.assertContains(response, 'invalid')
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
def test_render_html_view_invalid_user(self):
self._add_course_certificates(count=1, signatory_count=0)
test_url = get_certificate_url(
user_id=111,
course_id=six.text_type(self.course.id)
)
response = self.client.get(test_url)
self.assertContains(response, 'invalid')
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
def test_render_html_view_non_int_user(self):
self._add_course_certificates(count=1, signatory_count=0)
test_url = get_certificate_url(
user_id="Good tests make good neighbors",
course_id=six.text_type(self.course.id)
)
response = self.client.get(test_url)
self.assertEqual(response.status_code, 404)
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
def test_render_html_view_invalid_user_certificate(self):
self._add_course_certificates(count=1, signatory_count=0)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
self.cert.delete()
self.assertListEqual(list(GeneratedCertificate.eligible_certificates.all()), [])
response = self.client.get(test_url)
self.assertContains(response, 'invalid')
self.assertEqual(response.status_code, 404)
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED, PLATFORM_NAME=u'Űńíćődé Űńívéŕśítӳ')
def test_render_html_view_with_unicode_platform_name(self):
@@ -877,32 +843,37 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
self.assertEqual(response.status_code, 200)
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
def test_render_html_view_with_preview_mode(self):
def test_user_id_cert_url_not_supported(self):
"""
test certificate web view should render properly along with its signatories information when accessing it in
preview mode. Either the certificate is marked active or not.
tests the user id based certificate url is no longer supported
"""
self.cert.delete()
self.assertListEqual(list(GeneratedCertificate.eligible_certificates.all()), [])
self._add_course_certificates(count=1, signatory_count=2)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
test_url = reverse(
'certificates:unsupported_url', kwargs={'user_id': self.user.id, 'course_id': self.course_id}
)
response = self.client.get(test_url + '?preview=honor')
# accessing certificate web view in preview mode without
# staff or instructor access should show invalid certificate
self.assertContains(response, 'Cannot Find Certificate')
CourseStaffRole(self.course.id).add_users(self.user)
self.assertContains(response, 'URL Not Supported')
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
def test_render_html_view_with_preview_mode(self):
"""
test certificate web view should render properly along with its signatories information when accessing it in
preview mode only for the staff users. Either the certificate is marked active or not.
"""
self._add_course_certificates(count=1, signatory_count=2)
self.user.is_staff = True
self.user.save()
test_url = reverse('certificates:preview_cert', kwargs={'course_id': self.course_id})
response = self.client.get(test_url + '?preview=honor')
self.assertNotContains(response, self.course.display_name.encode('utf-8'))
self.assertContains(response, 'course_title_0')
self.assertContains(response, 'Signatory_Title 0')
@@ -926,7 +897,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
# user has already has certificate generated for 'honor' mode
# so let's try to preview in 'verified' mode.
@@ -953,7 +925,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._add_course_certificates(count=1, signatory_count=1, is_active=True)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
if self_paced or self.course.certificate_available_date > today:
@@ -977,7 +950,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
self.assertContains(response, "Invalid Certificate")
@@ -989,7 +963,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url + "?preview=honor")
self.assertContains(response, "Invalid Certificate Configuration")
@@ -1042,7 +1017,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._create_custom_named_template('test_template_3_course', org_id=2, mode='honor')
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
with patch('lms.djangoapps.certificates.api.get_course_organization_id') as mock_get_org_id:
@@ -1082,7 +1058,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._create_custom_named_template('test_template_4_course', org_id=2, mode='honor') # wrong org
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
with patch('lms.djangoapps.certificates.api.get_course_organization_id') as mock_get_org_id:
@@ -1105,7 +1082,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._create_custom_named_template('test_template_3_course', org_id=2, mode=None) # wrong org
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
with patch('lms.djangoapps.certificates.api.get_course_organization_id') as mock_get_org_id:
@@ -1129,7 +1107,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._create_custom_named_template('test_template_3_course', org_id=2, mode=mode) # wrong org
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
with patch('lms.djangoapps.certificates.api.get_course_organization_id') as mock_get_org_id:
@@ -1174,7 +1153,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
#create a org_mode_and_coursekey template language=null
self._create_custom_named_template(
@@ -1253,7 +1233,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
#create a org and mode template language=null
self._create_custom_named_template('test_null_lang_template', org_id=1, mode='honor', language=None)
@@ -1310,7 +1291,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._add_course_certificates(count=1, signatory_count=2)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
#create a org template language=null
self._create_custom_named_template('test_null_lang_template', org_id=1, language=None)
@@ -1368,7 +1350,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
#create a mode template language=null
self._create_custom_named_template('test_null_lang_template', mode='honor', language=None)
@@ -1429,7 +1412,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
#create a mode template language=null
self._create_custom_named_template('test_null_lang_template', org_id=1, mode='honor', language=None)
@@ -1485,7 +1469,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._create_custom_template_with_hours_of_effort(org_id=1, language=None)
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
self.assertEqual(response.status_code, 200)
@@ -1510,7 +1495,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
}):
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
with patch.dict("django.conf.settings.SOCIAL_SHARING_SETTINGS", {
"CERTIFICATE_TWITTER": True,
@@ -1539,7 +1525,8 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
self._create_custom_template(mode='honor')
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
# render certificate without template asset
@@ -1578,7 +1565,8 @@ class CertificateEventTests(CommonCertificatesTestCase, EventTrackingTestCase):
self.recreate_tracker()
test_url = get_certificate_url(
user_id=self.user.id,
course_id=six.text_type(self.course.id)
course_id=six.text_type(self.course.id),
uuid=self.cert.verify_uuid
)
response = self.client.get(test_url)
self.assertEqual(response.status_code, 200)
@@ -1608,7 +1596,8 @@ class CertificateEventTests(CommonCertificatesTestCase, EventTrackingTestCase):
cert_url = get_certificate_url(
user_id=self.user.id,
course_id=self.course_id
course_id=self.course_id,
uuid=self.cert.verify_uuid
)
test_url = '{}?evidence_visit=1'.format(cert_url)
self.recreate_tracker()

View File

@@ -13,8 +13,14 @@ urlpatterns = [
# Certificates HTML view end point to render web certs by user and course
url(
r'^user/(?P<user_id>[^/]*)/course/{course_id}'.format(course_id=settings.COURSE_ID_PATTERN),
views.render_html_view,
name='html_view'
views.unsupported_url,
name='unsupported_url'
),
url(
r'^course/{course_id}'.format(course_id=settings.COURSE_ID_PATTERN),
views.render_preview_certificate,
name='preview_cert'
),
# Certificates HTML view end point to render web certs by certificate_uuid

View File

@@ -8,10 +8,11 @@ import logging
from datetime import datetime
from uuid import uuid4
import pytz
import six
import pytz
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.http import Http404, HttpResponse
from django.template import RequestContext
from django.utils import translation
@@ -444,6 +445,26 @@ def _update_organization_context(context, course):
context['organization_logo'] = organization_logo
def unsupported_url(request, user_id, course_id):
"""
This view returns the un-supported url page aimed to let the user aware that
url is no longer supported
"""
platform_name = configuration_helpers.get_value("platform_name", settings.PLATFORM_NAME)
configuration = CertificateHtmlViewConfiguration.get_config()
return _render_invalid_certificate(
request, course_id, platform_name, configuration, cert_path='certificates/url_unsupported.html'
)
@login_required
def render_preview_certificate(request, course_id):
"""
This view renders the course certificate in preview mode
"""
return render_html_view(request, six.text_type(course_id))
def render_cert_by_uuid(request, certificate_uuid):
"""
This public view generates an HTML representation of the specified certificate
@@ -453,7 +474,7 @@ def render_cert_by_uuid(request, certificate_uuid):
verify_uuid=certificate_uuid,
status=CertificateStatuses.downloadable
)
return render_html_view(request, certificate.user.id, six.text_type(certificate.course_id))
return render_html_view(request, six.text_type(certificate.course_id), certificate)
except GeneratedCertificate.DoesNotExist:
raise Http404
@@ -462,16 +483,13 @@ def render_cert_by_uuid(request, certificate_uuid):
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 render_html_view(request, course_id, certificate=None):
"""
This public view generates an HTML representation of the specified user and course
If a certificate is not available, we display a "Sorry!" screen instead
"""
try:
user_id = int(user_id)
except ValueError:
raise Http404
user = certificate.user if certificate else request.user
user_id = user.id
preview_mode = request.GET.get('preview', None)
platform_name = configuration_helpers.get_value("platform_name", settings.PLATFORM_NAME)
configuration = CertificateHtmlViewConfiguration.get_config()
@@ -483,16 +501,15 @@ def render_html_view(request, user_id, course_id):
# Load the course and user objects
try:
course_key = CourseKey.from_string(course_id)
user = User.objects.get(id=user_id)
course = get_course_by_id(course_key)
# For any course or user exceptions, kick the user back to the "Invalid" screen
except (InvalidKeyError, User.DoesNotExist, Http404) as exception:
except (InvalidKeyError, Http404) as exception:
error_str = (
u"Invalid cert: error finding course %s or user with id "
u"%d. Specific error: %s"
u"Invalid cert: error finding course %s "
u"Specific error: %s"
)
log.info(error_str, course_id, user_id, str(exception))
log.info(error_str, course_id, str(exception))
return _render_invalid_certificate(request, course_id, platform_name, configuration)
# Kick the user back to the "Invalid" screen if the feature is disabled for the course
@@ -654,7 +671,8 @@ def _get_custom_template_and_language(course_id, course_mode, course_language):
return (None, None)
def _render_invalid_certificate(request, course_id, platform_name, configuration):
def _render_invalid_certificate(request, course_id, platform_name, configuration,
cert_path=INVALID_CERTIFICATE_TEMPLATE_PATH):
"""
Renders the invalid certificate view with default header and footer.
"""
@@ -663,7 +681,7 @@ def _render_invalid_certificate(request, course_id, platform_name, 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())
return render_to_response(INVALID_CERTIFICATE_TEMPLATE_PATH, context)
return render_to_response(cert_path, context)
def _render_valid_certificate(request, context, custom_template=None):

View File

@@ -410,10 +410,7 @@ class TestUserEnrollmentCertificates(UrlResetMixin, MobileAPITestCase, Milestone
certificate_data = response.data[0]['certificate']
self.assertRegex(
certificate_data['url'],
r'http.*/certificates/user/{user_id}/course/{course_id}'.format(
user_id=self.user.id,
course_id=self.course.id,
)
r'http.*/certificates/[0-9a-f]{32}'
)

View File

@@ -126,7 +126,10 @@ var edx = edx || {};
// Get or create the step container
$stepEl = $('#current-step-container');
if (!$stepEl.length) {
$stepEl = $('<div id="current-step-container"></div>').appendTo(this.el);
$stepEl = edx.HtmlUtils.append(
$(this.el),
edx.HtmlUtils.HTML('<div id="current-step-container"></div>').toString()
);
}
// Render the subview

View File

@@ -83,7 +83,10 @@
// Get or create the step container
$stepEl = $('#current-step-container');
if (!$stepEl.length) {
$stepEl = $('<div id="current-step-container"></div>').appendTo(this.el);
$stepEl = edx.HtmlUtils.append(
$(this.el),
edx.HtmlUtils.HTML('<div id="current-step-container"></div>').toString()
);
}
// Render the step subview

View File

@@ -1,15 +1,16 @@
(function(define) {
'use strict';
define([
'gettext', 'jquery', 'underscore', 'backbone', 'js/views/fields',
'gettext', 'jquery', 'underscore', 'backbone',
'edx-ui-toolkit/js/utils/html-utils', 'js/views/fields',
'text!templates/fields/field_image.underscore',
'backbone-super', 'jquery.fileupload'
], function(gettext, $, _, Backbone, FieldViews, field_image_template) {
], function(gettext, $, _, Backbone, HtmlUtils, FieldViews, FieldImageTemplate) {
var ImageFieldView = FieldViews.FieldView.extend({
fieldType: 'image',
fieldTemplate: field_image_template,
fieldTemplate: FieldImageTemplate,
uploadButtonSelector: '.upload-button-input',
titleAdd: gettext('Upload an image'),
@@ -44,7 +45,7 @@
},
render: function() {
this.$el.html(this.template({
var attributes = {
id: this.options.valueAttribute,
inputName: (this.options.inputName || 'file'),
imageUrl: _.result(this, 'imageUrl'),
@@ -54,7 +55,8 @@
removeButtonIcon: _.result(this, 'iconRemove'),
removeButtonTitle: _.result(this, 'removeButtonTitle'),
screenReaderTitle: _.result(this, 'screenReaderTitle')
}));
};
this.$el.html(HtmlUtils.HTML(this.template(attributes)).toString());
this.delegateEvents();
this.updateButtonsVisibility();
this.watchForPageUnload();
@@ -184,14 +186,14 @@
showUploadInProgressMessage: function() {
this.$('.u-field-upload-button').addClass('in-progress');
this.$('.upload-button-icon').html(this.iconProgress);
this.$('.upload-button-title').html(this.titleUploading);
HtmlUtils.setHtml(this.$('.upload-button-icon'), HtmlUtils.HTML(this.iconProgress));
HtmlUtils.setHtml(this.$('.upload-button-title'), HtmlUtils.HTML(this.titleUploading));
},
showRemovalInProgressMessage: function() {
this.$('.u-field-remove-button').css('opacity', 1);
this.$('.remove-button-icon').html(this.iconProgress);
this.$('.remove-button-title').html(this.titleRemoving);
HtmlUtils.setHtml(this.$('.remove-button-icon'), HtmlUtils.HTML(this.iconProgress));
HtmlUtils.setHtml(this.$('.remove-button-title'), HtmlUtils.HTML(this.titleRemoving));
},
setCurrentStatus: function(status) {

View File

@@ -9,7 +9,7 @@
},
render: function() {
this.$el.html(this.template({
this.$el.html(this.template({ // xss-lint: disable=javascript-jquery-html
type: this.model.get('type'),
title: this.model.get('title'),
message: this.model.get('message'),

View File

@@ -0,0 +1,18 @@
<%page expression_filter="h"/>
<%inherit file="accomplishment-base.html" />
<%! from django.utils.translation import ugettext as _ %>
<div class="wrapper-content status status-invalid">
<div class="wrapper-content-grid">
<main class="content content-main">
<section class="">
<h2 class="title">${_("URL Not Supported")}</h2>
<div class="copy">
<p>${_("This link is no longer valid. But dont worry—this Verified Certificate is still valid and available to share. If this is your certificate, please visit your dashboard to get a new shareable link. If you're viewing someone else's certificate, please contact them to get an updated link.")}</p>
</div>
</section>
</main>
<aside role="complementary" class="content-secondary about" aria-label="${_('About {platform_name} Certificates').format(platform_name=platform_name)}">
</aside>
</div>
</div>

View File

@@ -1,4 +1,9 @@
<%! from django.utils.translation import ugettext as _ %>
<%page expression_filter="h"/>
<%!
from django.utils.translation import ugettext as _
from openedx.core.djangolib.markup import HTML, Text
%>
<%
split_test = context.get('split_test')
@@ -11,8 +16,8 @@ show_link = group_configuration_url is not None
<div class="xblock-message information">
<p>
<span class="message-text">
${_("This content experiment uses group configuration '{group_configuration_name}'.").format(
group_configuration_name="<a href='{}'>{}</a>".format(group_configuration_url, user_partition.name) if show_link else user_partition.name
${Text(_("This content experiment uses group configuration '{group_configuration_name}'.")).format(
group_configuration_name=Text(HTML("<a href='{}'>{}</a>")).format(group_configuration_url, user_partition.name) if show_link else user_partition.name
)}
</span>
</p>
@@ -23,13 +28,13 @@ show_link = group_configuration_url is not None
% if is_root:
<div class="wrapper-groups is-active">
<h3 class="sr">${_("Active Groups")}</h3>
${active_groups_preview}
${HTML(active_groups_preview)}
</div>
% if inactive_groups_preview:
<div class="wrapper-groups is-inactive">
<h3 class="title">${_("Inactive Groups")}</h3>
${inactive_groups_preview}
${HTML(inactive_groups_preview)}
</div>
% endif
% endif