refactor: centralize learning MFE URL-building logic (#26689)
In preparation for switching LMS/Studio over from serving legacy courseware URLs in certain places (for example, resume_course_url) to serving learning micro-frontend URLs. TNL-7796
This commit is contained in:
@@ -21,7 +21,6 @@ from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from lms.djangoapps.course_api.api import course_detail
|
||||
from lms.djangoapps.course_home_api.toggles import course_home_mfe_dates_tab_is_active
|
||||
from lms.djangoapps.course_home_api.utils import get_microfrontend_url
|
||||
from lms.djangoapps.courseware.access import has_access
|
||||
from lms.djangoapps.courseware.courses import get_course_with_access
|
||||
from lms.djangoapps.courseware.masquerade import is_masquerading, setup_masquerade
|
||||
@@ -29,6 +28,7 @@ from lms.djangoapps.courseware.masquerade import is_masquerading, setup_masquera
|
||||
from openedx.core.djangoapps.schedules.utils import reset_self_paced_schedule
|
||||
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
|
||||
from openedx.features.course_experience.api.v1.serializers import CourseDeadlinesMobileSerializer
|
||||
from openedx.features.course_experience.url_helpers import get_learning_mfe_home_url
|
||||
from openedx.features.course_experience.utils import dates_banner_should_display
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -91,7 +91,7 @@ def reset_course_deadlines(request):
|
||||
tracker.emit('edx.ui.lms.reset_deadlines.clicked', research_event_data)
|
||||
|
||||
if course_home_mfe_dates_tab_is_active(course_key):
|
||||
body_link = get_microfrontend_url(course_key=str(course_key), view_name='dates')
|
||||
body_link = get_learning_mfe_home_url(course_key=str(course_key), view_name='dates')
|
||||
else:
|
||||
body_link = '{}{}'.format(settings.LMS_ROOT_URL, reverse('dates', args=[str(course_key)]))
|
||||
|
||||
|
||||
121
openedx/features/course_experience/url_helpers.py
Normal file
121
openedx/features/course_experience/url_helpers.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""
|
||||
Helper functions for logic related to learning (courseare & course home) URLs.
|
||||
|
||||
Centralizdd in openedx/features/course_experience instead of lms/djangoapps/courseware
|
||||
because the Studio course outline may need these utilities.
|
||||
"""
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.search import navigation_index, path_to_location
|
||||
|
||||
|
||||
def get_legacy_courseware_url(course_key, usage_key, request=None):
|
||||
"""
|
||||
Return a str with the URL for the specified legacy (LMS-rendered) coursweare content.
|
||||
|
||||
Args:
|
||||
course_id(str): Course Id string
|
||||
usage_key(str): The location id of course component
|
||||
|
||||
Raises:
|
||||
ItemNotFoundError if no data at the location or NoPathToItem if location not in any class
|
||||
|
||||
Returns:
|
||||
Redirect url string
|
||||
"""
|
||||
|
||||
(
|
||||
course_key, chapter, section, vertical_unused,
|
||||
position, final_target_id
|
||||
) = path_to_location(modulestore(), usage_key, request)
|
||||
|
||||
# choose the appropriate view (and provide the necessary args) based on the
|
||||
# args provided by the redirect.
|
||||
# Rely on index to do all error handling and access control.
|
||||
if chapter is None:
|
||||
redirect_url = reverse('courseware', args=(six.text_type(course_key), ))
|
||||
elif section is None:
|
||||
redirect_url = reverse('courseware_chapter', args=(six.text_type(course_key), chapter))
|
||||
elif position is None:
|
||||
redirect_url = reverse(
|
||||
'courseware_section',
|
||||
args=(six.text_type(course_key), chapter, section)
|
||||
)
|
||||
else:
|
||||
# Here we use the navigation_index from the position returned from
|
||||
# path_to_location - we can only navigate to the topmost vertical at the
|
||||
# moment
|
||||
redirect_url = reverse(
|
||||
'courseware_position',
|
||||
args=(six.text_type(course_key), chapter, section, navigation_index(position))
|
||||
)
|
||||
redirect_url += "?{}".format(urlencode({'activate_block_id': six.text_type(final_target_id)}))
|
||||
return redirect_url
|
||||
|
||||
|
||||
def get_learning_mfe_courseware_url(course_key, sequence_key=None, unit_key=None):
|
||||
"""
|
||||
Return a str with the URL for the specified coursweare content in the Learning MFE.
|
||||
|
||||
The micro-frontend determines the user's position in the vertical via
|
||||
a separate API call, so all we need here is the course_key, section, and
|
||||
vertical IDs to format it's URL. For simplicity and performance reasons,
|
||||
this method does not inspect the modulestore to try to figure out what
|
||||
Unit/Vertical a sequence is in. If you try to pass in a unit_key without
|
||||
a sequence_key, the value will just be ignored and you'll get a URL pointing
|
||||
to just the course_key.
|
||||
|
||||
It is also capable of determining our section and vertical if they're not
|
||||
present. Fully specifying it all is preferable, though, as the
|
||||
micro-frontend can save itself some work, resulting in a better user
|
||||
experience.
|
||||
|
||||
We're building a URL like this:
|
||||
|
||||
http://localhost:2000/course/course-v1:edX+DemoX+Demo_Course/block-v1:edX+DemoX+Demo_Course+type@sequential+block@19a30717eff543078a5d94ae9d6c18a5/block-v1:edX+DemoX+Demo_Course+type@vertical+block@4a1bba2a403f40bca5ec245e945b0d76
|
||||
|
||||
`course_key`, `sequence_key`, and `unit_key` can be either OpaqueKeys or
|
||||
strings. They're only ever used to concatenate a URL string.
|
||||
"""
|
||||
mfe_link = '{}/course/{}'.format(settings.LEARNING_MICROFRONTEND_URL, course_key)
|
||||
|
||||
if sequence_key:
|
||||
mfe_link += '/{}'.format(sequence_key)
|
||||
|
||||
if unit_key:
|
||||
mfe_link += '/{}'.format(unit_key)
|
||||
|
||||
return mfe_link
|
||||
|
||||
|
||||
def get_learning_mfe_home_url(course_key, view_name=None):
|
||||
"""
|
||||
Given a course run key and view name, return the appropriate course home (MFE) URL.
|
||||
|
||||
We're building a URL like this:
|
||||
|
||||
http://localhost:2000/course/course-v1:edX+DemoX+Demo_Course/dates
|
||||
|
||||
`course_key` can be either an OpaqueKey or a string.
|
||||
`view_name` is an optional string.
|
||||
"""
|
||||
mfe_link = f'{settings.LEARNING_MICROFRONTEND_URL}/course/{course_key}'
|
||||
|
||||
if view_name:
|
||||
mfe_link += f'/{view_name}'
|
||||
|
||||
return mfe_link
|
||||
|
||||
|
||||
def is_request_from_learning_mfe(request):
|
||||
"""
|
||||
Returns whether the given request was made by the frontend-app-learning MFE.
|
||||
"""
|
||||
return (
|
||||
settings.LEARNING_MICROFRONTEND_URL and
|
||||
request.META.get('HTTP_REFERER', '').startswith(settings.LEARNING_MICROFRONTEND_URL)
|
||||
)
|
||||
@@ -15,7 +15,7 @@ from web_fragments.fragment import Fragment
|
||||
from lms.djangoapps.courseware.courses import get_course_date_blocks, get_course_with_access
|
||||
from lms.djangoapps.courseware.tabs import DatesTab
|
||||
from lms.djangoapps.course_home_api.toggles import course_home_mfe_dates_tab_is_active
|
||||
from lms.djangoapps.course_home_api.utils import get_microfrontend_url
|
||||
from openedx.features.course_experience.url_helpers import get_learning_mfe_home_url
|
||||
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class CourseDatesFragmentView(EdxFragmentView):
|
||||
|
||||
dates_tab_enabled = DatesTab.is_enabled(course, request.user)
|
||||
if course_home_mfe_dates_tab_is_active(course_key):
|
||||
dates_tab_link = get_microfrontend_url(course_key=course.id, view_name='dates')
|
||||
dates_tab_link = get_learning_mfe_home_url(course_key=course.id, view_name='dates')
|
||||
else:
|
||||
dates_tab_link = reverse('dates', args=[course.id])
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ from opaque_keys.edx.keys import CourseKey
|
||||
from web_fragments.fragment import Fragment
|
||||
|
||||
from lms.djangoapps.course_home_api.toggles import course_home_mfe_outline_tab_is_active
|
||||
from lms.djangoapps.course_home_api.utils import get_microfrontend_url
|
||||
from lms.djangoapps.courseware.access import has_access
|
||||
from lms.djangoapps.courseware.courses import can_self_enroll_in_course, get_course_info_section, get_course_with_access
|
||||
from lms.djangoapps.course_goals.api import (
|
||||
@@ -33,6 +32,7 @@ from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
|
||||
from openedx.core.djangoapps.util.maintenance_banner import add_maintenance_banner
|
||||
from openedx.features.course_duration_limits.access import generate_course_expired_fragment
|
||||
from openedx.features.course_experience.course_tools import CourseToolsPluginManager
|
||||
from openedx.features.course_experience.url_helpers import get_learning_mfe_home_url
|
||||
from openedx.features.discounts.utils import get_first_purchase_offer_banner_fragment
|
||||
from openedx.features.discounts.utils import format_strikeout_price
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
@@ -72,7 +72,7 @@ class CourseHomeView(CourseTabView):
|
||||
def render_to_fragment(self, request, course=None, tab=None, **kwargs): # lint-amnesty, pylint: disable=arguments-differ, unused-argument
|
||||
course_id = six.text_type(course.id)
|
||||
if course_home_mfe_outline_tab_is_active(course.id) and not request.user.is_staff:
|
||||
microfrontend_url = get_microfrontend_url(course_key=course_id, view_name="home")
|
||||
microfrontend_url = get_learning_mfe_home_url(course_key=course_id, view_name="home")
|
||||
raise Redirect(microfrontend_url)
|
||||
home_fragment_view = CourseHomeFragmentView()
|
||||
return home_fragment_view.render_to_fragment(request, course_id=course_id, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user