Skip to content

Commit 91010ab

Browse files
authored
Merge pull request #37454 from openedx/feanil/drop_course_home
feat!: Drop the legacy studio course home page
2 parents f7a1a9d + 1ca24ee commit 91010ab

11 files changed

Lines changed: 56 additions & 1287 deletions

File tree

cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,15 @@ def get_course_key(self):
4040

4141
def get_use_new_home_page(self, obj):
4242
"""
43-
Method to get the use_new_home_page switch
43+
Method to indicate whether we should use the new home page.
44+
45+
This used to be based on a waffle flag but the flag is being removed so we
46+
default it to true for now until we can remove the need for it from the consumers
47+
of this serializer and the related APIs.
48+
49+
See https://github.com/openedx/edx-platform/issues/37497
4450
"""
45-
return toggles.use_new_home_page()
51+
return True
4652

4753
def get_use_new_custom_pages(self, obj):
4854
"""

cms/djangoapps/contentstore/tests/test_contentstore.py

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
from uuid import uuid4
1717

1818
import ddt
19-
import lxml.html
2019
from django.conf import settings
2120
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
2221
from django.test import TestCase
2322
from django.test.utils import override_settings
23+
from django.urls import reverse
2424
from edx_toggles.toggles.testutils import override_waffle_switch, override_waffle_flag
2525
from edxval.api import create_video, get_videos_for_course
2626
from fs.osfs import OSFS
@@ -1388,17 +1388,6 @@ def assert_course_permission_denied(self):
13881388
resp = self.client.ajax_post('/course/', self.course_data)
13891389
self.assertEqual(resp.status_code, 403)
13901390

1391-
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
1392-
def test_course_index_view_with_no_courses(self):
1393-
"""Test viewing the index page with no courses"""
1394-
resp = self.client.get_html('/home/')
1395-
self.assertContains(
1396-
resp,
1397-
f'<h1 class="page-header">{settings.STUDIO_SHORT_NAME} Home</h1>',
1398-
status_code=200,
1399-
html=True
1400-
)
1401-
14021391
def test_course_factory(self):
14031392
"""Test that the course factory works correctly."""
14041393
course = CourseFactory.create()
@@ -1879,17 +1868,21 @@ def assertInCourseListing(self, course_key):
18791868
"""
18801869
Asserts that the given course key is NOT in the unsucceeded course action section of the html.
18811870
"""
1882-
with override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True):
1883-
course_listing = lxml.html.fromstring(self.client.get_html('/home/').content)
1884-
self.assertEqual(len(self.get_unsucceeded_course_action_elements(course_listing, course_key)), 0)
1871+
response = self.client.get(reverse('cms.djangoapps.contentstore:v2:courses'))
1872+
assert str(course_key) not in [
1873+
course["course_key"]
1874+
for course in response.json()["results"]["in_process_course_actions"]
1875+
]
18851876

18861877
def assertInUnsucceededCourseActions(self, course_key):
18871878
"""
18881879
Asserts that the given course key is in the unsucceeded course action section of the html.
18891880
"""
1890-
with override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True):
1891-
course_listing = lxml.html.fromstring(self.client.get_html('/home/').content)
1892-
self.assertEqual(len(self.get_unsucceeded_course_action_elements(course_listing, course_key)), 1)
1881+
response = self.client.get(reverse('cms.djangoapps.contentstore:v2:courses'))
1882+
assert str(course_key) in [
1883+
course["course_key"]
1884+
for course in response.json()["results"]["in_process_course_actions"]
1885+
]
18931886

18941887
def verify_rerun_course(self, source_course_key, destination_course_key, destination_display_name):
18951888
"""

cms/djangoapps/contentstore/tests/test_course_listing.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@
88

99
import ddt
1010
from ccx_keys.locator import CCXLocator
11-
from django.conf import settings
1211
from django.test import RequestFactory
13-
from edx_toggles.toggles.testutils import override_waffle_flag
1412
from opaque_keys.edx.locations import CourseLocator
1513

16-
from cms.djangoapps.contentstore import toggles
1714
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient
1815
from cms.djangoapps.contentstore.utils import delete_course
1916
from cms.djangoapps.contentstore.views.course import (
@@ -89,15 +86,6 @@ def tearDown(self):
8986
self.client.logout()
9087
ModuleStoreTestCase.tearDown(self) # pylint: disable=non-parent-method-called
9188

92-
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
93-
def test_empty_course_listing(self):
94-
"""
95-
Test on empty course listing, studio name is properly displayed
96-
"""
97-
message = f"Are you staff on an existing {settings.STUDIO_SHORT_NAME} course?"
98-
response = self.client.get('/home')
99-
self.assertContains(response, message)
100-
10189
def test_get_course_list(self):
10290
"""
10391
Test getting courses with new access group format e.g. 'instructor_edx.course.run'

cms/djangoapps/contentstore/tests/test_i18n.py

Lines changed: 1 addition & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
Tests for validate Internationalization and XBlock i18n service.
33
"""
44
import gettext
5-
from unittest import mock, skip
5+
from unittest import mock
66

77
from django.utils import translation
8-
from edx_toggles.toggles.testutils import override_waffle_flag
98

109
from django.utils.translation import get_language
1110
from xblock.core import XBlock
@@ -14,10 +13,7 @@
1413
from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory
1514
from xmodule.tests.test_export import PureXBlock
1615

17-
from cms.djangoapps.contentstore import toggles
18-
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient
1916
from cms.djangoapps.contentstore.views.preview import _prepare_runtime_for_preview
20-
from common.djangoapps.student.tests.factories import UserFactory
2117

2218

2319
class FakeTranslations(XBlockI18nService):
@@ -166,101 +162,3 @@ def test_i18n_service_callable(self):
166162
Test: i18n service should be callable in studio.
167163
"""
168164
self.assertTrue(callable(self.block.runtime._services.get('i18n'))) # pylint: disable=protected-access
169-
170-
171-
class InternationalizationTest(ModuleStoreTestCase):
172-
"""
173-
Tests to validate Internationalization.
174-
"""
175-
176-
CREATE_USER = False
177-
178-
def setUp(self):
179-
"""
180-
These tests need a user in the DB so that the django Test Client
181-
can log them in.
182-
They inherit from the ModuleStoreTestCase class so that the mongodb collection
183-
will be cleared out before each test case execution and deleted
184-
afterwards.
185-
"""
186-
super().setUp()
187-
188-
self.uname = 'testuser'
189-
self.email = '[email protected]'
190-
self.password = self.TEST_PASSWORD
191-
192-
# Create the use so we can log them in.
193-
self.user = UserFactory.create(username=self.uname, email=self.email, password=self.password)
194-
195-
# Note that we do not actually need to do anything
196-
# for registration if we directly mark them active.
197-
self.user.is_active = True
198-
# Staff has access to view all courses
199-
self.user.is_staff = True
200-
self.user.save()
201-
202-
self.course_data = {
203-
'org': 'MITx',
204-
'number': '999',
205-
'display_name': 'Robot Super Course',
206-
}
207-
208-
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
209-
def test_course_plain_english(self):
210-
"""Test viewing the index page with no courses"""
211-
self.client = AjaxEnabledTestClient() # lint-amnesty, pylint: disable=attribute-defined-outside-init
212-
self.client.login(username=self.uname, password=self.password)
213-
214-
resp = self.client.get_html('/home/')
215-
self.assertContains(resp,
216-
'<h1 class="page-header">𝓢𝓽𝓾𝓭𝓲𝓸 Home</h1>',
217-
status_code=200,
218-
html=True)
219-
220-
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
221-
def test_course_explicit_english(self):
222-
"""Test viewing the index page with no courses"""
223-
self.client = AjaxEnabledTestClient() # lint-amnesty, pylint: disable=attribute-defined-outside-init
224-
self.client.login(username=self.uname, password=self.password)
225-
226-
resp = self.client.get_html(
227-
'/home/',
228-
{},
229-
HTTP_ACCEPT_LANGUAGE='en',
230-
)
231-
232-
self.assertContains(resp,
233-
'<h1 class="page-header">𝓢𝓽𝓾𝓭𝓲𝓸 Home</h1>',
234-
status_code=200,
235-
html=True)
236-
237-
# ****
238-
# NOTE:
239-
# ****
240-
#
241-
# This test will break when we replace this fake 'test' language
242-
# with actual Esperanto. This test will need to be updated with
243-
# actual Esperanto at that time.
244-
# Test temporarily disable since it depends on creation of dummy strings
245-
@skip
246-
def test_course_with_accents(self):
247-
"""Test viewing the index page with no courses"""
248-
self.client = AjaxEnabledTestClient() # lint-amnesty, pylint: disable=attribute-defined-outside-init
249-
self.client.login(username=self.uname, password=self.password)
250-
251-
resp = self.client.get_html(
252-
'/home/',
253-
{},
254-
HTTP_ACCEPT_LANGUAGE='eo'
255-
)
256-
257-
TEST_STRING = (
258-
'<h1 class="title-1">'
259-
'My \xc7\xf6\xfcrs\xe9s L#'
260-
'</h1>'
261-
)
262-
263-
self.assertContains(resp,
264-
TEST_STRING,
265-
status_code=200,
266-
html=True)

cms/djangoapps/contentstore/tests/tests.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import datetime
1212
import time
1313
from unittest import mock
14-
from urllib.parse import quote_plus
14+
from urllib.parse import quote_plus, unquote
1515

1616
from ddt import data, ddt, unpack
1717
from django.conf import settings
@@ -24,6 +24,7 @@
2424
from cms.djangoapps.contentstore import toggles
2525
from cms.djangoapps.contentstore.tests.test_course_settings import CourseTestCase
2626
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient, parse_json, registration, user
27+
from cms.djangoapps.contentstore.utils import get_studio_home_url
2728
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
2829
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
2930

@@ -114,12 +115,6 @@ def setUp(self):
114115
# clear the cache so ratelimiting won't affect these tests
115116
cache.clear()
116117

117-
def check_page_get(self, url, expected):
118-
resp = self.client.get_html(url)
119-
self.assertEqual(resp.status_code, expected)
120-
return resp
121-
122-
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
123118
def test_private_pages_auth(self):
124119
"""Make sure pages that do require login work."""
125120
auth_pages = (
@@ -143,18 +138,21 @@ def test_private_pages_auth(self):
143138
print('Not logged in')
144139
for page in auth_pages:
145140
print(f"Checking '{page}'")
146-
self.check_page_get(page, expected=302)
141+
resp = self.client.get_html(page)
142+
assert resp.status_code == 302
143+
assert resp.url == unquote(reverse("login", query={"next": page}))
147144

148145
# Logged in should work.
149146
self.login(self.email, self.pw)
150147

151148
print('Logged in')
152149
for page in simple_auth_pages:
153150
print(f"Checking '{page}'")
154-
self.check_page_get(page, expected=200)
151+
resp = self.client.get_html(page)
152+
assert resp.status_code == 302
153+
assert resp.url == get_studio_home_url()
155154

156155
@override_settings(SESSION_INACTIVITY_TIMEOUT_IN_SECONDS=1)
157-
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
158156
def test_inactive_session_timeout(self):
159157
"""
160158
Verify that an inactive session times out and redirects to the
@@ -168,7 +166,8 @@ def test_inactive_session_timeout(self):
168166
# make sure we can access courseware immediately
169167
course_url = '/home/'
170168
resp = self.client.get_html(course_url)
171-
self.assertEqual(resp.status_code, 200)
169+
assert resp.status_code == 302
170+
assert resp.url == get_studio_home_url()
172171

173172
# then wait a bit and see if we get timed out
174173
time.sleep(2)

cms/djangoapps/contentstore/toggles.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -162,25 +162,6 @@ def individualize_anonymous_user_id(course_id):
162162
return INDIVIDUALIZE_ANONYMOUS_USER_ID.is_enabled(course_id)
163163

164164

165-
# .. toggle_name: legacy_studio.home
166-
# .. toggle_implementation: WaffleFlag
167-
# .. toggle_default: False
168-
# .. toggle_description: Temporarily fall back to the old Studio logged-in landing page.
169-
# .. toggle_use_cases: temporary
170-
# .. toggle_creation_date: 2025-03-14
171-
# .. toggle_target_removal_date: 2025-09-14
172-
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
173-
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
174-
LEGACY_STUDIO_HOME = WaffleFlag('legacy_studio.home', __name__)
175-
176-
177-
def use_new_home_page():
178-
"""
179-
Returns a boolean if new studio home page mfe is enabled
180-
"""
181-
return not LEGACY_STUDIO_HOME.is_enabled()
182-
183-
184165
# .. toggle_name: legacy_studio.custom_pages
185166
# .. toggle_implementation: WaffleFlag
186167
# .. toggle_default: False

cms/djangoapps/contentstore/utils.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from bs4 import BeautifulSoup
1717
from django.conf import settings
18-
from django.core.exceptions import ObjectDoesNotExist, ValidationError
18+
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, ValidationError
1919
from django.urls import reverse
2020
from django.utils import translation
2121
from django.utils.text import Truncator
@@ -50,7 +50,6 @@
5050
use_new_files_uploads_page,
5151
use_new_grading_page,
5252
use_new_group_configurations_page,
53-
use_new_home_page,
5453
use_new_import_page,
5554
use_new_schedule_details_page,
5655
use_new_textbooks_page,
@@ -298,12 +297,15 @@ def get_studio_home_url():
298297
"""
299298
Gets course authoring microfrontend URL for Studio Home view.
300299
"""
301-
studio_home_url = None
302-
if use_new_home_page():
303-
mfe_base_url = settings.COURSE_AUTHORING_MICROFRONTEND_URL
304-
if mfe_base_url:
305-
studio_home_url = f'{mfe_base_url}/home'
306-
return studio_home_url
300+
mfe_base_url = settings.COURSE_AUTHORING_MICROFRONTEND_URL
301+
if mfe_base_url:
302+
studio_home_url = f'{mfe_base_url}/home'
303+
return studio_home_url
304+
305+
raise ImproperlyConfigured(
306+
"The COURSE_AUTHORING_MICROFRONTEND_URL must be configured. "
307+
"Please set it to the base url for your authoring MFE."
308+
)
307309

308310

309311
def get_schedule_details_url(course_locator) -> str:

0 commit comments

Comments
 (0)