diff --git a/lms/djangoapps/course_wiki/middleware.py b/lms/djangoapps/course_wiki/middleware.py index 37bef0b74740..ac34081673b6 100644 --- a/lms/djangoapps/course_wiki/middleware.py +++ b/lms/djangoapps/course_wiki/middleware.py @@ -2,19 +2,19 @@ from urllib.parse import urlparse + from django.conf import settings from django.core.exceptions import PermissionDenied from django.http import Http404 from django.shortcuts import redirect from django.utils.deprecation import MiddlewareMixin +from openedx_filters.learning.filters import CoursewareViewRedirectURL from wiki.models import reverse +from common.djangoapps.student.models import CourseEnrollment from lms.djangoapps.courseware.access import has_access from lms.djangoapps.courseware.courses import get_course_overview_with_access, get_course_with_access from openedx.core.lib.request_utils import course_id_from_url -from openedx.features.enterprise_support.api import get_enterprise_consent_url -from common.djangoapps.student.models import CourseEnrollment - from xmodule.modulestore.django import modulestore @@ -96,10 +96,14 @@ def process_view(self, request, view_func, view_args, view_kwargs): # lint-amne # we'll redirect them to the course about page return redirect('about_course', str(course_id)) - # If we need enterprise data sharing consent for this course, then redirect to the form. - consent_url = get_enterprise_consent_url(request, str(course_id), source='WikiAccessMiddleware') - if consent_url: - return redirect(consent_url) + # If a plugin requires a redirect for this course, redirect now. + redirect_urls, _, _ = CoursewareViewRedirectURL.run_filter( + redirect_urls=[], + request=request, + course_key=course_id, + ) + if redirect_urls: + return redirect(redirect_urls[0]) # set the course onto here so that the wiki template can show the course navigation request.course = course diff --git a/lms/djangoapps/course_wiki/tests/tests.py b/lms/djangoapps/course_wiki/tests/tests.py index 7821f659d983..e7f88f84d20d 100644 --- a/lms/djangoapps/course_wiki/tests/tests.py +++ b/lms/djangoapps/course_wiki/tests/tests.py @@ -3,17 +3,16 @@ """ -from unittest.mock import patch +from unittest.mock import MagicMock, patch from django.urls import reverse from lms.djangoapps.courseware.tests.tests import LoginEnrollmentTestCase from openedx.features.course_experience.url_helpers import make_learning_mfe_courseware_url -from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order -class WikiRedirectTestCase(EnterpriseTestConsentRequired, LoginEnrollmentTestCase, ModuleStoreTestCase): +class WikiRedirectTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): """ Tests for wiki course redirection. """ @@ -202,27 +201,33 @@ def test_create_wiki_with_long_course_id(self): assert resp.status_code == 200 @patch.dict("django.conf.settings.FEATURES", {'ALLOW_WIKI_ROOT_ACCESS': True}) - @patch('openedx.features.enterprise_support.api.enterprise_customer_for_request') - def test_consent_required(self, mock_enterprise_customer_for_request): + @patch('openedx_filters.learning.filters.CoursewareViewRedirectURL.run_filter') + def test_consent_required(self, mock_run_filter): """ - Test that enterprise data sharing consent is required when enabled for the various courseware views. + Test that wiki views redirect when the CoursewareViewRedirectURL filter provides a URL. """ - # ENT-924: Temporary solution to replace sensitive SSO usernames. - mock_enterprise_customer_for_request.return_value = None + redirect_url = 'http://example.com/grant_consent' + mock_run_filter.return_value = ([redirect_url], MagicMock(), MagicMock()) # Public wikis can be accessed by non-enrolled users, and so direct access is not gated by the consent page course = CourseFactory.create() course.allow_public_wiki_access = False course.save() - # However, for private wikis, enrolled users must pass through the consent gate + # However, for private wikis, enrolled users must pass through the filter redirect gate # (Unenrolled users are redirected to course/about) course_id = str(course.id) self.login(self.student, self.password) self.enroll(course) - for (url, status_code) in ( - (reverse('course_wiki', kwargs={'course_id': course_id}), 302), - (f'/courses/{course_id}/wiki/', 200), - ): - self.verify_consent_required(self.client, url, status_code=status_code) # lint-amnesty, pylint: disable=no-value-for-parameter + # The course_wiki view is decorated with courseware_view_redirect which calls the filter + url = reverse('course_wiki', kwargs={'course_id': course_id}) + response = self.client.get(url) + self.assertEqual(response.status_code, 302) + self.assertEqual(response['Location'], redirect_url) + + # The wiki middleware (/courses/.../wiki/) also calls the filter + url = f'/courses/{course_id}/wiki/' + response = self.client.get(url) + self.assertEqual(response.status_code, 302) + self.assertEqual(response['Location'], redirect_url) diff --git a/lms/djangoapps/course_wiki/views.py b/lms/djangoapps/course_wiki/views.py index 955bf4e04268..f1f8a7e58118 100644 --- a/lms/djangoapps/course_wiki/views.py +++ b/lms/djangoapps/course_wiki/views.py @@ -14,10 +14,10 @@ from wiki.models import Article, URLPath from lms.djangoapps.course_wiki.utils import course_wiki_slug +from lms.djangoapps.courseware.decorators import courseware_view_redirect from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangolib.markup import Text from openedx.core.lib.courses import get_course_by_id -from openedx.features.enterprise_support.api import data_sharing_consent_required log = logging.getLogger(__name__) @@ -31,7 +31,7 @@ def root_create(request): return redirect('wiki:get', path=root.path) -@data_sharing_consent_required +@courseware_view_redirect def course_wiki_redirect(request, course_id, wiki_path=""): """ This redirects to whatever page on the wiki that the course designates diff --git a/lms/djangoapps/courseware/access_utils.py b/lms/djangoapps/courseware/access_utils.py index 4a9ec90e4a00..9fbe7a0002fb 100644 --- a/lms/djangoapps/courseware/access_utils.py +++ b/lms/djangoapps/courseware/access_utils.py @@ -8,7 +8,6 @@ from crum import get_current_request from django.conf import settings -from enterprise.models import EnterpriseCourseEnrollment, EnterpriseCustomerUser from pytz import UTC from common.djangoapps.student.models import CourseEnrollment @@ -20,13 +19,13 @@ EnrollmentRequiredAccessError, IncorrectActiveEnterpriseAccessError, StartDateEnterpriseLearnerError, - StartDateError, + StartDateError ) from lms.djangoapps.courseware.masquerade import get_course_masquerade, is_masquerading_as_student from openedx.features.course_experience import ( COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, COURSE_PRE_START_ACCESS_FLAG, - ENFORCE_MASQUERADE_START_DATES, + ENFORCE_MASQUERADE_START_DATES ) from xmodule.course_block import COURSE_VISIBILITY_PUBLIC # lint-amnesty, pylint: disable=wrong-import-order @@ -70,58 +69,22 @@ def adjust_start_date(user, days_early_for_beta, start, course_key): return start -def enterprise_learner_enrolled(request, user, course_key): +def _get_courseware_redirect_url(request, course_key): """ - Determine if the learner should be redirected to the enterprise learner portal by checking their enterprise - memberships/enrollments. If all of the following are true, then we are safe to redirect the learner: - - * The learner is linked to an enterprise customer, - * The enterprise customer has subsidized the learner's enrollment in the requested course, - * The enterprise customer has the learner portal enabled. - - NOTE: This function MUST be called from a view, or it will throw an exception. + Return the first courseware redirect URL provided by plugins, or None. Args: - request (django.http.HttpRequest): The current request being handled. Must not be None. - user (User): The requesting enter, potentially an enterprise learner. - course_key (str): The requested course to check for enrollment. + request (django.http.HttpRequest): The current request. + course_key: The course key for the view being accessed. Returns: - bool: True if the learner is enrolled via a linked enterprise customer and can safely be redirected to the - enterprise learner dashboard. + str or None: The first redirect URL returned by plugins, or None if no redirect is needed. """ - from openedx.features.enterprise_support.api import enterprise_customer_from_session_or_learner_data - - if not user.is_authenticated: - return False - - # enterprise_customer_data is either None (if learner is not linked to any customer) or a serialized - # EnterpriseCustomer representing the learner's active linked customer. - enterprise_customer_data = enterprise_customer_from_session_or_learner_data(request) - learner_portal_enabled = enterprise_customer_data and enterprise_customer_data["enable_learner_portal"] - if not learner_portal_enabled: - return False - - # Additionally make sure the enterprise learner is actually enrolled in the requested course, subsidized - # via the discovered customer. - enterprise_enrollments = EnterpriseCourseEnrollment.objects.filter( - course_id=course_key, - enterprise_customer_user__user_id=user.id, - enterprise_customer_user__enterprise_customer__uuid=enterprise_customer_data["uuid"], - ) - enterprise_enrollment_exists = enterprise_enrollments.exists() - log.info( - ( - "[enterprise_learner_enrolled] Checking for an enterprise enrollment for " - "lms_user_id=%s in course_key=%s via enterprise_customer_uuid=%s. " - "Exists: %s" - ), - user.id, - course_key, - enterprise_customer_data["uuid"], - enterprise_enrollment_exists, + from openedx_filters.learning.filters import CoursewareViewRedirectURL + redirect_urls, _, _ = CoursewareViewRedirectURL.run_filter( + redirect_urls=[], request=request, course_key=course_key ) - return enterprise_enrollment_exists + return redirect_urls[0] if redirect_urls else None def check_start_date(user, days_early_for_beta, start, course_key, display_error_to_user=True, now=None): @@ -155,10 +118,10 @@ def check_start_date(user, days_early_for_beta, start, course_key, display_error if should_grant_access: return ACCESS_GRANTED - # Before returning a StartDateError, determine if the learner should be redirected to the enterprise learner - # portal by returning StartDateEnterpriseLearnerError instead. + # Before returning a StartDateError, determine if a plugin requires a redirect (e.g. enterprise learner + # portal), and if so return StartDateEnterpriseLearnerError instead. request = get_current_request() - if request and enterprise_learner_enrolled(request, user, course_key): + if request and _get_courseware_redirect_url(request, course_key): return StartDateEnterpriseLearnerError(start, display_error_to_user=display_error_to_user) return StartDateError(start, display_error_to_user=display_error_to_user) @@ -232,22 +195,17 @@ def check_public_access(course, visibilities): def check_data_sharing_consent(course_id): """ - Grants access if the user is do not need DataSharing consent, otherwise returns data sharing link. + Grants access if no courseware redirect is pending for this course; otherwise returns an access error. Returns: AccessResponse: Either ACCESS_GRANTED or DataSharingConsentRequiredAccessError """ - from openedx.features.enterprise_support.api import get_enterprise_consent_url - - consent_url = get_enterprise_consent_url( - request=get_current_request(), - course_id=str(course_id), - return_to="courseware", - enrollment_exists=True, - source="CoursewareAccess", - ) - if consent_url: - return DataSharingConsentRequiredAccessError(consent_url=consent_url) + request = get_current_request() + if not request: + return ACCESS_GRANTED + redirect_url = _get_courseware_redirect_url(request, course_id) + if redirect_url: + return DataSharingConsentRequiredAccessError(consent_url=redirect_url) return ACCESS_GRANTED @@ -259,6 +217,7 @@ def check_correct_active_enterprise_customer(user, course_id): Returns: AccessResponse: Either ACCESS_GRANTED or IncorrectActiveEnterpriseAccessError """ + from enterprise.models import EnterpriseCourseEnrollment, EnterpriseCustomerUser enterprise_enrollments = EnterpriseCourseEnrollment.objects.filter( course_id=course_id, enterprise_customer_user__user_id=user.id ) diff --git a/lms/djangoapps/courseware/decorators.py b/lms/djangoapps/courseware/decorators.py new file mode 100644 index 000000000000..73c5716e6979 --- /dev/null +++ b/lms/djangoapps/courseware/decorators.py @@ -0,0 +1,55 @@ +""" +Decorators for courseware views. +""" +import functools + +from django.shortcuts import redirect +from opaque_keys.edx.keys import CourseKey +from openedx_filters.learning.filters import CoursewareViewRedirectURL + + +def courseware_view_redirect(view_func): + """ + Decorator that calls the CoursewareViewRedirectURL filter before rendering a courseware view. + + If any pipeline step returns a non-empty list of redirect URLs, the user is redirected + to the first URL in the list. Otherwise, the original view is rendered normally. + + Usage:: + + @courseware_view_redirect + def my_view(request, course_id, ...): + ... + + Works with both function-based views and ``method_decorator``-wrapped class-based views. + The decorator extracts the ``course_id`` or ``course_key`` from the view arguments. + """ + @functools.wraps(view_func) + def _wrapper(request_or_self, *args, **kwargs): + # Support both function views (request as first arg) and method views + # (self as first arg, request as second arg). + if hasattr(request_or_self, 'method'): + # Function-based view: first arg is request + request = request_or_self + else: + # Class-based view via method_decorator: first arg is self, second is request + request = args[0] if args else kwargs.get('request') + + course_id = kwargs.get('course_id') or (args[0] if args and not hasattr(request_or_self, 'method') else None) + try: + course_key = CourseKey.from_string(str(course_id)) if course_id else None + except Exception: # pylint: disable=broad-except + course_key = None + + if course_key is not None: + redirect_urls, _request, _course_key = CoursewareViewRedirectURL.run_filter( + redirect_urls=[], + request=request, + course_key=course_key, + ) + if redirect_urls: + return redirect(redirect_urls[0]) + + return view_func(request_or_self, *args, **kwargs) + + return _wrapper diff --git a/lms/djangoapps/courseware/tests/test_access.py b/lms/djangoapps/courseware/tests/test_access.py index 4e97e09c83f1..2e6b4955ee52 100644 --- a/lms/djangoapps/courseware/tests/test_access.py +++ b/lms/djangoapps/courseware/tests/test_access.py @@ -952,15 +952,15 @@ def test_course_catalog_access_num_queries_enterprise(self, user_attr_name, cour # read: CourseAccessRole + django_comment_client.Role num_queries = 2 else: - # read: CourseAccessRole + EnterpriseCourseEnrollment - num_queries = 2 + # read: CourseAccessRole + num_queries = 1 elif user_attr_name == 'user_normal': if course_attr_name == 'course_started': # read: CourseAccessRole + django_comment_client.Role + FBEEnrollmentExclusion + CourseMode num_queries = 4 else: - # read: CourseAccessRole + CourseEnrollmentAllowed + EnterpriseCourseEnrollment - num_queries = 3 + # read: CourseAccessRole + CourseEnrollmentAllowed + num_queries = 2 elif user_attr_name == 'user_anonymous': if course_attr_name == 'course_started': # read: CourseMode diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 1c0b6aa57f52..df381d908ef7 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -104,7 +104,6 @@ EnterpriseCustomerUserFactory, EnterpriseCustomerFactory ) -from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired from openedx.features.enterprise_support.api import add_enterprise_customer_to_session from enterprise.api.v1.serializers import EnterpriseCustomerSerializer @@ -2670,9 +2669,9 @@ def course_options(self): return options -class EnterpriseConsentTestCase(EnterpriseTestConsentRequired, ModuleStoreTestCase): +class EnterpriseConsentTestCase(ModuleStoreTestCase): """ - Ensure that the Enterprise Data Consent redirects are in place only when consent is required. + Ensure that courseware views redirect when the CoursewareViewRedirectURL filter provides a URL. """ def setUp(self): @@ -2683,20 +2682,22 @@ def setUp(self): CourseOverview.load_from_module_store(self.course.id) CourseEnrollmentFactory(user=self.user, course_id=self.course.id) - @patch('openedx.features.enterprise_support.api.enterprise_customer_for_request') - def test_consent_required(self, mock_enterprise_customer_for_request): + @patch('openedx_filters.learning.filters.CoursewareViewRedirectURL.run_filter') + def test_consent_required(self, mock_run_filter): """ - Test that enterprise data sharing consent is required when enabled for the various courseware views. + Test that courseware views redirect to the URL returned by the CoursewareViewRedirectURL filter. """ - # ENT-924: Temporary solution to replace sensitive SSO usernames. - mock_enterprise_customer_for_request.return_value = None + redirect_url = 'http://example.com/grant_consent' + mock_run_filter.return_value = ([redirect_url], MagicMock(), MagicMock()) course_id = str(self.course.id) for url in ( reverse("progress", kwargs=dict(course_id=course_id)), reverse("student_progress", kwargs=dict(course_id=course_id, student_id=str(self.user.id))), ): - self.verify_consent_required(self.client, url) # lint-amnesty, pylint: disable=no-value-for-parameter + response = self.client.get(url) + self.assertEqual(response.status_code, 302) + self.assertEqual(response['Location'], redirect_url) @ddt.ddt @@ -2766,7 +2767,13 @@ def test_is_course_open_for_learner( EnterpriseCourseEnrollmentFactory(enterprise_customer_user=enterprise_customer_user, course_id=course.id) set_current_request(request) - access_response = check_course_open_for_learner(staff_user, course) + if setup_enterprise_enrollment: + # Mock the filter to simulate an enterprise plugin redirecting the learner. + with patch('openedx_filters.learning.filters.CoursewareViewRedirectURL.run_filter') as mock_filter: + mock_filter.return_value = (['http://enterprise-portal.example.com/'], MagicMock(), MagicMock()) + access_response = check_course_open_for_learner(staff_user, course) + else: + access_response = check_course_open_for_learner(staff_user, course) assert bool(access_response) == expected_has_access assert access_response.error_code == expected_error_code diff --git a/lms/djangoapps/courseware/views/index.py b/lms/djangoapps/courseware/views/index.py index 1034674b7b41..95be4fe56c1b 100644 --- a/lms/djangoapps/courseware/views/index.py +++ b/lms/djangoapps/courseware/views/index.py @@ -9,21 +9,20 @@ from django.contrib.auth.views import redirect_to_login from django.utils.decorators import method_decorator +from django.utils.functional import cached_property from django.views.decorators.cache import cache_control from django.views.decorators.csrf import ensure_csrf_cookie -from django.utils.functional import cached_property from django.views.generic import View - from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey -from xmodule.modulestore.django import modulestore from common.djangoapps.util.views import ensure_valid_course_key +from lms.djangoapps.courseware.decorators import courseware_view_redirect from lms.djangoapps.courseware.exceptions import Redirect from lms.djangoapps.courseware.masquerade import setup_masquerade -from openedx.features.course_experience.url_helpers import make_learning_mfe_courseware_url from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG -from openedx.features.enterprise_support.api import data_sharing_consent_required +from openedx.features.course_experience.url_helpers import make_learning_mfe_courseware_url +from xmodule.modulestore.django import modulestore from ..block_render import get_block_for_descriptor from ..courses import get_course_with_access @@ -44,7 +43,7 @@ def enable_unenrolled_access(self): @method_decorator(ensure_csrf_cookie) @method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True)) @method_decorator(ensure_valid_course_key) - @method_decorator(data_sharing_consent_required) + @method_decorator(courseware_view_redirect) def get(self, request, course_id, section=None, subsection=None, position=None): """ Instead of loading the legacy courseware sequences pages, load the equivalent URL diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index 590a5f5acef4..888bc2f3c192 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -12,14 +12,15 @@ import nh3 import requests +from completion.waffle import ENABLE_COMPLETION_TRACKING_SWITCH from django.conf import settings from django.contrib.auth.decorators import login_required from django.contrib.auth.models import AnonymousUser, User # lint-amnesty, pylint: disable=imported-auth-user from django.core.exceptions import PermissionDenied from django.db import transaction from django.db.models import Q, prefetch_related_objects +from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, JsonResponse from django.shortcuts import redirect -from django.http import JsonResponse, Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden from django.template.context_processors import csrf from django.urls import reverse from django.utils.decorators import method_decorator @@ -34,37 +35,25 @@ from edx_django_utils.monitoring import set_custom_attribute, set_custom_attributes_for_course_key from edx_django_utils.plugins import pluggable_override from ipware.ip import get_client_ip -from xblock.core import XBlock - -from lms.djangoapps.static_template_view.views import render_500 from markupsafe import escape from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey from openedx_filters.learning.filters import CourseAboutRenderStarted, RenderXBlockStarted -from requests.exceptions import ConnectionError, Timeout # pylint: disable=redefined-builtin from pytz import UTC +from requests.exceptions import ConnectionError, Timeout # pylint: disable=redefined-builtin from rest_framework import status from rest_framework.decorators import api_view, throttle_classes +from rest_framework.fields import BooleanField from rest_framework.response import Response from rest_framework.throttling import UserRateThrottle -from rest_framework.fields import BooleanField from web_fragments.fragment import Fragment -from xmodule.course_block import ( - COURSE_VISIBILITY_PUBLIC, - COURSE_VISIBILITY_PUBLIC_OUTLINE, - CATALOG_VISIBILITY_CATALOG_AND_ABOUT, -) -from xmodule.modulestore import ModuleStoreEnum # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.modulestore.django import modulestore -from xmodule.modulestore.exceptions import ItemNotFoundError, NoPathToItem -from xmodule.tabs import CourseTabList -from xmodule.x_module import STUDENT_VIEW +from xblock.core import XBlock from common.djangoapps.course_modes.models import CourseMode, get_course_prices from common.djangoapps.edxmako.shortcuts import marketing_link, render_to_response, render_to_string from common.djangoapps.student import auth -from common.djangoapps.student.roles import CourseStaffRole from common.djangoapps.student.models import CourseEnrollment, UserTestGroup +from common.djangoapps.student.roles import CourseStaffRole from common.djangoapps.util.cache import cache, cache_if_anonymous from common.djangoapps.util.course import course_location_from_key, get_link_for_about_page from common.djangoapps.util.db import outer_atomic @@ -93,16 +82,13 @@ sort_by_start_date ) from lms.djangoapps.courseware.date_summary import verified_upgrade_deadline_link +from lms.djangoapps.courseware.decorators import courseware_view_redirect from lms.djangoapps.courseware.exceptions import CourseAccessRedirect, Redirect from lms.djangoapps.courseware.masquerade import is_masquerading_as_specific_student, setup_masquerade from lms.djangoapps.courseware.model_data import FieldDataCache from lms.djangoapps.courseware.models import BaseStudentModuleHistory, StudentModule from lms.djangoapps.courseware.permissions import MASQUERADE_AS_STUDENT, VIEW_COURSE_HOME, VIEW_COURSEWARE -from lms.djangoapps.courseware.toggles import ( - course_is_invitation_only, - courseware_mfe_search_is_enabled, -) -from completion.waffle import ENABLE_COMPLETION_TRACKING_SWITCH +from lms.djangoapps.courseware.toggles import course_is_invitation_only, courseware_mfe_search_is_enabled from lms.djangoapps.courseware.user_state_client import DjangoXBlockUserStateClient from lms.djangoapps.courseware.utils import ( _use_new_financial_assistance_flow, @@ -114,6 +100,7 @@ from lms.djangoapps.grades.api import CourseGradeFactory from lms.djangoapps.instructor.enrollment import uses_shib from lms.djangoapps.instructor.views.api import require_global_staff +from lms.djangoapps.static_template_view.views import render_500 from lms.djangoapps.survey import views as survey_views from lms.djangoapps.verify_student.services import IDVerificationService from openedx.core.djangoapps.catalog.utils import ( @@ -135,8 +122,8 @@ from openedx.core.djangoapps.programs.utils import ProgramMarketingDataExtender from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.util.user_messages import PageLevelMessages -from openedx.core.djangoapps.video_config.toggles import PUBLIC_VIDEO_SHARE from openedx.core.djangoapps.video_config.sharing import is_public_sharing_enabled +from openedx.core.djangoapps.video_config.toggles import PUBLIC_VIDEO_SHARE from openedx.core.djangoapps.zendesk_proxy.utils import create_zendesk_ticket from openedx.core.djangolib.markup import HTML, Text from openedx.core.lib.courses import get_course_by_id @@ -151,14 +138,20 @@ ) from openedx.features.course_experience.utils import dates_banner_should_display from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML -from openedx.features.enterprise_support.api import data_sharing_consent_required +from xmodule.course_block import ( + CATALOG_VISIBILITY_CATALOG_AND_ABOUT, + COURSE_VISIBILITY_PUBLIC, + COURSE_VISIBILITY_PUBLIC_OUTLINE +) +from xmodule.modulestore import ModuleStoreEnum # lint-amnesty, pylint: disable=wrong-import-order +from xmodule.modulestore.django import modulestore +from xmodule.modulestore.exceptions import ItemNotFoundError, NoPathToItem +from xmodule.tabs import CourseTabList +from xmodule.x_module import STUDENT_VIEW from ..block_render import get_block, get_block_by_usage_id, get_block_for_descriptor from ..tabs import _get_dynamic_tabs -from ..toggles import ( - COURSEWARE_OPTIMIZED_RENDER_XBLOCK, - ENABLE_COURSE_DISCOVERY_DEFAULT_LANGUAGE_FILTER, -) +from ..toggles import COURSEWARE_OPTIMIZED_RENDER_XBLOCK, ENABLE_COURSE_DISCOVERY_DEFAULT_LANGUAGE_FILTER log = logging.getLogger("edx.courseware") @@ -528,7 +521,7 @@ class CourseTabView(EdxFragmentView): """ @method_decorator(ensure_csrf_cookie) @method_decorator(ensure_valid_course_key) - @method_decorator(data_sharing_consent_required) + @method_decorator(courseware_view_redirect) def get(self, request, course_id, tab_type, **kwargs): # lint-amnesty, pylint: disable=arguments-differ """ Displays a course tab page that contains a web fragment. @@ -977,7 +970,7 @@ def dates(request, course_id): @login_required @cache_control(no_cache=True, no_store=True, must_revalidate=True) @ensure_valid_course_key -@data_sharing_consent_required +@courseware_view_redirect def progress(request, course_id, student_id=None): """ Display the progress page. """ course_key = CourseKey.from_string(course_id) diff --git a/lms/envs/common.py b/lms/envs/common.py index 917dd025e96f..f6e192c9d2da 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -3172,3 +3172,15 @@ def _should_send_certificate_events(settings): SSL_AUTH_DN_FORMAT_STRING = ( "/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client CA v1/CN={0}/emailAddress={1}" ) + +########################## OpenEdX Filters Configuration #################### + +OPEN_EDX_FILTERS_CONFIG = { + "org.openedx.learning.courseware.view.redirect_url.requested.v1": { + "fail_silently": True, + "pipeline": [ + "enterprise.filters.courseware.ConsentRedirectStep", + "enterprise.filters.courseware.LearnerPortalRedirectStep", + ], + }, +}