${_("Certificates")}
- % if certificates is None: -- ${_("This module is not enabled.")} -
-- ${_("This course does not use a mode that offers certificates.")} -
-${_("Loading")}
-diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py index 46f8cebd140d..8b7e94618135 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py +++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py @@ -167,8 +167,7 @@ def get_use_new_certificates_page(self, obj): """ Method to get the use_new_certificates_page switch """ - course_key = self.get_course_key() - return toggles.use_new_certificates_page(course_key) + return True def get_use_new_textbooks_page(self, obj): """ diff --git a/cms/djangoapps/contentstore/toggles.py b/cms/djangoapps/contentstore/toggles.py index 30a030f966a9..a24a1371a768 100644 --- a/cms/djangoapps/contentstore/toggles.py +++ b/cms/djangoapps/contentstore/toggles.py @@ -346,24 +346,6 @@ def use_new_course_team_page(course_key): return not LEGACY_STUDIO_COURSE_TEAM.is_enabled(course_key) -# .. toggle_name: legacy_studio.certificates -# .. toggle_implementation: WaffleFlag -# .. toggle_default: False -# .. toggle_description: Temporarily fall back to the old Studio Course Certificates page. -# .. toggle_use_cases: temporary -# .. toggle_creation_date: 2025-03-14 -# .. toggle_target_removal_date: 2025-09-14 -# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275 -# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available. -LEGACY_STUDIO_CERTIFICATES = CourseWaffleFlag('legacy_studio.certificates', __name__) - - -def use_new_certificates_page(course_key): - """ - Returns a boolean if new studio certificates mfe is enabled - """ - return not LEGACY_STUDIO_CERTIFICATES.is_enabled(course_key) - # .. toggle_name: legacy_studio.configurations # .. toggle_implementation: WaffleFlag diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 35818125efab..2bde09938256 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -42,7 +42,6 @@ libraries_v2_enabled, split_library_view_on_dashboard, use_new_advanced_settings_page, - use_new_certificates_page, use_new_course_team_page, use_new_export_page, use_new_grading_page, @@ -470,13 +469,10 @@ def get_certificates_url(course_locator) -> str: """ Gets course authoring microfrontend URL for certificates page view. """ - certificates_url = None - if use_new_certificates_page(course_locator): - mfe_base_url = get_course_authoring_url(course_locator) - course_mfe_url = f'{mfe_base_url}/course/{course_locator}/certificates' - if mfe_base_url: - certificates_url = course_mfe_url - return certificates_url + mfe_base_url = get_course_authoring_url(course_locator) + if mfe_base_url: + return f'{mfe_base_url}/course/{course_locator}/certificates' + return '' def get_textbooks_url(course_locator) -> str: diff --git a/cms/djangoapps/contentstore/views/certificates.py b/cms/djangoapps/contentstore/views/certificates.py index 88f7d113aaa5..4dd1edc5b77f 100644 --- a/cms/djangoapps/contentstore/views/certificates.py +++ b/cms/djangoapps/contentstore/views/certificates.py @@ -41,7 +41,6 @@ from cms.djangoapps.contentstore.views.permissions import HasStudioWriteAccess from cms.djangoapps.contentstore.views.serializers import CertificateActivationSerializer, CertificateSerializer -from common.djangoapps.edxmako.shortcuts import render_to_response from common.djangoapps.student.auth import has_studio_write_access from common.djangoapps.student.roles import GlobalStaff from common.djangoapps.util.json_request import JsonResponse @@ -49,8 +48,7 @@ from xmodule.modulestore import EdxJSONEncoder # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order -from ..toggles import use_new_certificates_page -from ..utils import get_certificates_context, get_certificates_url, reverse_course_url +from ..utils import get_certificates_url, reverse_course_url from .certificate_manager import CertificateManager, CertificateValidationError CERTIFICATE_MINIMUM_ID = 100 @@ -143,10 +141,7 @@ def certificates_list_handler(request, course_key_string): return JsonResponse({"error": msg}, status=403) if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'): - if use_new_certificates_page(course_key): - return redirect(get_certificates_url(course_key)) - certificates_context = get_certificates_context(course, request.user) - return render_to_response('certificates.html', certificates_context) + return redirect(get_certificates_url(course_key)) elif "application/json" in request.META.get('HTTP_ACCEPT'): # Retrieve the list of certificates for the specified course if request.method == 'GET': diff --git a/cms/djangoapps/contentstore/views/tests/test_certificates.py b/cms/djangoapps/contentstore/views/tests/test_certificates.py index bd3f2b1b7e54..3201d09a0091 100644 --- a/cms/djangoapps/contentstore/views/tests/test_certificates.py +++ b/cms/djangoapps/contentstore/views/tests/test_certificates.py @@ -5,15 +5,12 @@ import itertools import json -from unittest import mock import ddt from django.conf import settings from django.test.utils import override_settings -from edx_toggles.toggles.testutils import override_waffle_flag from opaque_keys.edx.keys import AssetKey -from cms.djangoapps.contentstore import toggles from cms.djangoapps.contentstore.tests.utils import CourseTestCase from cms.djangoapps.contentstore.utils import get_lms_link_for_certificate_web_view, reverse_course_url from common.djangoapps.course_modes.tests.factories import CourseModeFactory @@ -277,11 +274,9 @@ def test_lms_link_for_certificate_web_view(self): ) self.assertEqual(link, test_url) # noqa: PT009 - @override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True) - @mock.patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True}) def test_certificate_info_in_response(self): """ - Test that certificate has been created and rendered properly with non-audit course mode. + Test that a created certificate is returned in the JSON GET response. """ CourseModeFactory.create(course_id=self.course.id, mode_slug='verified') response = self.client.ajax_post( @@ -291,12 +286,6 @@ def test_certificate_info_in_response(self): self.assertEqual(response.status_code, 201) # noqa: PT009 - # in html response - result = self.client.get_html(self._url()) - self.assertContains(result, 'Test certificate') - self.assertContains(result, 'Test description') - - # in JSON response response = self.client.get_json(self._url()) data = json.loads(response.content.decode('utf-8')) self.assertEqual(len(data), 1) # noqa: PT009 @@ -304,23 +293,6 @@ def test_certificate_info_in_response(self): self.assertEqual(data[0]['description'], 'Test description') # noqa: PT009 self.assertEqual(data[0]['version'], CERTIFICATE_SCHEMA_VERSION) # noqa: PT009 - @mock.patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True}) - @override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True) - def test_certificate_info_not_in_response(self): - """ - Test that certificate has not been rendered audit only course mode. - """ - response = self.client.ajax_post( - self._url(), - data=CERTIFICATE_JSON_WITH_SIGNATORIES - ) - - self.assertEqual(response.status_code, 201) # noqa: PT009 - - # in html response - result = self.client.get_html(self._url()) - self.assertNotContains(result, 'Test certificate') - def test_unsupported_http_accept_header(self): """ Test if not allowed header present in request. @@ -350,55 +322,6 @@ def test_not_permitted(self): ) self.assertContains(response, "error", status_code=403) - @override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True) - def test_audit_course_mode_is_skipped(self): - """ - Tests audit course mode is skipped when rendering certificates page. - """ - CourseModeFactory.create(course_id=self.course.id) - CourseModeFactory.create(course_id=self.course.id, mode_slug='verified') - response = self.client.get_html( - self._url(), - ) - self.assertEqual(response.status_code, 200) # noqa: PT009 - self.assertContains(response, 'verified') - self.assertNotContains(response, 'audit') - - @override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True) - def test_audit_only_disables_cert(self): - """ - Tests audit course mode is skipped when rendering certificates page. - """ - CourseModeFactory.create(course_id=self.course.id, mode_slug='audit') - response = self.client.get_html( - self._url(), - ) - self.assertEqual(response.status_code, 200) # noqa: PT009 - self.assertContains(response, 'This course does not use a mode that offers certificates.') - self.assertNotContains(response, 'This module is not enabled.') - self.assertNotContains(response, 'Loading') - - @ddt.data( - ['audit', 'verified'], - ['verified'], - ['audit', 'verified', 'credit'], - ['verified', 'credit'], - ['professional'] - ) - @override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True) - def test_non_audit_enables_cert(self, slugs): - """ - Tests audit course mode is skipped when rendering certificates page. - """ - for slug in slugs: - CourseModeFactory.create(course_id=self.course.id, mode_slug=slug) - response = self.client.get_html( - self._url(), - ) - self.assertEqual(response.status_code, 200) # noqa: PT009 - self.assertNotContains(response, 'This course does not use a mode that offers certificates.') - self.assertNotContains(response, 'This module is not enabled.') - self.assertContains(response, 'Loading') def test_assign_unique_identifier_to_certificates(self): """ diff --git a/cms/djangoapps/contentstore/views/tests/test_exam_settings_view.py b/cms/djangoapps/contentstore/views/tests/test_exam_settings_view.py index ef1d7f945dd7..e5d39d5b80a6 100644 --- a/cms/djangoapps/contentstore/views/tests/test_exam_settings_view.py +++ b/cms/djangoapps/contentstore/views/tests/test_exam_settings_view.py @@ -24,7 +24,6 @@ "ENABLE_PROCTORED_EXAMS": True, }, ) -@override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True) @override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True) @override_waffle_flag(toggles.LEGACY_STUDIO_CONFIGURATIONS, True) @override_waffle_flag(toggles.LEGACY_STUDIO_GRADING, True) @@ -51,7 +50,6 @@ def _get_exam_settings_alert_text(raw_html_content): @override_waffle_flag(toggles.LEGACY_STUDIO_EXAM_SETTINGS, True) @ddt.data( - "certificates_list_handler", "settings_handler", "group_configurations_list_handler", "grading_handler", @@ -68,7 +66,6 @@ def test_view_without_exam_settings_enabled(self, handler): self.assertNotContains(resp, 'Proctored Exam Settings') @ddt.data( - "certificates_list_handler", "settings_handler", "group_configurations_list_handler", "grading_handler", diff --git a/cms/static/cms/js/build.js b/cms/static/cms/js/build.js index 73089ed04a76..74c1d8dd6a0a 100644 --- a/cms/static/cms/js/build.js +++ b/cms/static/cms/js/build.js @@ -22,7 +22,6 @@ 'js/factories/course_create_rerun', 'js/factories/export', 'js/factories/group_configurations', - 'js/certificates/factories/certificates_page_factory', 'js/factories/index', 'js/factories/manage_users', 'js/factories/outline', diff --git a/cms/static/js/certificates/factories/certificates_page_factory.js b/cms/static/js/certificates/factories/certificates_page_factory.js deleted file mode 100644 index 923b8fea9699..000000000000 --- a/cms/static/js/certificates/factories/certificates_page_factory.js +++ /dev/null @@ -1,50 +0,0 @@ -// Backbone.js Page Object Factory: Certificates - -/** -Notes from Andy Armstrong: -The basic idea of a page factory is that it is a single RequireJS dependency that can be loaded in a template -to create a page object. This was added for the RequireJS Optimizer, which needs to have a single root to determine -statically all of the dependencies needed by a page. The RequireJS Optimizer combines these dependencies into a single -optimized JS file. Mako templates typically contain a block that constructs the page object using this page factory. -Unit tests for the page factory verify that it behaves as desired. Some of these factories are more complex than others. -The RequireJS Optimizer is only enabled in Studio at present, so the page factories aren't strictly required in the LMS. -We do intend to enable page factories on the LMS too. -*/ - -define([ - 'jquery', - 'js/certificates/collections/certificates', - 'js/certificates/models/certificate', - 'js/certificates/views/certificates_page', - 'js/certificates/views/certificate_preview' -], -function($, CertificatesCollection, Certificate, CertificatesPage, CertificatePreview) { - 'use strict'; - - return function(certificatesJson, certificateUrl, courseOutlineUrl, courseModes, certificateWebViewUrl, - isActive, certificateActivationHandlerUrl) { - // Initialize the model collection, passing any necessary options to the constructor - var certificatesCollection = new CertificatesCollection(certificatesJson, { - parse: true, - canBeEmpty: true, - certificateUrl: certificateUrl - }); - - // associating the certificate_preview globally. - // need to show / hide this view in some other places. - if (!window.certWebPreview && certificateWebViewUrl) { - window.certWebPreview = new CertificatePreview({ - course_modes: courseModes, - certificate_web_view_url: certificateWebViewUrl, - certificate_activation_handler_url: certificateActivationHandlerUrl, - is_active: isActive - }); - } - - // Execute the page object's rendering workflow - new CertificatesPage({ - el: $('#content'), - certificatesCollection: certificatesCollection - }).render(); - }; -}); diff --git a/cms/templates/certificates.html b/cms/templates/certificates.html deleted file mode 100644 index 400d300bf579..000000000000 --- a/cms/templates/certificates.html +++ /dev/null @@ -1,130 +0,0 @@ -<%page expression_filter="h"/> -<%inherit file="base.html" /> -<%def name="online_help_token()"><% return "certificates" %>%def> -<%namespace name='static' file='static_content.html'/> -<%! -from cms.djangoapps.contentstore import utils -from django.utils.translation import gettext as _ -from openedx.core.djangolib.markup import HTML, Text -from openedx.core.djangolib.js_utils import ( - dump_js_escaped_json, js_escaped_string -) -from six.moves.urllib.parse import quote -%> - -<%block name="title">${_("Course Certificates")}%block> -<%block name="bodyclass">is-signedin course view-certificates%block> - -<%block name="header_extras"> -% for template_name in ["certificate-details", "certificate-editor", "signatory-editor", "signatory-details", "basic-modal", "modal-button", "list", "upload-dialog", "certificate-web-preview", "signatory-actions"]: - -% endfor -%block> - -<%block name="jsextra"> - -%block> - -<%block name="requirejs"> -% if has_certificate_modes: - require(["js/certificates/factories/certificates_page_factory"], function(CertificatesPageFactory) { - CertificatesPageFactory( - ${certificates | n, dump_js_escaped_json}, - "${certificate_url | n, js_escaped_string}", - "${course_outline_url | n, js_escaped_string}", - ${course_modes | n, dump_js_escaped_json}, - ${certificate_web_view_url | n, dump_js_escaped_json}, - ${is_active | n, dump_js_escaped_json}, - ${certificate_activation_handler_url | n, dump_js_escaped_json} - ); - }); -% endif -%block> - -<%block name="content"> -
- ${_("This module is not enabled.")} -
-- ${_("This course does not use a mode that offers certificates.")} -
-${_("Loading")}
-