diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 879405a7396d..f4ec4ef0c44e 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -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=unicode(course_key), mode=mode ) diff --git a/cms/djangoapps/contentstore/views/certificates.py b/cms/djangoapps/contentstore/views/certificates.py index d2ed27ae50e0..17bb0fc74e4b 100644 --- a/cms/djangoapps/contentstore/views/certificates.py +++ b/cms/djangoapps/contentstore/views/certificates.py @@ -402,7 +402,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. ) diff --git a/cms/static/js/views/uploads.js b/cms/static/js/views/uploads.js index c71d5bf060af..2e4ec671e7bf 100644 --- a/cms/static/js/views/uploads.js +++ b/cms/static/js/views/uploads.js @@ -33,7 +33,7 @@ define(['jquery', 'underscore', 'gettext', 'js/views/modals/base_modal', 'edx-ui renderContents: function() { var isValid = this.model.isValid(), selectedFile = this.model.get('selectedFile'), - oldInput = this.$('input[type=file]').get(0); + oldInput = this.$('input[type=file]'); BaseModal.prototype.renderContents.call(this); // Ideally, we'd like to tell the browser to pre-populate the // with the selectedFile if we have one -- but @@ -44,7 +44,7 @@ define(['jquery', 'underscore', 'gettext', 'js/views/modals/base_modal', 'edx-ui // a blank input to prompt the user to upload a different (valid) file. if (selectedFile && isValid) { $(oldInput).removeClass('error'); - this.$('input[type=file]').replaceWith(HtmlUtils.ensureHtml(oldInput).toString()); + this.$('input[type=file]').replaceWith(HtmlUtils.HTML(oldInput).toString()); this.$('.action-upload').removeClass('disabled'); } else { this.$('.action-upload').addClass('disabled'); diff --git a/cms/templates/js/group-configuration-details.underscore b/cms/templates/js/group-configuration-details.underscore index 8310a2a657bf..f3fd77971827 100644 --- a/cms/templates/js/group-configuration-details.underscore +++ b/cms/templates/js/group-configuration-details.underscore @@ -31,9 +31,9 @@ <% if(showGroups) { %> <% allocation = Math.floor(100 / groups.length) %> -
    +
      <% groups.each(function(group, groupIndex) { %> -
    1. +
    2. <%- group.get('name') %> <%- allocation %>%
    3. diff --git a/cms/templates/js/maintenance/force-published-course-response.underscore b/cms/templates/js/maintenance/force-published-course-response.underscore index d77059ca5311..7441413dd47c 100644 --- a/cms/templates/js/maintenance/force-published-course-response.underscore +++ b/cms/templates/js/maintenance/force-published-course-response.underscore @@ -3,7 +3,7 @@ <%- gettext('You have done a dry run of force publishing the course. Nothing has changed. Had you run it, the following course versions would have been change.') %>
      - <%= StringUtils.interpolate( + <%- StringUtils.interpolate( gettext('The published branch version, {published}, was reset to the draft branch version, {draft}.'), { published: current_versions['published-branch'], diff --git a/cms/templates/js/mock/mock-xmodule-editor.underscore b/cms/templates/js/mock/mock-xmodule-editor.underscore index db0771e482a5..d06fcb49d88e 100644 --- a/cms/templates/js/mock/mock-xmodule-editor.underscore +++ b/cms/templates/js/mock/mock-xmodule-editor.underscore @@ -16,13 +16,13 @@ diff --git a/cms/templates/js/publish-editor.underscore b/cms/templates/js/publish-editor.underscore index 27c4ce290274..519564f72198 100644 --- a/cms/templates/js/publish-editor.underscore +++ b/cms/templates/js/publish-editor.underscore @@ -6,13 +6,13 @@ <% _.each(xblockInfo.get('child_info').children, function(subsection) { %> <% if (subsection.isPublishable()) { %>
    4. -

      <%= subsection.get('display_name') %>

      +

      <%- subsection.get('display_name') %>

        <% _.each(subsection.get('child_info').children, function(unit) { %> <% if (unit.isPublishable()) { %>
      1. - <%= unit.get('display_name') %> + <%- unit.get('display_name') %>
      2. <% } %> <% }); %> @@ -27,7 +27,7 @@ <% _.each(xblockInfo.get('child_info').children, function(unit) { %> <% if (unit.isPublishable()) { %>
      3. - <%= unit.get('display_name') %> + <%- unit.get('display_name') %>
      4. <% } %> <% }); %> diff --git a/cms/templates/js/unit-outline.underscore b/cms/templates/js/unit-outline.underscore index b5e48f72fd32..8ea1ddf1098a 100644 --- a/cms/templates/js/unit-outline.underscore +++ b/cms/templates/js/unit-outline.underscore @@ -1,23 +1,23 @@ <% if (parentInfo) { %> -
      5. -
        -

        - - <%- xblockInfo.get('display_name') %> +
      6. + <% } %> -
        -
          +
          +
          <% if (childType) { %> -
          - - <%= addChildLabel %> + <% } %> diff --git a/cms/templates/js/video/transcripts/file-upload.underscore b/cms/templates/js/video/transcripts/file-upload.underscore index 925b846c0180..fea4f5eec9e7 100644 --- a/cms/templates/js/video/transcripts/file-upload.underscore +++ b/cms/templates/js/video/transcripts/file-upload.underscore @@ -4,6 +4,6 @@
          - + accept="<%- _.map(ext, function(val){ return '.' + val; }).join(', ') %>"> +
          diff --git a/cms/templates/js/xblock-validation-messages.underscore b/cms/templates/js/xblock-validation-messages.underscore index fc1cc142bd89..c3f0bcc02301 100644 --- a/cms/templates/js/xblock-validation-messages.underscore +++ b/cms/templates/js/xblock-validation-messages.underscore @@ -3,7 +3,7 @@ var summaryMessage = validation.get("summary"); var aggregateMessageType = summaryMessage.type; var aggregateValidationClass = aggregateMessageType === "error"? "has-errors" : "has-warnings"; %> -
          +

          <%- summaryMessage.text %> <% if (summaryMessage.action_class) { %> @@ -25,7 +25,7 @@ var aggregateValidationClass = aggregateMessageType === "error"? "has-errors" : var messageType = message.type var messageTypeDisplayName = getDisplayName(messageType) %> -

        1. +
        2. <% if (messageTypeDisplayName) { %> <%- messageTypeDisplayName %>: diff --git a/lms/djangoapps/badges/events/course_complete.py b/lms/djangoapps/badges/events/course_complete.py index 38ff2f1782c5..b85b4102fa91 100644 --- a/lms/djangoapps/badges/events/course_complete.py +++ b/lms/djangoapps/badges/events/course_complete.py @@ -57,8 +57,12 @@ def evidence_url(user_id, course_key): Generates a URL to the user's Certificate HTML view, along with a GET variable that will signal the evidence visit event. """ + course_id = unicode(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': unicode(course_key)}) + '?evidence_visit=1' + 'certificates:render_cert_by_uuid', kwargs={'certificate_uuid': cert.verify_uuid}) + '?evidence_visit=1' def criteria(course_key): diff --git a/lms/djangoapps/certificates/api.py b/lms/djangoapps/certificates/api.py index e54501b2e0e7..79437c3db24b 100644 --- a/lms/djangoapps/certificates/api.py +++ b/lms/djangoapps/certificates/api.py @@ -416,13 +416,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": unicode(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): @@ -455,7 +455,7 @@ def get_certificate_url(user_id=None, course_id=None, uuid=None): 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) return url @@ -568,10 +568,11 @@ def emit_certificate_event(event_name, user, course_id, course=None, event_data= 'org_id': course.org, 'course_id': unicode(course_id) } + 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, uuid=event_data['certificate_id']) } event_data = event_data or {} event_data.update(data) diff --git a/lms/djangoapps/certificates/urls.py b/lms/djangoapps/certificates/urls.py index 9954a2c39d15..e0a08a12bd75 100644 --- a/lms/djangoapps/certificates/urls.py +++ b/lms/djangoapps/certificates/urls.py @@ -11,8 +11,14 @@ # Certificates HTML view end point to render web certs by user and course url( r'^user/(?P[^/]*)/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 diff --git a/lms/djangoapps/certificates/views/webview.py b/lms/djangoapps/certificates/views/webview.py index 0ef11992a657..c4fadfb3822e 100644 --- a/lms/djangoapps/certificates/views/webview.py +++ b/lms/djangoapps/certificates/views/webview.py @@ -9,7 +9,6 @@ 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 @@ -43,7 +42,6 @@ from openedx.core.djangoapps.catalog.utils import get_course_run_details from openedx.core.djangoapps.lang_pref.api import get_closest_released_language from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers -from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag, WaffleFlagNamespace from openedx.core.lib.courses import course_image_url from openedx.core.djangoapps.certificates.api import display_date_for_certificate, certificates_viewable_for_course from student.models import LinkedInAddToProfileConfiguration @@ -57,21 +55,6 @@ INVALID_CERTIFICATE_TEMPLATE_PATH = 'certificates/invalid.html' -WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name='certificates') -LOGIN_REQUIRED_FLAG = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'require_login') - - -def certs_login_required(view): - """ - Require login when the 'certificates.require_login' is enabled - """ - def wrap(request, user_id, course_id, *args, **kwargs): - course_key = CourseKey.from_string(course_id) - if LOGIN_REQUIRED_FLAG.is_enabled(course_key): - return login_required(view)(request, user_id, course_id, *args, **kwargs) - else: - return view(request, user_id, course_id, *args, **kwargs) - return wrap def get_certificate_description(mode, certificate_type, platform_name): @@ -481,6 +464,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( + 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, unicode(course_id)) + + def render_cert_by_uuid(request, certificate_uuid): """ This public view generates an HTML representation of the specified certificate @@ -490,7 +493,7 @@ def render_cert_by_uuid(request, certificate_uuid): verify_uuid=certificate_uuid, status=CertificateStatuses.downloadable ) - return render_html_view(request, certificate.user.id, unicode(certificate.course_id)) + return render_html_view(request, unicode(certificate.course_id), certificate) except GeneratedCertificate.DoesNotExist: raise Http404 @@ -499,17 +502,13 @@ def render_cert_by_uuid(request, certificate_uuid): template_path="certificates/server-error.html", test_func=lambda request: request.GET.get('preview', None) ) -@certs_login_required -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() @@ -521,16 +520,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 = ( - "Invalid cert: error finding course %s or user with id " - "%d. Specific error: %s" + "Invalid cert: error finding course %s " + "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(course_id, platform_name, configuration) # Kick the user back to the "Invalid" screen if the feature is disabled for the course @@ -677,10 +675,11 @@ def _get_custom_template_and_language(course_id, course_mode, course_language): return (None, None) -def _render_invalid_certificate(course_id, platform_name, configuration): +def _render_invalid_certificate(course_id, platform_name, configuration, + cert_path=INVALID_CERTIFICATE_TEMPLATE_PATH): context = {} _update_context_with_basic_info(context, course_id, platform_name, configuration) - 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): diff --git a/lms/templates/certificates/url_unsupported.html b/lms/templates/certificates/url_unsupported.html new file mode 100644 index 000000000000..f4f50b2f9c57 --- /dev/null +++ b/lms/templates/certificates/url_unsupported.html @@ -0,0 +1,18 @@ +<%page expression_filter="h"/> +<%inherit file="accomplishment-base.html" /> +<%! from django.utils.translation import gettext as _ %> + +
          +
          +
          +
          +

          ${_("URL Not Supported")}

          +
          +

          ${_("This link is no longer valid. But don’t 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.")}

          +
          +
          +
          + +
          +