From 923f0498ee5f33d00ea0895e95600e2547a0e3d2 Mon Sep 17 00:00:00 2001 From: irfanuddinahmad Date: Thu, 23 Apr 2026 12:09:19 +0500 Subject: [PATCH 1/5] 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 8f66aad73f6d..ab0e1574515a 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 26612697137328ba29ab4667081a188f98618860 Mon Sep 17 00:00:00 2001 From: irfanuddinahmad Date: Tue, 28 Apr 2026 15:43:46 +0500 Subject: [PATCH 2/5] 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 e278e5846af9..cd40d327e326 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 eb8a4ea6fc11..fb47a63dd17f 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 e38d1533b5fb..1c95d30ebd01 100644 --- a/cms/djangoapps/contentstore/tests/tests.py +++ b/cms/djangoapps/contentstore/tests/tests.py @@ -237,20 +237,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 5e9052febf83..80cfedd85645 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 6fc73068298f..260992343fdd 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 baa69e35b3def173eecb5291c1016b5e4d66057e Mon Sep 17 00:00:00 2001 From: irfanuddinahmad Date: Thu, 30 Apr 2026 18:24:21 +0500 Subject: [PATCH 3/5] 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 7f5aa9673845..e634714cc46e 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")}