Merge pull request #14790 from edx/andya/course-redirects
Add data sharing consent redirect for more course tabs
This commit is contained in:
@@ -22,11 +22,11 @@ from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from course_modes.models import CourseMode, Mode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from openedx.core.djangoapps.embargo.test_utils import restrict_course
|
||||
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseServiceMockMixin
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from util.testing import UrlResetMixin
|
||||
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
|
||||
from util.tests.mixins.enterprise import EnterpriseServiceMockMixin
|
||||
from util.tests.mixins.discovery import CourseCatalogServiceMockMixin
|
||||
from util import organizations_helpers as organizations_api
|
||||
|
||||
|
||||
@@ -20,14 +20,13 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
from lms.djangoapps.commerce.utils import EcommerceService
|
||||
from openedx.core.djangoapps.api_admin.utils import course_discovery_api_client
|
||||
from course_modes.models import CourseMode
|
||||
from courseware.access import has_access
|
||||
from edxmako.shortcuts import render_to_response
|
||||
from openedx.core.djangoapps.embargo import api as embargo_api
|
||||
from openedx.features.enterprise_support import api as enterprise_api
|
||||
from student.models import CourseEnrollment
|
||||
from util.db import outer_atomic
|
||||
from util import enterprise_helpers as enterprise_api
|
||||
from util import organizations_helpers as organization_api
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ from course_modes.models import CourseMode
|
||||
from enrollment.views import EnrollmentUserThrottle
|
||||
from util.models import RateLimitConfiguration
|
||||
from util.testing import UrlResetMixin
|
||||
from util.tests.mixins.enterprise import EnterpriseServiceMockMixin
|
||||
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseServiceMockMixin
|
||||
from enrollment import api
|
||||
from enrollment.errors import CourseEnrollmentError
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
|
||||
@@ -20,13 +20,13 @@ from openedx.core.djangoapps.cors_csrf.authentication import SessionAuthenticati
|
||||
from openedx.core.djangoapps.cors_csrf.decorators import ensure_csrf_cookie_cross_domain
|
||||
from openedx.core.djangoapps.embargo import api as embargo_api
|
||||
from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in
|
||||
from openedx.features.enterprise_support.api import enterprise_enabled, EnterpriseApiClient, EnterpriseApiException
|
||||
from openedx.core.lib.api.authentication import (
|
||||
SessionAuthenticationAllowInactiveUser, OAuth2AuthenticationAllowInactiveUser,
|
||||
)
|
||||
from openedx.core.lib.api.permissions import ApiKeyHeaderPermission, ApiKeyHeaderPermissionIsAuthenticated
|
||||
from openedx.core.lib.exceptions import CourseNotFoundError
|
||||
from openedx.core.lib.log_utils import audit_log
|
||||
from util.enterprise_helpers import enterprise_enabled, EnterpriseApiClient, EnterpriseApiException
|
||||
from enrollment import api
|
||||
from enrollment.errors import (
|
||||
CourseEnrollmentError,
|
||||
|
||||
@@ -103,7 +103,6 @@ from util.milestones_helpers import (
|
||||
)
|
||||
|
||||
from util.password_policy_validators import validate_password_strength
|
||||
from util.enterprise_helpers import get_dashboard_consent_notification
|
||||
import third_party_auth
|
||||
from third_party_auth import pipeline, provider
|
||||
from student.helpers import (
|
||||
@@ -117,6 +116,7 @@ from student.models import anonymous_id_for_user, UserAttribute, EnrollStatusCha
|
||||
from shoppingcart.models import DonationConfiguration, CourseRegistrationCode
|
||||
|
||||
from openedx.core.djangoapps.embargo import api as embargo_api
|
||||
from openedx.features.enterprise_support.api import get_dashboard_consent_notification
|
||||
|
||||
import analytics
|
||||
from eventtracking import tracker
|
||||
|
||||
@@ -10,7 +10,7 @@ If true, it:
|
||||
b) calls apply_settings(), passing in the Django settings
|
||||
"""
|
||||
|
||||
from util.enterprise_helpers import insert_enterprise_pipeline_elements
|
||||
from openedx.features.enterprise_support.api import insert_enterprise_pipeline_elements
|
||||
|
||||
_FIELDS_STORED_IN_SESSION = ['auth_entry', 'next']
|
||||
_MIDDLEWARE_CLASSES = (
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
from third_party_auth import provider, settings
|
||||
from third_party_auth.tests import testutil
|
||||
from util.enterprise_helpers import enterprise_enabled
|
||||
import unittest
|
||||
|
||||
from openedx.features.enterprise_support.api import enterprise_enabled
|
||||
|
||||
|
||||
_ORIGINAL_AUTHENTICATION_BACKENDS = ('first_authentication_backend',)
|
||||
_ORIGINAL_INSTALLED_APPS = ('first_installed_app',)
|
||||
|
||||
@@ -10,7 +10,7 @@ from courseware.courses import get_course_with_access, get_course_overview_with_
|
||||
from courseware.access import has_access
|
||||
from student.models import CourseEnrollment
|
||||
from util.request import course_id_from_url
|
||||
from util.enterprise_helpers import get_enterprise_consent_url
|
||||
from openedx.features.enterprise_support.api import get_enterprise_consent_url
|
||||
|
||||
|
||||
class WikiAccessMiddleware(object):
|
||||
|
||||
@@ -2,7 +2,7 @@ from django.core.urlresolvers import reverse
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase
|
||||
from util.tests.mixins.enterprise import EnterpriseTestConsentRequired
|
||||
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ from courseware.courses import get_course_by_id
|
||||
from course_wiki.utils import course_wiki_slug
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from util.enterprise_helpers import data_sharing_consent_required
|
||||
from openedx.features.enterprise_support.api import data_sharing_consent_required
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase):
|
||||
resp = self.client.get(url)
|
||||
self.assertNotIn("You are not currently enrolled in this course", resp.content)
|
||||
|
||||
@mock.patch('courseware.views.views.get_enterprise_consent_url')
|
||||
@mock.patch('openedx.features.enterprise_support.api.get_enterprise_consent_url')
|
||||
def test_redirection_missing_enterprise_consent(self, mock_get_url):
|
||||
"""
|
||||
Verify that users viewing the course info who are enrolled, but have not provided
|
||||
|
||||
@@ -197,7 +197,7 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
)
|
||||
)
|
||||
|
||||
@patch('courseware.views.index.get_enterprise_consent_url')
|
||||
@patch('openedx.features.enterprise_support.api.get_enterprise_consent_url')
|
||||
def test_redirection_missing_enterprise_consent(self, mock_get_url):
|
||||
"""
|
||||
Verify that enrolled students are redirected to the Enterprise consent
|
||||
|
||||
@@ -41,21 +41,25 @@ from commerce.models import CommerceConfiguration
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from courseware.model_data import set_score
|
||||
from courseware.module_render import toc_for_course
|
||||
from courseware.testutils import RenderXBlockTestMixin
|
||||
from courseware.tests.factories import StudentModuleFactory, GlobalStaffFactory
|
||||
from courseware.url_helpers import get_redirect_url
|
||||
from courseware.user_state_client import DjangoXBlockUserStateClient
|
||||
from courseware.views.index import render_accordion
|
||||
from lms.djangoapps.commerce.utils import EcommerceService # pylint: disable=import-error
|
||||
from milestones.tests.utils import MilestonesTestCaseMixin
|
||||
from openedx.core.djangoapps.catalog.tests.factories import CourseFactory as CatalogCourseFactory
|
||||
from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory, CourseRunFactory
|
||||
from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.crawlers.models import CrawlersConfig
|
||||
from openedx.core.djangoapps.credit.api import set_credit_requirements
|
||||
from openedx.core.djangoapps.credit.models import CreditCourse, CreditProvider
|
||||
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
|
||||
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
|
||||
from openedx.core.lib.gating import api as gating_api
|
||||
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import AdminFactory, UserFactory, CourseEnrollmentFactory
|
||||
from util.tests.mixins.enterprise import EnterpriseTestConsentRequired
|
||||
from util.tests.test_date_utils import fake_ugettext, fake_pgettext
|
||||
from util.url import reload_django_url_config
|
||||
from util.views import ensure_valid_course_key
|
||||
@@ -64,12 +68,6 @@ from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.django_utils import TEST_DATA_MIXED_MODULESTORE
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls
|
||||
from openedx.core.djangoapps.catalog.tests.factories import CourseFactory as CatalogCourseFactory
|
||||
from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory, CourseRunFactory
|
||||
from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin
|
||||
from openedx.core.djangoapps.credit.api import set_credit_requirements
|
||||
from openedx.core.djangoapps.credit.models import CreditCourse, CreditProvider
|
||||
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
|
||||
|
||||
|
||||
@attr(shard=1)
|
||||
|
||||
@@ -32,13 +32,12 @@ from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
|
||||
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
|
||||
from openedx.core.djangoapps.crawlers.models import CrawlersConfig
|
||||
from openedx.core.djangoapps.monitoring_utils import set_custom_metrics_for_course_key
|
||||
from openedx.features.enterprise_support.api import data_sharing_consent_required
|
||||
from request_cache.middleware import RequestCache
|
||||
from shoppingcart.models import CourseRegistrationCode
|
||||
from student.models import CourseEnrollment
|
||||
from student.views import is_course_blocked
|
||||
from student.roles import GlobalStaff
|
||||
from survey.utils import must_answer_survey
|
||||
from util.enterprise_helpers import get_enterprise_consent_url
|
||||
from util.views import ensure_valid_course_key
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.x_module import STUDENT_VIEW
|
||||
@@ -73,6 +72,7 @@ class CoursewareIndex(View):
|
||||
@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)
|
||||
def get(self, request, course_id, chapter=None, section=None, position=None):
|
||||
"""
|
||||
Displays courseware accordion and associated content. If course, chapter,
|
||||
@@ -194,22 +194,6 @@ class CoursewareIndex(View):
|
||||
self._redirect_if_needed_to_register()
|
||||
self._redirect_if_needed_for_prereqs()
|
||||
self._redirect_if_needed_for_course_survey()
|
||||
self._redirect_if_data_sharing_consent_needed()
|
||||
|
||||
def _redirect_if_data_sharing_consent_needed(self):
|
||||
"""
|
||||
Determine if the user needs to provide data sharing consent before accessing
|
||||
the course, and redirect the user to provide consent if needed.
|
||||
"""
|
||||
course_id = unicode(self.course_key)
|
||||
consent_url = get_enterprise_consent_url(self.request, course_id, user=self.real_user, return_to='courseware')
|
||||
if consent_url:
|
||||
log.warning(
|
||||
u'User %s cannot access the course %s because they have not granted consent',
|
||||
self.real_user,
|
||||
course_id,
|
||||
)
|
||||
raise Redirect(consent_url)
|
||||
|
||||
def _redirect_if_needed_to_pay_for_course(self):
|
||||
"""
|
||||
|
||||
@@ -89,13 +89,13 @@ from openedx.core.djangoapps.programs.utils import ProgramMarketingDataExtender
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
|
||||
from openedx.core.djangoapps.monitoring_utils import set_custom_metrics_for_course_key
|
||||
from openedx.features.enterprise_support.api import data_sharing_consent_required
|
||||
from shoppingcart.utils import is_shopping_cart_enabled
|
||||
from student.models import UserTestGroup, CourseEnrollment
|
||||
from student.roles import GlobalStaff
|
||||
from util.cache import cache, cache_if_anonymous
|
||||
from util.date_utils import strftime_localized
|
||||
from util.db import outer_atomic
|
||||
from util.enterprise_helpers import get_enterprise_consent_url
|
||||
from util.milestones_helpers import get_prerequisite_courses_display
|
||||
from util.views import _record_feedback_in_zendesk
|
||||
from util.views import ensure_valid_course_key, ensure_valid_usage_key
|
||||
@@ -107,7 +107,6 @@ from ..entrance_exams import user_can_skip_entrance_exam
|
||||
from ..module_render import get_module_for_descriptor, get_module, get_module_by_usage_id
|
||||
|
||||
from web_fragments.fragment import Fragment
|
||||
from web_fragments.views import FragmentView
|
||||
|
||||
log = logging.getLogger("edx.courseware")
|
||||
|
||||
@@ -291,6 +290,7 @@ def jump_to(_request, course_id, location):
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@ensure_valid_course_key
|
||||
@data_sharing_consent_required
|
||||
def course_info(request, course_id):
|
||||
"""
|
||||
Display the course's info.html, or 404 if there is no such course.
|
||||
@@ -330,12 +330,6 @@ def course_info(request, course_id):
|
||||
# to access CCX redirect him to dashboard.
|
||||
return redirect(reverse('dashboard'))
|
||||
|
||||
# If the user is sponsored by an enterprise customer, and we still need to get data
|
||||
# sharing consent, redirect to do that first.
|
||||
consent_url = get_enterprise_consent_url(request, course_id, user=user, return_to='info')
|
||||
if consent_url:
|
||||
return redirect(consent_url)
|
||||
|
||||
# If the user needs to take an entrance exam to access this course, then we'll need
|
||||
# to send them to that specific course module before allowing them into other areas
|
||||
if not user_can_skip_entrance_exam(user, course):
|
||||
@@ -495,6 +489,7 @@ class CourseTabView(EdxFragmentView):
|
||||
"""
|
||||
@method_decorator(ensure_csrf_cookie)
|
||||
@method_decorator(ensure_valid_course_key)
|
||||
@method_decorator(data_sharing_consent_required)
|
||||
def get(self, request, course_id, tab_type, **kwargs):
|
||||
"""
|
||||
Displays a course tab page that contains a web fragment.
|
||||
@@ -811,6 +806,7 @@ def program_marketing(request, program_uuid):
|
||||
@login_required
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
@ensure_valid_course_key
|
||||
@data_sharing_consent_required
|
||||
def progress(request, course_id, student_id=None):
|
||||
""" Display the progress page. """
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
@@ -838,12 +834,6 @@ def _progress(request, course_key, student_id):
|
||||
course = get_course_with_access(request.user, 'load', course_key, depth=None, check_if_enrolled=True)
|
||||
prep_course_for_grading(course, request)
|
||||
|
||||
# If the user is sponsored by an enterprise customer, and we still need to get data
|
||||
# sharing consent, redirect to do that first.
|
||||
consent_url = get_enterprise_consent_url(request, unicode(course.id), return_to='progress')
|
||||
if consent_url:
|
||||
return redirect(consent_url)
|
||||
|
||||
# check to see if there is a required survey that must be taken before
|
||||
# the user can access the course.
|
||||
if survey.utils.must_answer_survey(course, request.user):
|
||||
|
||||
@@ -23,8 +23,8 @@ from django_comment_client.utils import strip_none
|
||||
from lms.djangoapps.discussion import views
|
||||
from student.tests.factories import UserFactory, CourseEnrollmentFactory
|
||||
from util.testing import UrlResetMixin
|
||||
from util.tests.mixins.enterprise import EnterpriseTestConsentRequired
|
||||
from openedx.core.djangoapps.util.testing import ContentGroupTestCase
|
||||
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.django_utils import (
|
||||
@@ -363,14 +363,12 @@ class SingleThreadQueryCountTestCase(ForumsEnableMixin, ModuleStoreTestCase):
|
||||
(ModuleStoreEnum.Type.split, False, 1, 3, 3, 12, 1),
|
||||
(ModuleStoreEnum.Type.split, False, 50, 3, 3, 12, 1),
|
||||
|
||||
# Enabling Enterprise integration increases the number of (cached and uncached) SQL queries by 1,
|
||||
# because the presence of the user's consent for the course must be checked.
|
||||
# But there should be no effect on the number of mongo queries made.
|
||||
(ModuleStoreEnum.Type.mongo, True, 1, 5, 3, 14, 2),
|
||||
(ModuleStoreEnum.Type.mongo, True, 50, 5, 3, 14, 2),
|
||||
# Enabling Enterprise integration should have no effect on the number of mongo queries made.
|
||||
(ModuleStoreEnum.Type.mongo, True, 1, 5, 3, 13, 1),
|
||||
(ModuleStoreEnum.Type.mongo, True, 50, 5, 3, 13, 1),
|
||||
# split mongo: 3 queries, regardless of thread response size.
|
||||
(ModuleStoreEnum.Type.split, True, 1, 3, 3, 13, 2),
|
||||
(ModuleStoreEnum.Type.split, True, 50, 3, 3, 13, 2),
|
||||
(ModuleStoreEnum.Type.split, True, 1, 3, 3, 12, 1),
|
||||
(ModuleStoreEnum.Type.split, True, 50, 3, 3, 12, 1),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_number_of_mongo_queries(
|
||||
@@ -1616,8 +1614,6 @@ class EnterpriseConsentTestCase(EnterpriseTestConsentRequired, ForumsEnableMixin
|
||||
for url in (
|
||||
reverse('discussion.views.forum_form_discussion',
|
||||
kwargs=dict(course_id=course_id)),
|
||||
reverse('discussion.views.inline_discussion',
|
||||
kwargs=dict(course_id=course_id, discussion_id=self.discussion_id)),
|
||||
reverse('discussion.views.single_thread',
|
||||
kwargs=dict(course_id=course_id, discussion_id=self.discussion_id, thread_id=thread_id)),
|
||||
):
|
||||
|
||||
@@ -53,7 +53,6 @@ from django_comment_client.utils import (
|
||||
)
|
||||
import django_comment_client.utils as utils
|
||||
import lms.lib.comment_client as cc
|
||||
from util.enterprise_helpers import data_sharing_consent_required
|
||||
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
@@ -199,7 +198,6 @@ def use_bulk_ops(view_func):
|
||||
|
||||
|
||||
@login_required
|
||||
@data_sharing_consent_required
|
||||
@use_bulk_ops
|
||||
def inline_discussion(request, course_key, discussion_id):
|
||||
"""
|
||||
@@ -236,7 +234,6 @@ def inline_discussion(request, course_key, discussion_id):
|
||||
|
||||
|
||||
@login_required
|
||||
@data_sharing_consent_required
|
||||
@use_bulk_ops
|
||||
def forum_form_discussion(request, course_key):
|
||||
"""
|
||||
@@ -277,7 +274,6 @@ def forum_form_discussion(request, course_key):
|
||||
|
||||
@require_GET
|
||||
@login_required
|
||||
@data_sharing_consent_required
|
||||
@use_bulk_ops
|
||||
def single_thread(request, course_key, discussion_id, thread_id):
|
||||
"""
|
||||
|
||||
@@ -34,6 +34,7 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
|
||||
from openedx.core.djangoapps.theming.helpers import is_request_in_themed_site
|
||||
from openedx.core.djangoapps.user_api.accounts.api import request_password_change
|
||||
from openedx.core.djangoapps.user_api.errors import UserNotFound
|
||||
from openedx.features.enterprise_support.api import set_enterprise_branding_filter_param
|
||||
from openedx.core.lib.time_zone_utils import TIME_ZONE_CHOICES
|
||||
from openedx.core.lib.edx_api_utils import get_edx_api_data
|
||||
from student.models import UserProfile
|
||||
@@ -47,7 +48,6 @@ from third_party_auth import pipeline
|
||||
from third_party_auth.decorators import xframe_allow_whitelisted
|
||||
from util.bad_request_rate_limiter import BadRequestRateLimiter
|
||||
from util.date_utils import strftime_localized
|
||||
from util.enterprise_helpers import set_enterprise_branding_filter_param
|
||||
|
||||
AUDIT_LOG = logging.getLogger("audit")
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -2180,6 +2180,7 @@ INSTALLED_APPS = (
|
||||
# Features
|
||||
'openedx.features.course_bookmarks',
|
||||
'openedx.features.course_experience',
|
||||
'openedx.features.enterprise_support',
|
||||
)
|
||||
|
||||
######################### CSRF #########################################
|
||||
|
||||
@@ -14,7 +14,7 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
from branding import api as branding_api
|
||||
# app that handles site status messages
|
||||
from status.status import get_site_status_msg
|
||||
from util.enterprise_helpers import get_enterprise_customer_logo_url
|
||||
from openedx.features.enterprise_support.api import get_enterprise_customer_logo_url
|
||||
%>
|
||||
|
||||
## Provide a hook for themes to inject branding on top.
|
||||
|
||||
@@ -11,13 +11,13 @@ from django.conf.urls.static import static
|
||||
from courseware.views.views import CourseTabView, EnrollStaffView, StaticCourseTabView
|
||||
from config_models.views import ConfigurationModelCurrentAPIView
|
||||
from courseware.views.index import CoursewareIndex
|
||||
from django_comment_common.models import ForumsConfig
|
||||
from openedx.core.djangoapps.auth_exchange.views import LoginWithAccessTokenView
|
||||
from openedx.core.djangoapps.catalog.models import CatalogIntegration
|
||||
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
|
||||
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
|
||||
from django_comment_common.models import ForumsConfig
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from util.enterprise_helpers import enterprise_enabled
|
||||
from openedx.features.enterprise_support.api import enterprise_enabled
|
||||
|
||||
# Uncomment the next two lines to enable the admin:
|
||||
if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'):
|
||||
|
||||
7
openedx/features/enterprise_support/README.rst
Normal file
7
openedx/features/enterprise_support/README.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
Enterprise Support
|
||||
------------------
|
||||
|
||||
This directory contains a Django application to support usage of
|
||||
enterprise features within edx-platform. The majority of the capabilities
|
||||
are provided through the external edx-enterprise library that can be found
|
||||
here: `https://github.com/edx/edx-enterprise`_.
|
||||
0
openedx/features/enterprise_support/__init__.py
Normal file
0
openedx/features/enterprise_support/__init__.py
Normal file
@@ -1,17 +1,21 @@
|
||||
"""
|
||||
Helpers to access the enterprise app
|
||||
APIs providing support for enterprise functionality.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from functools import wraps
|
||||
import hashlib
|
||||
import logging
|
||||
import six
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.cache import cache
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.shortcuts import redirect
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.translation import ugettext as _
|
||||
from edxmako.shortcuts import render_to_string
|
||||
from slumber.exceptions import HttpClientError, HttpServerError
|
||||
|
||||
from edx_rest_api_client.client import EdxRestApiClient
|
||||
try:
|
||||
from enterprise import utils as enterprise_utils
|
||||
@@ -25,9 +29,6 @@ from requests.exceptions import ConnectionError, Timeout
|
||||
from openedx.core.djangoapps.api_admin.utils import course_discovery_api_client
|
||||
|
||||
from openedx.core.lib.token_utils import JwtBuilder
|
||||
from slumber.exceptions import HttpClientError, HttpServerError
|
||||
import hashlib
|
||||
import six
|
||||
|
||||
|
||||
CONSENT_FAILED_PARAMETER = 'consent_failed'
|
||||
@@ -207,6 +208,12 @@ def data_sharing_consent_required(view_func):
|
||||
# Redirect to the consent URL, if consent is required.
|
||||
consent_url = get_enterprise_consent_url(request, course_id)
|
||||
if consent_url:
|
||||
real_user = getattr(request.user, 'real_user', request.user)
|
||||
LOGGER.warning(
|
||||
u'User %s cannot access the course %s because they have not granted consent',
|
||||
real_user,
|
||||
course_id,
|
||||
)
|
||||
return redirect(consent_url)
|
||||
|
||||
# Otherwise, drop through to wrapped view
|
||||
@@ -439,7 +446,7 @@ def get_dashboard_consent_notification(request, user, course_enrollments):
|
||||
)
|
||||
|
||||
return render_to_string(
|
||||
'util/enterprise_consent_declined_notification.html',
|
||||
'enterprise_support/enterprise_consent_declined_notification.html',
|
||||
{
|
||||
'title': title,
|
||||
'message': message,
|
||||
@@ -1,4 +1,7 @@
|
||||
## mako
|
||||
|
||||
<%page expression_filter="h"/>
|
||||
|
||||
<div class="wrapper-msg urgency-info">
|
||||
<div class="msg">
|
||||
<span class="msg-icon fa fa-info-circle" aria-hidden="true"></span>
|
||||
@@ -147,8 +147,8 @@ class EnterpriseTestConsentRequired(object):
|
||||
* url: URL to test
|
||||
* status_code: expected status code of URL when no data sharing consent is required.
|
||||
"""
|
||||
with mock.patch('util.enterprise_helpers.enterprise_enabled', return_value=True):
|
||||
with mock.patch('util.enterprise_helpers.consent_necessary_for_course') as mock_consent_necessary:
|
||||
with mock.patch('openedx.features.enterprise_support.api.enterprise_enabled', return_value=True):
|
||||
with mock.patch('openedx.features.enterprise_support.api.consent_necessary_for_course') as mock_consent_necessary: # pylint: disable=line-too-long
|
||||
# Ensure that when consent is necessary, the user is redirected to the consent page.
|
||||
mock_consent_necessary.return_value = True
|
||||
response = client.get(url)
|
||||
@@ -1,14 +1,14 @@
|
||||
"""
|
||||
Test the enterprise app helpers
|
||||
Test the enterprise support APIs.
|
||||
"""
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.test.utils import override_settings
|
||||
import mock
|
||||
|
||||
from util.enterprise_helpers import (
|
||||
from openedx.features.enterprise_support.api import (
|
||||
enterprise_enabled,
|
||||
insert_enterprise_pipeline_elements,
|
||||
data_sharing_consent_required,
|
||||
@@ -21,9 +21,9 @@ from util.enterprise_helpers import (
|
||||
|
||||
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
class TestEnterpriseHelpers(unittest.TestCase):
|
||||
class TestEnterpriseApi(unittest.TestCase):
|
||||
"""
|
||||
Test enterprise app helpers
|
||||
Test enterprise support APIs.
|
||||
"""
|
||||
|
||||
@override_settings(ENABLE_ENTERPRISE_INTEGRATION=False)
|
||||
@@ -101,7 +101,10 @@ class TestEnterpriseHelpers(unittest.TestCase):
|
||||
self.assertEqual(logo_url, None)
|
||||
|
||||
@override_settings(ENABLE_ENTERPRISE_INTEGRATION=True)
|
||||
@mock.patch('util.enterprise_helpers.get_enterprise_branding_filter_param', mock.Mock(return_value=None))
|
||||
@mock.patch(
|
||||
'openedx.features.enterprise_support.api.get_enterprise_branding_filter_param',
|
||||
mock.Mock(return_value=None)
|
||||
)
|
||||
def test_get_enterprise_customer_logo_url_return_none_when_param_missing(self):
|
||||
"""
|
||||
Test get_enterprise_customer_logo_url return 'None' when filter parameters are missing.
|
||||
@@ -142,8 +145,8 @@ class TestEnterpriseHelpers(unittest.TestCase):
|
||||
else:
|
||||
self.assertEqual(response, (args, kwargs))
|
||||
|
||||
@mock.patch('util.enterprise_helpers.enterprise_enabled')
|
||||
@mock.patch('util.enterprise_helpers.consent_necessary_for_course')
|
||||
@mock.patch('openedx.features.enterprise_support.api.enterprise_enabled')
|
||||
@mock.patch('openedx.features.enterprise_support.api.consent_necessary_for_course')
|
||||
def test_data_consent_required_enterprise_disabled(self,
|
||||
mock_consent_necessary,
|
||||
mock_enterprise_enabled):
|
||||
@@ -158,8 +161,8 @@ class TestEnterpriseHelpers(unittest.TestCase):
|
||||
mock_enterprise_enabled.assert_called_once()
|
||||
mock_consent_necessary.assert_not_called()
|
||||
|
||||
@mock.patch('util.enterprise_helpers.enterprise_enabled')
|
||||
@mock.patch('util.enterprise_helpers.consent_necessary_for_course')
|
||||
@mock.patch('openedx.features.enterprise_support.api.enterprise_enabled')
|
||||
@mock.patch('openedx.features.enterprise_support.api.consent_necessary_for_course')
|
||||
def test_no_course_data_consent_required(self,
|
||||
mock_consent_necessary,
|
||||
mock_enterprise_enabled):
|
||||
@@ -176,9 +179,9 @@ class TestEnterpriseHelpers(unittest.TestCase):
|
||||
mock_enterprise_enabled.assert_called_once()
|
||||
mock_consent_necessary.assert_called_once()
|
||||
|
||||
@mock.patch('util.enterprise_helpers.enterprise_enabled')
|
||||
@mock.patch('util.enterprise_helpers.consent_necessary_for_course')
|
||||
@mock.patch('util.enterprise_helpers.get_enterprise_consent_url')
|
||||
@mock.patch('openedx.features.enterprise_support.api.enterprise_enabled')
|
||||
@mock.patch('openedx.features.enterprise_support.api.consent_necessary_for_course')
|
||||
@mock.patch('openedx.features.enterprise_support.api.get_enterprise_consent_url')
|
||||
def test_data_consent_required(self, mock_get_consent_url, mock_consent_necessary, mock_enterprise_enabled):
|
||||
"""
|
||||
Verify that the wrapped function returns a redirect to the consent URL when enterprise integration is enabled,
|
||||
@@ -195,7 +198,7 @@ class TestEnterpriseHelpers(unittest.TestCase):
|
||||
mock_enterprise_enabled.assert_called_once()
|
||||
mock_consent_necessary.assert_called_once()
|
||||
|
||||
@mock.patch('util.enterprise_helpers.consent_needed_for_course')
|
||||
@mock.patch('openedx.features.enterprise_support.api.consent_needed_for_course')
|
||||
def test_get_enterprise_consent_url(self, needed_for_course_mock):
|
||||
"""
|
||||
Verify that get_enterprise_consent_url correctly builds URLs.
|
||||
@@ -264,7 +267,7 @@ class TestEnterpriseHelpers(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(notification_string, '')
|
||||
|
||||
@mock.patch('util.enterprise_helpers.EnterpriseCourseEnrollment')
|
||||
@mock.patch('openedx.features.enterprise_support.api.EnterpriseCourseEnrollment')
|
||||
def test_get_dashboard_consent_notification_no_contact_info(self, ece_mock):
|
||||
mock_get_ece = ece_mock.objects.get
|
||||
ece_mock.DoesNotExist = Exception
|
||||
@@ -300,7 +303,7 @@ class TestEnterpriseHelpers(unittest.TestCase):
|
||||
expected_header = 'Enrollment in edX Demo Course was not complete.'
|
||||
self.assertIn(expected_header, notification_string)
|
||||
|
||||
@mock.patch('util.enterprise_helpers.EnterpriseCourseEnrollment')
|
||||
@mock.patch('openedx.features.enterprise_support.api.EnterpriseCourseEnrollment')
|
||||
def test_get_dashboard_consent_notification_contact_info(self, ece_mock):
|
||||
mock_get_ece = ece_mock.objects.get
|
||||
ece_mock.DoesNotExist = Exception
|
||||
Reference in New Issue
Block a user