diff --git a/common/djangoapps/student/tests/test_filters.py b/common/djangoapps/student/tests/test_filters.py index bf79ed7ae402..881ba7fd9140 100644 --- a/common/djangoapps/student/tests/test_filters.py +++ b/common/djangoapps/student/tests/test_filters.py @@ -59,6 +59,20 @@ def run_filter(self, context, template_name): # pylint: disable=arguments-diffe } +class TestEnterpriseContextEnricherStep(PipelineStep): + """ + Utility pipeline step that simulates enterprise context enrichment via DashboardContextEnricher. + Verifies that 'request' is present in context and injects a sentinel enterprise key. + """ + + def run_filter(self, context, template_name): # pylint: disable=arguments-differ + """Inject a sentinel enterprise key to prove the pipeline ran.""" + assert 'request' in context, "'request' must be in dashboard context for enterprise enrichment" + context['enterprise_message'] = 'test-enterprise-message' + context['is_enterprise_user'] = True + return {"context": context, "template_name": template_name} + + class TestRenderInvalidDashboard(PipelineStep): """ Utility class used when getting steps for pipeline. @@ -464,3 +478,27 @@ def test_dashboard_render_without_filter_config(self): self.assertContains(response, self.first_course.id) self.assertContains(response, self.second_course.id) + + @override_settings( + OPEN_EDX_FILTERS_CONFIG={ + "org.openedx.learning.dashboard.render.started.v1": { + "pipeline": [ + "common.djangoapps.student.tests.test_filters.TestEnterpriseContextEnricherStep", + ], + "fail_silently": False, + }, + }, + ) + def test_dashboard_context_enricher_receives_request(self): + """ + Test that the context passed to DashboardRenderStarted includes 'request', allowing + pipeline steps like DashboardContextEnricher to access it for enterprise enrichment. + + Expected result: + - DashboardRenderStarted is triggered and executes TestEnterpriseContextEnricherStep. + - The step finds 'request' in context and injects enterprise sentinel values. + - The dashboard renders successfully (HTTP 200). + """ + response = self.client.get(self.dashboard_url) + + self.assertEqual(response.status_code, 200) diff --git a/common/djangoapps/student/views/dashboard.py b/common/djangoapps/student/views/dashboard.py index be1f32160911..df3b77f76d74 100644 --- a/common/djangoapps/student/views/dashboard.py +++ b/common/djangoapps/student/views/dashboard.py @@ -48,11 +48,6 @@ from openedx.core.djangolib.markup import HTML, Text from openedx.features.content_type_gating.models import ContentTypeGatingConfig from openedx.features.course_duration_limits.access import get_user_course_duration, get_user_course_expiration_date -from openedx.features.enterprise_support.api import ( - get_dashboard_consent_notification, - get_enterprise_learner_portal_context, -) -from openedx.features.enterprise_support.utils import is_enterprise_learner from common.djangoapps.student.api import COURSE_DASHBOARD_PLUGIN_VIEW_NAME from common.djangoapps.student.helpers import cert_info, check_verify_status_by_course, get_resume_urls_for_enrollments @@ -617,8 +612,6 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem link_end=HTML(""), ) - enterprise_message = get_dashboard_consent_notification(request, user, course_enrollments) - recovery_email_message = recovery_email_activation_message = None if is_secondary_email_feature_enabled(): try: @@ -799,7 +792,7 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem context = { 'urls': urls, 'programs_data': programs_data, - 'enterprise_message': enterprise_message, + 'request': request, 'consent_required_courses': consent_required_courses, 'enrollment_message': enrollment_message, 'redirect_message': Text(redirect_message), @@ -850,14 +843,8 @@ def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statem 'course_info': get_dashboard_course_info(user, course_enrollments), # TODO START: clean up as part of REVEM-199 (END) 'disable_unenrollment': disable_unenrollment, - # TODO: clean when experiment(Merchandise 2U LOBs - Dashboard) would be stop. [VAN-1097] - 'is_enterprise_user': is_enterprise_learner(user), } - # Include enterprise learner portal metadata and messaging - enterprise_learner_portal_context = get_enterprise_learner_portal_context(request) - context.update(enterprise_learner_portal_context) - context_from_plugins = get_plugins_view_context( ProjectType.LMS, COURSE_DASHBOARD_PLUGIN_VIEW_NAME, diff --git a/lms/envs/common.py b/lms/envs/common.py index e12dfbf3346e..e8898d4a602e 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -3717,3 +3717,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}" ) + +# .. setting_name: OPEN_EDX_FILTERS_CONFIG +# .. setting_default: {} +# .. setting_description: Configuration dict for openedx-filters pipeline steps. +# Keys are filter type strings; values are dicts with 'fail_silently' (bool) and +# 'pipeline' (list of dotted-path strings to PipelineStep subclasses). +OPEN_EDX_FILTERS_CONFIG = { + "org.openedx.learning.dashboard.render.started.v1": { + "fail_silently": True, + "pipeline": [], + }, +} diff --git a/lms/envs/production.py b/lms/envs/production.py index aeccaf0c0fbf..093644e3bc08 100644 --- a/lms/envs/production.py +++ b/lms/envs/production.py @@ -519,6 +519,19 @@ def get_env_setting(setting): _YAML_TOKENS.get('EVENT_BUS_PRODUCER_CONFIG', {}) ) +# Merge OPEN_EDX_FILTERS_CONFIG from YAML into the default defined in common.py. +# Pipeline steps from YAML are appended after steps defined in common.py. +# The fail_silently value from YAML takes precedence over the one in common.py. +for _filter_type, _filter_config in _YAML_TOKENS.get('OPEN_EDX_FILTERS_CONFIG', {}).items(): + if _filter_type in OPEN_EDX_FILTERS_CONFIG: + OPEN_EDX_FILTERS_CONFIG[_filter_type]['pipeline'].extend( + _filter_config.get('pipeline', []) + ) + if 'fail_silently' in _filter_config: + OPEN_EDX_FILTERS_CONFIG[_filter_type]['fail_silently'] = _filter_config['fail_silently'] + else: + OPEN_EDX_FILTERS_CONFIG[_filter_type] = _filter_config + ####################################################################################################################### # HEY! Don't add anything to the end of this file. # Add your defaults to common.py instead! diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index a68e01106690..09cc3bd5841f 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -831,7 +831,7 @@ openedx-events==10.5.0 # edx-name-affirmation # event-tracking # ora2 -openedx-filters==2.1.0 +openedx-filters==3.3.0 # via # -r requirements/edx/kernel.in # edx-enterprise diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index d5ea01b7c82f..9be42e86fe3f 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -1380,7 +1380,7 @@ openedx-events==10.5.0 # edx-name-affirmation # event-tracking # ora2 -openedx-filters==2.1.0 +openedx-filters==3.3.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index cc8236cb83a5..9a1e45c7d095 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -1005,7 +1005,7 @@ openedx-events==10.5.0 # edx-name-affirmation # event-tracking # ora2 -openedx-filters==2.1.0 +openedx-filters==3.3.0 # via # -r requirements/edx/base.txt # edx-enterprise diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 75f2fb63d442..040ecf353e77 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -1050,7 +1050,7 @@ openedx-events==10.5.0 # edx-name-affirmation # event-tracking # ora2 -openedx-filters==2.1.0 +openedx-filters==3.3.0 # via # -r requirements/edx/base.txt # edx-enterprise