From f26e206b179efd13b29327295f1fca2123896064 Mon Sep 17 00:00:00 2001 From: irfanuddinahmad Date: Thu, 23 Apr 2026 12:09:19 +0500 Subject: [PATCH 1/3] feat!: Simplify import/export page redirects to always use MFE for courses. The import and export pages in Studio have been replaced with new views in the Authoring MFE for courses. The legacy condition guarded by the 'legacy_studio.import' and 'legacy_studio.export' waffle flags is removed. Courses now always redirect to the MFE; libraries continue to use the legacy HTML page as v2 libraries are not yet fully rolled out. This work is part of https://github.com/openedx/edx-platform/issues/36108 BREAKING CHANGE: The 'legacy_studio.import' and 'legacy_studio.export' waffle flags will no longer be respected for courses. The system will behave as if the flags are set to false permanently for courses. --- cms/djangoapps/contentstore/views/import_export.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cms/djangoapps/contentstore/views/import_export.py b/cms/djangoapps/contentstore/views/import_export.py index fd3b876d8625..3280e0b7b1f5 100644 --- a/cms/djangoapps/contentstore/views/import_export.py +++ b/cms/djangoapps/contentstore/views/import_export.py @@ -43,7 +43,6 @@ from ..storage import course_import_export_storage from ..tasks import CourseExportTask, CourseImportTask, export_olx, import_olx -from ..toggles import use_new_export_page, use_new_import_page from ..utils import IMPORTABLE_FILE_TYPES, get_export_url, get_import_url, reverse_course_url, reverse_library_url __all__ = [ @@ -99,7 +98,7 @@ def import_handler(request, course_key_string): return _write_chunk(request, courselike_key) elif request.method == 'GET': # assume html - if use_new_import_page(courselike_key) and not library: + if not library: return redirect(get_import_url(courselike_key)) status_url = reverse_course_url( "import_status_handler", courselike_key, kwargs={'filename': "fillerName"} @@ -358,7 +357,7 @@ def export_handler(request, course_key_string): export_olx.delay(request.user.id, course_key_string, request.LANGUAGE_CODE) return JsonResponse({'ExportStatus': 1}) elif 'text/html' in requested_format: - if use_new_export_page(course_key) and not library: + if not library: return redirect(get_export_url(course_key)) return render_to_response('export.html', context) else: From 984df84de5276452a1712c65fff71bc30d91bf20 Mon Sep 17 00:00:00 2001 From: irfanuddinahmad Date: Tue, 28 Apr 2026 15:43:46 +0500 Subject: [PATCH 2/3] fix: fixed tests --- .../contentstore/tests/test_contentstore.py | 4 ---- .../tests/test_course_settings.py | 4 ---- cms/djangoapps/contentstore/tests/tests.py | 9 ++++--- cms/djangoapps/contentstore/utils.py | 24 +++++++------------ .../views/tests/test_import_export.py | 11 --------- 5 files changed, 12 insertions(+), 40 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index 5c33e156721b..df8b0adbe0db 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -1487,10 +1487,6 @@ def test_get_json(handler): self.assertContains(resp, 'Chapter 2') # go to various pages - with override_waffle_flag(toggles.LEGACY_STUDIO_IMPORT, True): - test_get_html('import_handler') - with override_waffle_flag(toggles.LEGACY_STUDIO_EXPORT, True): - test_get_html('export_handler') with override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_TEAM, True): test_get_html('course_team_handler') with override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True): diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index 5d726627f27e..bc6eed4e1637 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -164,8 +164,6 @@ def test_discussion_fields_available(self, is_pages_and_resources_enabled, @ddt.data(False, True) @override_waffle_flag(toggles.LEGACY_STUDIO_ADVANCED_SETTINGS, True) - @override_waffle_flag(toggles.LEGACY_STUDIO_IMPORT, True) - @override_waffle_flag(toggles.LEGACY_STUDIO_EXPORT, True) @override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_TEAM, True) @override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True) @override_waffle_flag(toggles.LEGACY_STUDIO_GRADING, True) @@ -180,8 +178,6 @@ def test_disable_advanced_settings_feature(self, disable_advanced_settings): 'DISABLE_ADVANCED_SETTINGS': disable_advanced_settings, }): for handler in ( - 'import_handler', - 'export_handler', 'course_team_handler', 'settings_handler', 'grading_handler', diff --git a/cms/djangoapps/contentstore/tests/tests.py b/cms/djangoapps/contentstore/tests/tests.py index 23403da686ef..cdc4f6b917ae 100644 --- a/cms/djangoapps/contentstore/tests/tests.py +++ b/cms/djangoapps/contentstore/tests/tests.py @@ -264,20 +264,19 @@ def setUp(self): super().setUp() self.course = CourseFactory.create(org='edX', number='test_course_key', display_name='Test Course') - @data(('edX/test_course_key/Test_Course', 200), ('garbage:edX+test_course_key+Test_Course', 404)) + @data(('edX/test_course_key/Test_Course', 302, 200), ('garbage:edX+test_course_key+Test_Course', 404, 404)) @unpack - @override_waffle_flag(toggles.LEGACY_STUDIO_IMPORT, True) - def test_course_key_decorator(self, course_key, status_code): + def test_course_key_decorator(self, course_key, import_status_code, import_status_handler_code): """ Tests for the ensure_valid_course_key decorator. """ url = f'/import/{course_key}' resp = self.client.get_html(url) - self.assertEqual(resp.status_code, status_code) # noqa: PT009 + self.assertEqual(resp.status_code, import_status_code) # noqa: PT009 url = '/import_status/{course_key}/{filename}'.format( course_key=course_key, filename='xyz.tar.gz' ) resp = self.client.get_html(url) - self.assertEqual(resp.status_code, status_code) # noqa: PT009 + self.assertEqual(resp.status_code, import_status_handler_code) # noqa: PT009 diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 35818125efab..2226f25ca28d 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -44,10 +44,8 @@ use_new_advanced_settings_page, use_new_certificates_page, use_new_course_team_page, - use_new_export_page, use_new_grading_page, use_new_group_configurations_page, - use_new_import_page, use_new_schedule_details_page, use_new_unit_page, ) @@ -368,26 +366,20 @@ def get_import_url(course_locator) -> str: """ Gets course authoring microfrontend URL for import page view. """ - import_url = None - if use_new_import_page(course_locator): - mfe_base_url = get_course_authoring_url(course_locator) - course_mfe_url = f'{mfe_base_url}/course/{course_locator}/import' - if mfe_base_url: - import_url = course_mfe_url - return import_url + mfe_base_url = get_course_authoring_url(course_locator) + if mfe_base_url: + return f'{mfe_base_url}/course/{course_locator}/import' + return '' def get_export_url(course_locator) -> str: """ Gets course authoring microfrontend URL for export page view. """ - export_url = None - if use_new_export_page(course_locator): - mfe_base_url = get_course_authoring_url(course_locator) - course_mfe_url = f'{mfe_base_url}/course/{course_locator}/export' - if mfe_base_url: - export_url = course_mfe_url - return export_url + mfe_base_url = get_course_authoring_url(course_locator) + if mfe_base_url: + return f'{mfe_base_url}/course/{course_locator}/export' + return '' def get_optimizer_url(course_locator) -> str: diff --git a/cms/djangoapps/contentstore/views/tests/test_import_export.py b/cms/djangoapps/contentstore/views/tests/test_import_export.py index b363faa57174..9e5bc07bae04 100644 --- a/cms/djangoapps/contentstore/views/tests/test_import_export.py +++ b/cms/djangoapps/contentstore/views/tests/test_import_export.py @@ -23,7 +23,6 @@ from django.core.exceptions import SuspiciousOperation from django.core.files.storage import FileSystemStorage from django.test.utils import override_settings -from edx_toggles.toggles.testutils import override_waffle_flag from milestones.tests.utils import MilestonesTestCaseMixin from opaque_keys.edx.locator import LibraryLocator from openedx_authz.constants.roles import COURSE_DATA_RESEARCHER, COURSE_STAFF @@ -34,7 +33,6 @@ from user_tasks.models import UserTaskStatus from cms.djangoapps.contentstore import errors as import_error -from cms.djangoapps.contentstore import toggles from cms.djangoapps.contentstore.api.tests.base import BaseCourseViewTest from cms.djangoapps.contentstore.storage import course_import_export_storage from cms.djangoapps.contentstore.tests.test_libraries import LibraryTestCase @@ -754,15 +752,6 @@ def setUp(self): self.url = reverse_course_url('export_handler', self.course.id) self.status_url = reverse_course_url('export_status_handler', self.course.id) - @override_waffle_flag(toggles.LEGACY_STUDIO_EXPORT, True) - def test_export_html(self): - """ - Get the HTML for the page. - """ - resp = self.client.get_html(self.url) - self.assertEqual(resp.status_code, 200) # noqa: PT009 - self.assertContains(resp, "Export My Course Content") - def test_export_json_unsupported(self): """ JSON is unsupported. From ff4a8e940e77fa2f06fb431a4136e1dd41c7ab47 Mon Sep 17 00:00:00 2001 From: irfanuddinahmad Date: Thu, 30 Apr 2026 18:24:21 +0500 Subject: [PATCH 3/3] fix: fixed toggles --- .../v1/serializers/course_waffle_flags.py | 6 +-- cms/djangoapps/contentstore/toggles.py | 37 ------------------- cms/templates/widgets/header.html | 18 --------- 3 files changed, 2 insertions(+), 59 deletions(-) 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..6cafdc357322 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 @@ -102,15 +102,13 @@ def get_use_new_import_page(self, obj): """ Method to get the use_new_import_page switch """ - course_key = self.get_course_key() - return toggles.use_new_import_page(course_key) + return True def get_use_new_export_page(self, obj): """ Method to get the use_new_export_page switch """ - course_key = self.get_course_key() - return toggles.use_new_export_page(course_key) + return True def get_use_new_files_uploads_page(self, obj): """ diff --git a/cms/djangoapps/contentstore/toggles.py b/cms/djangoapps/contentstore/toggles.py index 30a030f966a9..a6ab31309c43 100644 --- a/cms/djangoapps/contentstore/toggles.py +++ b/cms/djangoapps/contentstore/toggles.py @@ -235,43 +235,6 @@ def use_new_grading_page(course_key): return not LEGACY_STUDIO_GRADING.is_enabled(course_key) -# .. toggle_name: legacy_studio.import -# .. toggle_implementation: WaffleFlag -# .. toggle_default: False -# .. toggle_description: Temporarily fall back to the old Course Import 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_IMPORT = CourseWaffleFlag('legacy_studio.import', __name__) - - -def use_new_import_page(course_key): - """ - Returns a boolean if new studio import mfe is enabled - """ - return not LEGACY_STUDIO_IMPORT.is_enabled(course_key) - - -# .. toggle_name: legacy_studio.export -# .. toggle_implementation: WaffleFlag -# .. toggle_default: False -# .. toggle_description: Temporarily fall back to the old Course Export 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_EXPORT = CourseWaffleFlag('legacy_studio.export', __name__) - - -def use_new_export_page(course_key): - """ - Returns a boolean if new studio export mfe is enabled - """ - return not LEGACY_STUDIO_EXPORT.is_enabled(course_key) - # .. toggle_name: contentstore.new_studio_mfe.use_new_video_uploads_page # .. toggle_implementation: CourseWaffleFlag diff --git a/cms/templates/widgets/header.html b/cms/templates/widgets/header.html index 4a8c7a142a86..a5c1465edc07 100644 --- a/cms/templates/widgets/header.html +++ b/cms/templates/widgets/header.html @@ -34,9 +34,7 @@

assets_url = reverse('assets_handler', kwargs={'course_key_string': str(course_key)}) textbooks_url = reverse('textbooks_list_handler', kwargs={'course_key_string': str(course_key)}) videos_url = reverse('videos_handler', kwargs={'course_key_string': str(course_key)}) - import_url = reverse('import_handler', kwargs={'course_key_string': str(course_key)}) course_info_url = reverse('course_info_handler', kwargs={'course_key_string': str(course_key)}) - export_url = reverse('export_handler', kwargs={'course_key_string': str(course_key)}) settings_url = reverse('settings_handler', kwargs={'course_key_string': str(course_key)}) grading_url = reverse('grading_handler', kwargs={'course_key_string': str(course_key)}) advanced_settings_url = reverse('advanced_settings_handler', kwargs={'course_key_string': str(course_key)}) @@ -50,8 +48,6 @@

grading_mfe_enabled = toggles.use_new_grading_page(context_course.id) course_team_mfe_enabled = toggles.use_new_course_team_page(context_course.id) advanced_settings_mfe_enabled = toggles.use_new_advanced_settings_page(context_course.id) - import_mfe_enabled = toggles.use_new_import_page(context_course.id) - export_mfe_enabled = toggles.use_new_export_page(context_course.id) optimizer_enabled = toggles.enable_course_optimizer(context_course.id) libraries_v2_enabled = toggles.libraries_v2_enabled() @@ -186,26 +182,12 @@

${_("Tools")}