AA-253: don't send outline if not enrolled
This is for the learning MFE outline endpoint. Also clean up some related permissions issues.
This commit is contained in:
@@ -2,12 +2,13 @@
|
||||
Tests for Outline Tab API in the Course Home API
|
||||
"""
|
||||
|
||||
import itertools
|
||||
from datetime import datetime
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from mock import patch
|
||||
from mock import Mock, patch
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests
|
||||
@@ -17,7 +18,7 @@ from openedx.core.djangoapps.user_api.tests.factories import UserCourseTagFactor
|
||||
from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, ENABLE_COURSE_GOALS
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.course_module import COURSE_VISIBILITY_PUBLIC
|
||||
from xmodule.course_module import COURSE_VISIBILITY_PUBLIC, COURSE_VISIBILITY_PUBLIC_OUTLINE
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
|
||||
@@ -108,29 +109,11 @@ class OutlineTabTestViews(BaseCourseHomeTests):
|
||||
|
||||
@COURSE_HOME_MICROFRONTEND.override(active=True)
|
||||
@COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True)
|
||||
@ddt.data(
|
||||
(True, True, True, True), # happy path
|
||||
(True, False, False, True), # is enrolled
|
||||
(False, True, False, True), # is staff
|
||||
(False, False, True, True), # public visibility
|
||||
(False, False, False, False), # no access
|
||||
)
|
||||
@ddt.unpack
|
||||
@COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.override()
|
||||
def test_handouts(self, is_enrolled, is_staff, is_public, handouts_visible):
|
||||
if is_enrolled:
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
if is_staff:
|
||||
self.user.is_staff = True
|
||||
self.user.save()
|
||||
if is_public:
|
||||
self.course.course_visibility = COURSE_VISIBILITY_PUBLIC
|
||||
self.course = self.update_course(self.course, self.user.id)
|
||||
|
||||
def test_handouts(self):
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
self.store.create_item(self.user.id, self.course.id, 'course_info', 'handouts', fields={'data': '<p>Hi</p>'})
|
||||
|
||||
handouts_html = self.client.get(self.url).data['handouts_html']
|
||||
self.assertEqual(handouts_html, '<p>Hi</p>' if handouts_visible else '')
|
||||
self.assertEqual(self.client.get(self.url).data['handouts_html'], '<p>Hi</p>')
|
||||
|
||||
@COURSE_HOME_MICROFRONTEND.override(active=True)
|
||||
@COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True)
|
||||
@@ -151,6 +134,7 @@ class OutlineTabTestViews(BaseCourseHomeTests):
|
||||
@COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True)
|
||||
@ddt.data(True, False)
|
||||
def test_welcome_message(self, welcome_message_is_dismissed):
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
self.store.create_item(
|
||||
self.user.id, self.course.id,
|
||||
'course_info',
|
||||
@@ -175,19 +159,17 @@ class OutlineTabTestViews(BaseCourseHomeTests):
|
||||
|
||||
@COURSE_HOME_MICROFRONTEND.override(active=True)
|
||||
@COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True)
|
||||
@patch('lms.djangoapps.course_home_api.outline.v1.views.generate_offer_html', new=Mock(return_value='<p>Offer</p>'))
|
||||
def test_offer_html(self):
|
||||
with patch('lms.djangoapps.course_home_api.outline.v1.views.generate_offer_html') as gen_html:
|
||||
html = '<div>Offer HTML</div>'
|
||||
gen_html.return_value = html
|
||||
self.assertEqual(self.client.get(self.url).data['offer_html'], html)
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
self.assertEqual(self.client.get(self.url).data['offer_html'], '<p>Offer</p>')
|
||||
|
||||
@COURSE_HOME_MICROFRONTEND.override(active=True)
|
||||
@COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override(active=True)
|
||||
@patch('lms.djangoapps.course_home_api.outline.v1.views.generate_course_expired_message', new=Mock(return_value='<p>Expired</p>'))
|
||||
def test_course_expired_html(self):
|
||||
with patch('lms.djangoapps.course_home_api.outline.v1.views.generate_course_expired_message') as gen_html:
|
||||
html = '<div>Course expired HTML</div>'
|
||||
gen_html.return_value = html
|
||||
self.assertEqual(self.client.get(self.url).data['course_expired_html'], html)
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
self.assertEqual(self.client.get(self.url).data['course_expired_html'], '<p>Expired</p>')
|
||||
|
||||
@ENABLE_COURSE_GOALS.override(active=True)
|
||||
@COURSE_HOME_MICROFRONTEND.override(active=True)
|
||||
@@ -284,3 +266,42 @@ class OutlineTabTestViews(BaseCourseHomeTests):
|
||||
ungraded_data = response.data['course_blocks']['blocks'][str(sequential2.location)]
|
||||
self.assertEqual(ungraded_data['display_name'], 'Ungraded')
|
||||
self.assertIsNone(ungraded_data['icon'])
|
||||
|
||||
@COURSE_HOME_MICROFRONTEND.override()
|
||||
@COURSE_HOME_MICROFRONTEND_OUTLINE_TAB.override()
|
||||
@COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.override()
|
||||
@patch('lms.djangoapps.course_home_api.outline.v1.views.generate_offer_html', new=Mock(return_value='<p>Offer</p>'))
|
||||
@patch('lms.djangoapps.course_home_api.outline.v1.views.generate_course_expired_message', new=Mock(return_value='<p>Expired</p>'))
|
||||
@ddt.data(*itertools.product([True, False], [True, False],
|
||||
[None, COURSE_VISIBILITY_PUBLIC, COURSE_VISIBILITY_PUBLIC_OUTLINE]))
|
||||
@ddt.unpack
|
||||
def test_visibility(self, is_enrolled, is_staff, course_visibility):
|
||||
if is_enrolled:
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
if is_staff:
|
||||
self.user.is_staff = True
|
||||
self.user.save()
|
||||
if course_visibility:
|
||||
self.course.course_visibility = course_visibility
|
||||
self.course = self.update_course(self.course, self.user.id)
|
||||
|
||||
self.store.create_item(self.user.id, self.course.id, 'course_info', 'handouts', fields={'data': '<p>Handouts</p>'})
|
||||
self.store.create_item(self.user.id, self.course.id, 'course_info', 'updates', fields={
|
||||
'items': [{
|
||||
'content': '<p>Welcome</p>',
|
||||
'status': 'visible',
|
||||
'date': 'July 23, 2020',
|
||||
'id': 1,
|
||||
}]
|
||||
})
|
||||
|
||||
show_enrolled = is_enrolled or is_staff
|
||||
is_public = course_visibility == COURSE_VISIBILITY_PUBLIC
|
||||
is_public_outline = course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE
|
||||
|
||||
data = self.client.get(self.url).data
|
||||
self.assertEqual(data['course_blocks'] is not None, show_enrolled or is_public or is_public_outline)
|
||||
self.assertEqual(data['handouts_html'] is not None, show_enrolled or is_public)
|
||||
self.assertEqual(data['offer_html'] is not None, show_enrolled)
|
||||
self.assertEqual(data['course_expired_html'] is not None, show_enrolled)
|
||||
self.assertEqual(data['resume_course']['url'] is not None, show_enrolled)
|
||||
|
||||
@@ -39,7 +39,7 @@ from openedx.features.course_experience.views.latest_update import LatestUpdateF
|
||||
from openedx.features.course_experience.views.welcome_message import PREFERENCE_KEY, WelcomeMessageFragmentView
|
||||
from openedx.features.discounts.utils import generate_offer_html
|
||||
from student.models import CourseEnrollment
|
||||
from xmodule.course_module import COURSE_VISIBILITY_PUBLIC
|
||||
from xmodule.course_module import COURSE_VISIBILITY_PUBLIC, COURSE_VISIBILITY_PUBLIC_OUTLINE
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
|
||||
@@ -150,22 +150,23 @@ class OutlineTabView(RetrieveAPIView):
|
||||
enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
|
||||
allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(course_key)
|
||||
allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
|
||||
allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE
|
||||
is_enrolled = enrollment and enrollment.is_active
|
||||
is_staff = has_access(request.user, 'staff', course_key)
|
||||
is_staff = bool(has_access(request.user, 'staff', course_key))
|
||||
show_enrolled = is_enrolled or is_staff
|
||||
|
||||
show_handouts = show_enrolled or allow_public
|
||||
handouts_html = get_course_info_section(request, request.user, course, 'handouts') if show_handouts else ''
|
||||
|
||||
# TODO: TNL-7185 Legacy: Refactor to return the offer & expired data and format the message in the MFE
|
||||
offer_html = generate_offer_html(request.user, course_overview)
|
||||
course_expired_html = generate_course_expired_message(request.user, course_overview)
|
||||
offer_html = show_enrolled and generate_offer_html(request.user, course_overview)
|
||||
course_expired_html = show_enrolled and generate_course_expired_message(request.user, course_overview)
|
||||
|
||||
welcome_message_html = None
|
||||
if get_course_tag(request.user, course_key, PREFERENCE_KEY) != 'False':
|
||||
if show_enrolled:
|
||||
if LATEST_UPDATE_FLAG.is_enabled(course_key):
|
||||
welcome_message_html = LatestUpdateFragmentView().latest_update_html(request, course)
|
||||
else:
|
||||
elif get_course_tag(request.user, course_key, PREFERENCE_KEY) != 'False':
|
||||
welcome_message_html = WelcomeMessageFragmentView().welcome_message_html(request, course)
|
||||
|
||||
enroll_alert = {
|
||||
@@ -191,25 +192,26 @@ class OutlineTabView(RetrieveAPIView):
|
||||
if course_home_mfe_dates_tab_is_active(course.id):
|
||||
dates_tab_link = get_microfrontend_url(course_key=course.id, view_name='dates')
|
||||
|
||||
course_blocks = get_course_outline_block_tree(request, course_key_string, request.user if is_enrolled else None)
|
||||
|
||||
has_visited_course = False
|
||||
try:
|
||||
resume_block = get_key_to_last_completed_block(request.user, course.id)
|
||||
has_visited_course = True
|
||||
except UnavailableCompletionData:
|
||||
resume_block = course_usage_key
|
||||
|
||||
resume_path = reverse('jump_to', kwargs={
|
||||
'course_id': course_key_string,
|
||||
'location': str(resume_block)
|
||||
})
|
||||
resume_course_url = request.build_absolute_uri(resume_path)
|
||||
course_blocks = None
|
||||
if show_enrolled or allow_public or allow_public_outline:
|
||||
outline_user = request.user if show_enrolled else None
|
||||
course_blocks = get_course_outline_block_tree(request, course_key_string, outline_user)
|
||||
|
||||
resume_course = {
|
||||
'has_visited_course': has_visited_course,
|
||||
'url': resume_course_url,
|
||||
'has_visited_course': False,
|
||||
'url': None,
|
||||
}
|
||||
if show_enrolled:
|
||||
try:
|
||||
resume_block = get_key_to_last_completed_block(request.user, course.id)
|
||||
resume_course['has_visited_course'] = True
|
||||
except UnavailableCompletionData:
|
||||
resume_block = course_usage_key
|
||||
resume_path = reverse('jump_to', kwargs={
|
||||
'course_id': course_key_string,
|
||||
'location': str(resume_block)
|
||||
})
|
||||
resume_course['url'] = request.build_absolute_uri(resume_path)
|
||||
|
||||
dates_widget = {
|
||||
'course_date_blocks': [block for block in date_blocks if not isinstance(block, TodaysDate)],
|
||||
@@ -241,15 +243,15 @@ class OutlineTabView(RetrieveAPIView):
|
||||
|
||||
data = {
|
||||
'course_blocks': course_blocks,
|
||||
'course_expired_html': course_expired_html,
|
||||
'course_expired_html': course_expired_html or None,
|
||||
'course_goals': course_goals,
|
||||
'course_tools': course_tools,
|
||||
'dates_widget': dates_widget,
|
||||
'enroll_alert': enroll_alert,
|
||||
'handouts_html': handouts_html,
|
||||
'offer_html': offer_html,
|
||||
'handouts_html': handouts_html or None,
|
||||
'offer_html': offer_html or None,
|
||||
'resume_course': resume_course,
|
||||
'welcome_message_html': welcome_message_html,
|
||||
'welcome_message_html': welcome_message_html or None,
|
||||
}
|
||||
context = self.get_serializer_context()
|
||||
context['course_key'] = course_key
|
||||
|
||||
@@ -42,16 +42,15 @@ def can_show_verified_upgrade(user, enrollment, course=None):
|
||||
Arguments:
|
||||
user (:class:`.AuthUser`): The user from the request.user property
|
||||
enrollment (:class:`.CourseEnrollment`): The enrollment under consideration.
|
||||
If None, then the enrollment is considered to be upgradeable.
|
||||
If None, then the enrollment is not considered to be upgradeable.
|
||||
course (:class:`.ModulestoreCourse`): Optional passed in modulestore course.
|
||||
If provided, it is expected to correspond to `enrollment.course.id`.
|
||||
If not provided, the course will be loaded from the modulestore.
|
||||
We use the course to retrieve user partitions when calculating whether
|
||||
the upgrade link will be shown.
|
||||
"""
|
||||
# Return `true` if user is not enrolled in course
|
||||
if enrollment is None:
|
||||
return False
|
||||
return False # this got accidentally flipped in 2017 (commit 8468357), but leaving alone to not switch again
|
||||
partition_service = PartitionService(enrollment.course.id, course=course)
|
||||
enrollment_track_partition = partition_service.get_user_partition(ENROLLMENT_TRACK_PARTITION_ID)
|
||||
group = partition_service.get_group(user, enrollment_track_partition)
|
||||
|
||||
@@ -34,6 +34,7 @@ from lms.djangoapps.courseware.toggles import REDIRECT_TO_COURSEWARE_MICROFRONTE
|
||||
from lms.djangoapps.courseware.utils import can_show_verified_upgrade
|
||||
from lms.djangoapps.courseware.utils import verified_upgrade_deadline_link
|
||||
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin
|
||||
from openedx.features.course_experience import DISPLAY_COURSE_SOCK_FLAG
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
from openedx.features.course_duration_limits.access import generate_course_expired_message
|
||||
from openedx.features.discounts.utils import generate_offer_html
|
||||
@@ -132,8 +133,8 @@ class CoursewareMeta:
|
||||
|
||||
@property
|
||||
def can_show_upgrade_sock(self):
|
||||
can_show = can_show_verified_upgrade(self.effective_user, self.enrollment_object)
|
||||
return can_show
|
||||
return (DISPLAY_COURSE_SOCK_FLAG.is_enabled(self.course_key) and
|
||||
can_show_verified_upgrade(self.effective_user, self.enrollment_object))
|
||||
|
||||
@property
|
||||
def license(self):
|
||||
|
||||
Reference in New Issue
Block a user