Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lms/djangoapps/course_home_api/outline/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,8 @@ def completions_dict(self):
Dictionary keys are block keys and values are int values
representing the completion status of the block.
"""
if self.request.user.is_anonymous:
return {}
course_key_string = self.kwargs.get('course_key_string')
course_key = CourseKey.from_string(course_key_string)
completions = BlockCompletion.objects.filter(user=self.request.user, context_key=course_key).values_list(
Expand Down
61 changes: 55 additions & 6 deletions lms/djangoapps/courseware/access.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
ACCESS_DENIED,
ACCESS_GRANTED,
check_course_open_for_learner,
check_public_access,
check_start_date,
debug,
)
Expand All @@ -41,6 +42,7 @@
from lms.djangoapps.mobile_api.models import IgnoreMobileAvailableFlagConfig
from lms.djangoapps.courseware.toggles import course_is_invitation_only
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.learning_sequences.models import CourseContext, LearningContext
from openedx.features.course_duration_limits.access import check_course_expired
from common.djangoapps.student import auth
from common.djangoapps.student.models import CourseEnrollmentAllowed
Expand All @@ -61,7 +63,12 @@
get_pre_requisite_courses_not_completed,
is_prerequisite_courses_enabled
)
from xmodule.course_block import CATALOG_VISIBILITY_ABOUT, CATALOG_VISIBILITY_CATALOG_AND_ABOUT, CourseBlock # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.course_block import ( # lint-amnesty, pylint: disable=wrong-import-order
CATALOG_VISIBILITY_ABOUT,
CATALOG_VISIBILITY_CATALOG_AND_ABOUT,
COURSE_VISIBILITY_PUBLIC,
CourseBlock,
)
from xmodule.error_block import ErrorBlock # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.partitions.partitions import NoSuchUserPartitionError, NoSuchUserPartitionGroupError # lint-amnesty, pylint: disable=wrong-import-order

Expand Down Expand Up @@ -364,6 +371,10 @@ def can_load():
else:
return visible_to_nonstaff

# Anonymous users on public courses can load (so course_metadata returns has_access and MFE doesn't redirect).
if not user.is_authenticated and check_public_access(courselike, [COURSE_VISIBILITY_PUBLIC]):
return ACCESS_GRANTED

open_for_learner = check_course_open_for_learner(user, courselike)
if not open_for_learner:
staff_access = _has_staff_access_to_block(user, courselike, courselike.id)
Expand Down Expand Up @@ -581,6 +592,19 @@ def can_load():
students to see blocks. If not, views should check the course, so we
don't have to hit the enrollments table on every block load.
"""
# If the user has staff access, they can load the block and checks below are not needed.
staff_access_response = _has_staff_access_to_block(user, block, course_key)
if staff_access_response:
return staff_access_response

# Anonymous users: allow block load when course has public (unenrolled) access,
# so that Learning MFE and sequence API can serve public_view without requiring login.
if not user.is_authenticated and course_key:
course_like = _get_course_like_for_public_access(course_key)
if course_like and check_public_access(course_like, [COURSE_VISIBILITY_PUBLIC]):
# Only visibility check; skip start date so sequence metadata matches outline API.
return _visible_to_nonstaff_users(block, display_error_to_user=False)

# If the user (or the role the user is currently masquerading as) does not have
# access to this content, then deny access. The problem with calling _has_staff_access_to_block
# before this method is that _has_staff_access_to_block short-circuits and returns True
Expand All @@ -589,11 +613,6 @@ def can_load():
if not group_access_response:
return group_access_response

# If the user has staff access, they can load the block and checks below are not needed.
staff_access_response = _has_staff_access_to_block(user, block, course_key)
if staff_access_response:
return staff_access_response

return (
_visible_to_nonstaff_users(block, display_error_to_user=False) and
(
Expand Down Expand Up @@ -693,6 +712,36 @@ def check_support():

##### Internal helper methods below

def _get_course_like_for_public_access(course_key):
"""
Return an object with .id and .course_visibility for check_public_access.
Prefer learning_sequences.CourseContext (same source as outline API).
"""
try:
course_context = (
LearningContext.objects
.select_related('course_context')
.get(context_key=course_key)
.course_context
)
return type('CourseLike', (), {
'id': course_key,
'course_visibility': course_context.course_visibility,
})()
except (LearningContext.DoesNotExist, CourseContext.DoesNotExist):
pass
try:
overview = CourseOverview.get_from_id(course_key)
if overview is not None:
return type('CourseLike', (), {
'id': course_key,
'course_visibility': overview.course_visibility,
})()
except (CourseOverview.DoesNotExist, IOError):
pass
return None


def _dispatch(table, action, user, obj):
"""
Helper: call table[action], raising a nice pretty error if there is no such key.
Expand Down
16 changes: 14 additions & 2 deletions lms/djangoapps/courseware/courses.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@
OldMongoAccessError,
StartDateError
)
from lms.djangoapps.courseware.access_utils import check_authentication, check_data_sharing_consent, check_enrollment, \
check_correct_active_enterprise_customer, is_priority_access_error
from lms.djangoapps.courseware.access_utils import (
check_authentication,
check_correct_active_enterprise_customer,
check_data_sharing_consent,
check_enrollment,
check_public_access,
is_priority_access_error,
)
from lms.djangoapps.courseware.context_processor import get_user_timezone_or_last_seen_timezone_or_utc
from lms.djangoapps.courseware.courseware_access_exception import CoursewareAccessException
from lms.djangoapps.courseware.date_summary import (
Expand Down Expand Up @@ -66,6 +72,7 @@
from openedx.features.course_duration_limits.access import AuditExpiredError
from openedx.features.course_experience import RELATIVE_DATES_FLAG
from openedx.features.course_experience.utils import is_block_structure_complete_for_assignments
from xmodule.course_block import COURSE_VISIBILITY_PUBLIC # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.exceptions import ItemNotFoundError # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.x_module import STUDENT_VIEW # lint-amnesty, pylint: disable=wrong-import-order
Expand Down Expand Up @@ -246,6 +253,11 @@ def check_course_access_with_redirect(
# StartDateError should be ignored
if isinstance(access_response, StartDateError) and allow_not_started_courses:
return
# Anonymous users on public courses can view before start date (e.g. Learning MFE public_view).
if isinstance(access_response, StartDateError) and not user.is_authenticated and check_public_access(
course, [COURSE_VISIBILITY_PUBLIC]
):
return
# Redirect if StartDateError
if isinstance(access_response, StartDateError):
start_date = strftime_localized(course.start, 'SHORT_DATE')
Expand Down
2 changes: 1 addition & 1 deletion xmodule/video_block/video_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ def public_view(self, context):

fragment = Fragment(self.get_html(view=PUBLIC_VIEW, context=context))
add_css_to_fragment(fragment, 'VideoBlockDisplay.css')
add_webpack_js_to_fragment(fragment, 'VideoBlockMain')
add_webpack_js_to_fragment(fragment, 'VideoBlockDisplay')
fragment.initialize_js('Video')
return fragment

Expand Down
Loading