def masquerade_as_group_member(user, course, partition_id, group_id): """ Installs a masquerade for the specified user and course, to enable the user to masquerade as belonging to the specific partition/group combination. Arguments: user (User): a user. course (CourseDescriptor): a course. partition_id (int): the integer partition id, referring to partitions already configured in the course. group_id (int); the integer group id, within the specified partition. Returns: the status code for the AJAX response to update the user's masquerade for the specified course. """ request = _create_mock_json_request(user, data={ "role": "student", "user_partition_id": partition_id, "group_id": group_id }) response = MasqueradeView.as_view()(request, six.text_type(course.id)) setup_masquerade(request, course.id, True) return response.status_code
def _determine_user(self, request, course_key: CourseKey) -> types.User: """ For which user should we get an outline? Uses a combination of the user on the request object, session masquerading data, and a manually passed in "user" parameter. Ensures that the requesting user has permission to view course outline of target user. Raise request-level exceptions otherwise. The "user" querystring param is expected to be a username, with a blank value being interpreted as the anonymous user. It will take priority over session masquerading, if provided. """ has_staff_access = has_access(request.user, 'staff', course_key).has_access target_username = request.GET.get("user") if target_username is not None: target_user = self._get_target_user(request, course_key, has_staff_access, target_username) # Just like in masquerading, set real_user so that the # SafeSessions middleware can see that the user didn't # change unexpectedly. target_user.real_user = request.user return target_user _course_masquerade, user = setup_masquerade(request, course_key, has_staff_access) return user
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) if course_home_legacy_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) is_staff = bool(has_access(request.user, 'staff', course_key)) _, request.user = setup_masquerade( request, course_key, staff_access=is_staff, reset_masquerade_data=True, ) if not CourseEnrollment.is_enrolled(request.user, course_key) and not is_staff: return Response('User not enrolled.', status=401) blocks = get_course_date_blocks(course, request.user, request, include_access=True, include_past_dates=True) learner_is_full_access = not ContentTypeGatingConfig.enabled_for_enrollment( user=request.user, course_key=course_key, ) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] data = { 'has_ended': course.has_ended(), 'course_date_blocks': [block for block in blocks if not isinstance(block, TodaysDate)], 'learner_is_full_access': learner_is_full_access, 'user_timezone': user_timezone, } context = self.get_serializer_context() context['learner_is_full_access'] = learner_is_full_access serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def __init__(self, course_key, request, username=''): self.request = request self.overview = course_detail( self.request, username or self.request.user.username, course_key, ) # We must compute course load access *before* setting up masquerading, # else course staff (who are not enrolled) will not be able view # their course from the perspective of a learner. self.load_access = check_course_access( self.overview, self.request.user, 'load', check_if_enrolled=True, check_if_authenticated=True, ) self.original_user_is_staff = has_access(self.request.user, 'staff', self.overview).has_access self.original_user_is_global_staff = self.request.user.is_staff self.course_key = course_key self.course = get_course_by_id(self.course_key) self.course_masquerade, self.effective_user = setup_masquerade( self.request, course_key, staff_access=self.original_user_is_staff, ) self.request.user = self.effective_user self.is_staff = has_access(self.effective_user, 'staff', self.overview).has_access self.enrollment_object = CourseEnrollment.get_enrollment(self.effective_user, self.course_key, select_related=['celebration', 'user__celebration']) self.can_view_legacy_courseware = courseware_legacy_is_visible( course_key=course_key, is_global_staff=self.original_user_is_global_staff, )
def get(self, request, usage_key_string, *args, **kwargs): # lint-amnesty, pylint: disable=unused-argument """ Return response to a GET request. """ try: usage_key = UsageKey.from_string(usage_key_string) except InvalidKeyError: raise NotFound(f"Invalid usage key: '{usage_key_string}'.") # lint-amnesty, pylint: disable=raise-missing-from _, request.user = setup_masquerade( request, usage_key.course_key, staff_access=has_access(request.user, 'staff', usage_key.course_key), reset_masquerade_data=True, ) sequence, _ = get_module_by_usage_id( self.request, str(usage_key.course_key), str(usage_key), disable_staff_debug_info=True, will_recheck_access=True) view = STUDENT_VIEW if request.user.is_anonymous: view = PUBLIC_VIEW context = {'specific_masquerade': is_masquerading_as_specific_student(request.user, usage_key.course_key)} return Response(sequence.get_metadata(view=view, context=context))
def get(self, request, usage_key_string, *args, **kwargs): # lint-amnesty, pylint: disable=unused-argument """ Return response to a GET request. """ try: usage_key = UsageKey.from_string(usage_key_string) except InvalidKeyError as exc: raise NotFound(f"Invalid usage key: '{usage_key_string}'.") from exc _, request.user = setup_masquerade( request, usage_key.course_key, staff_access=has_access(request.user, 'staff', usage_key.course_key), reset_masquerade_data=True, ) sequence, _ = get_module_by_usage_id( self.request, str(usage_key.course_key), str(usage_key), disable_staff_debug_info=True, will_recheck_access=True) if not hasattr(sequence, 'get_metadata'): # Looks like we were asked for metadata on something that is not a sequence (or section). return Response(status=status.HTTP_422_UNPROCESSABLE_ENTITY) view = STUDENT_VIEW if request.user.is_anonymous: view = PUBLIC_VIEW context = {'specific_masquerade': is_masquerading_as_specific_student(request.user, usage_key.course_key)} return Response(sequence.get_metadata(view=view, context=context))
def __init__(self, course_key, request, username=''): self.request = request self.overview = course_detail( self.request, username or self.request.user.username, course_key, ) self.original_user_is_staff = has_access(self.request.user, 'staff', self.overview).has_access self.original_user_is_global_staff = self.request.user.is_staff self.course_key = course_key self.course = get_course_by_id(self.course_key) self.course_masquerade, self.effective_user = setup_masquerade( self.request, course_key, staff_access=self.original_user_is_staff, ) self.request.user = self.effective_user self.is_staff = has_access(self.effective_user, 'staff', self.overview).has_access self.enrollment_object = CourseEnrollment.get_enrollment( self.effective_user, self.course_key, select_related=['celebration', 'user__celebration']) self.can_view_legacy_courseware = courseware_legacy_is_visible( course_key=course_key, is_global_staff=self.original_user_is_global_staff, )
def reset_course_deadlines(request): """ Set the start_date of a schedule to today, which in turn will adjust due dates for sequentials belonging to a self paced course """ from lms.urls import RENDER_XBLOCK_NAME from openedx.features.course_experience.urls import COURSE_HOME_VIEW_NAME detail_id_dict = ast.literal_eval( request.POST.get('reset_deadlines_redirect_url_id_dict')) redirect_url = request.POST.get('reset_deadlines_redirect_url_base', COURSE_HOME_VIEW_NAME) course_key = CourseKey.from_string(detail_id_dict['course_id']) masquerade_details, masquerade_user = setup_masquerade( request, course_key, has_access(request.user, 'staff', course_key)) if masquerade_details and masquerade_details.role == 'student' and masquerade_details.user_name and ( redirect_url == COURSE_HOME_VIEW_NAME): # Masquerading as a specific student, so reset that student's schedule user = masquerade_user else: user = request.user reset_self_paced_schedule(user, course_key) if redirect_url == RENDER_XBLOCK_NAME: detail_id_dict.pop('course_id') return redirect(reverse(redirect_url, kwargs=detail_id_dict))
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) original_user_is_staff = has_access(request.user, 'staff', course_key).has_access _, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) course = course_detail(request, request.user.username, course_key) user_is_enrolled = CourseEnrollment.is_enrolled(request.user, course_key_string) data = { 'course_id': course.id, 'is_staff': has_access(request.user, 'staff', course_key).has_access, 'original_user_is_staff': original_user_is_staff, 'number': course.display_number_with_default, 'org': course.display_org_with_default, 'tabs': get_course_tab_list(request.user, course), 'title': course.display_name_with_default, 'is_self_paced': getattr(course, 'self_paced', False), 'is_enrolled': user_is_enrolled, } context = self.get_serializer_context() context['course'] = course serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def post(self, request, course_key_string, *args, **kwargs): # lint-amnesty, pylint: disable=unused-argument """ Handle a POST request. """ course_key = CourseKey.from_string(course_key_string) # Check if we're masquerading as someone else. If so, we should just ignore this request. _, user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) if user != request.user: return Response(status=202) # "Accepted" data = dict(request.data) first_section = data.pop('first_section', None) if data: return Response(status=400) # there were parameters we didn't recognize enrollment = CourseEnrollment.get_enrollment(request.user, course_key) if not enrollment: return Response(status=404) defaults = {} if first_section is not None: defaults['celebrate_first_section'] = first_section if defaults: _, created = CourseEnrollmentCelebration.objects.update_or_create(enrollment=enrollment, defaults=defaults) return Response(status=201 if created else 200) else: return Response(status=200) # just silently allow it
def get(self, request, usage_key_string, *args, **kwargs): # lint-amnesty, pylint: disable=unused-argument """ Return response to a GET request. """ try: usage_key = UsageKey.from_string(usage_key_string) except InvalidKeyError: raise NotFound("Invalid usage key: '{}'.".format(usage_key_string)) # lint-amnesty, pylint: disable=raise-missing-from _, request.user = setup_masquerade( request, usage_key.course_key, staff_access=has_access(request.user, 'staff', usage_key.course_key), reset_masquerade_data=True, ) sequence, _ = get_module_by_usage_id(self.request, str(usage_key.course_key), str(usage_key), disable_staff_debug_info=True, will_recheck_access=True) view = STUDENT_VIEW if request.user.is_anonymous: view = PUBLIC_VIEW return Response( json.loads(sequence.handle_ajax('metadata', None, view=view)))
def _get_student_user(self, request, course_key, student_id, is_staff): """Gets the student User object, either from coaching, masquerading, or normal actual request""" if student_id: try: student_id = int(student_id) except ValueError as e: raise Http404 from e if student_id is None or student_id == request.user.id: _, student = setup_masquerade( request, course_key, staff_access=is_staff, reset_masquerade_data=True ) return student # When a student_id is passed in, we display the progress page for the user # with the provided user id, rather than the requesting user try: coach_access = has_ccx_coach_role(request.user, course_key) except CCXLocatorValidationException: coach_access = False has_access_on_students_profiles = is_staff or coach_access # Requesting access to a different student's profile if not has_access_on_students_profiles: raise Http404 try: return User.objects.get(id=student_id) except User.DoesNotExist as exc: raise Http404 from exc
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) monitoring_utils.set_custom_metric('is_staff', request.user.is_staff) _, request.user = setup_masquerade(request, course_key, staff_access=has_access( request.user, 'staff', course_key), reset_masquerade_data=True) user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] transformers = BlockStructureTransformers() transformers += course_blocks_api.get_course_block_access_transformers( request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True) enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) course_grade = CourseGradeFactory().read(request.user, course) courseware_summary = course_grade.chapter_grades.values() data = { 'course_blocks': course_blocks, 'courseware_summary': courseware_summary, 'enrollment_mode': enrollment_mode, 'user_timezone': user_timezone, } context = self.get_serializer_context() context['staff_access'] = bool( has_access(request.user, 'staff', course)) context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def reset_course_deadlines(request): """ Set the start_date of a schedule to today, which in turn will adjust due dates for sequentials belonging to a self paced course IMPORTANT NOTE: If updates are happening to the logic here, ALSO UPDATE the `reset_course_deadlines` function in common/djangoapps/util/views.py as well. """ course_key = request.data.get('course_key', None) # If body doesnt contain 'course_key', return 400 to client. if not course_key: raise ParseError(_("'course_key' is required.")) # If body contains params other than 'course_key', return 400 to client. if len(request.data) > 1: raise ParseError(_("Only 'course_key' is expected.")) try: course_key = CourseKey.from_string(course_key) _course_masquerade, user = setup_masquerade( request, course_key, has_access(request.user, 'staff', course_key)) missed_deadlines, missed_gated_content = dates_banner_should_display( course_key, user) if missed_deadlines and not missed_gated_content: reset_self_paced_schedule(user, course_key) if course_home_mfe_dates_tab_is_active(course_key): body_link = get_microfrontend_url(course_key=str(course_key), view_name='dates') else: body_link = '{}{}'.format(settings.LMS_ROOT_URL, reverse('dates', args=[str(course_key)])) return Response({ 'body': format_html('<a href="{}">{}</a>', body_link, _('View all dates')), 'header': _('Your due dates have been successfully shifted to help you stay on track.' ), 'link': body_link, 'link_text': _('View all dates'), 'message': _('Deadlines successfully reset.'), }) except Exception as e: log.exception(e) raise UnableToResetDeadlines
def _get_context(request, course_id): """ Return all course/student/user data """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, "load", course_key) # Get general info of course grade_cutoff, avg_grade, min_grade, max_grade = _get_course_info( course, course_key) # masquerade and student required for preview_menu (admin) staff_access = bool(has_access(request.user, 'staff', course)) masquerade, student = setup_masquerade(request, course_key, staff_access, reset_masquerade_data=True) prefetch_related_objects([student], 'groups') if request.user.id != student.id: # refetch the course as the assumed student course = get_course_with_access(student, 'load', course_key, check_if_enrolled=True) course_grade = CourseGradeFactory().read(student, course) # Student grades courseware_summary = list(course_grade.chapter_grades.values()) course_expiration_fragment = generate_course_expired_fragment( student, course) context = { "course": course, "avg_grade": avg_grade, "min_grade": min_grade, "max_grade": max_grade, "grade_cutoff": grade_cutoff, "supports_preview_menu": True, "staff_access": staff_access, "masquerade": masquerade, "can_masquerade": staff_access, "student": student, "courseware_summary": courseware_summary, "grade_summary": course_grade.summary, "course_expiration_fragment": course_expiration_fragment, "grade_percent_scaled": grade_percent_scaled, "get_section_visibility": get_section_visibility, "get_feedback": get_feedback, "update_url": reverse('feedback_post_update'), "set_visibility_url": reverse('feedback_post_set_visibility'), } return context
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) original_user_is_staff = has_access(request.user, 'staff', course_key).has_access _, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) username = request.user.username if request.user.username else None course = course_detail(request, request.user.username, course_key) enrollment = CourseEnrollment.get_enrollment(request.user, course_key_string) user_is_enrolled = bool(enrollment and enrollment.is_active) courseware_meta = CoursewareMeta(course_key, request, request.user.username) can_load_courseware = courseware_meta.is_microfrontend_enabled_for_user( ) browser_timezone = self.request.query_params.get( 'browser_timezone', None) celebrations = get_celebrations_dict(request.user, enrollment, course, browser_timezone) data = { 'course_id': course.id, 'username': username, 'is_staff': has_access(request.user, 'staff', course_key).has_access, 'original_user_is_staff': original_user_is_staff, 'number': course.display_number_with_default, 'org': course.display_org_with_default, 'tabs': get_course_tab_list(request.user, course), 'title': course.display_name_with_default, 'is_self_paced': getattr(course, 'self_paced', False), 'is_enrolled': user_is_enrolled, 'can_load_courseware': can_load_courseware, 'celebrations': celebrations, } context = self.get_serializer_context() context['course'] = course context['course_overview'] = course context['enrollment'] = enrollment serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def reset_course_deadlines(request): """ Set the start_date of a schedule to today, which in turn will adjust due dates for sequentials belonging to a self paced course """ course_key = CourseKey.from_string(request.POST.get('course_id')) _course_masquerade, user = setup_masquerade( request, course_key, has_access(request.user, 'staff', course_key)) missed_deadlines, missed_gated_content = dates_banner_should_display( course_key, user) if missed_deadlines and not missed_gated_content: reset_self_paced_schedule(user, course_key) referrer = request.META.get('HTTP_REFERER') return redirect(referrer) if referrer else HttpResponse()
def reset_course_deadlines(request, course_id): """ Set the start_date of a schedule to today, which in turn will adjust due dates for sequentials belonging to a self paced course """ course_key = CourseKey.from_string(course_id) masquerade_details, masquerade_user = setup_masquerade( request, course_key, has_access(request.user, 'staff', course_key)) if masquerade_details and masquerade_details.role == 'student' and masquerade_details.user_name: # Masquerading as a specific student, so reset that student's schedule user = masquerade_user else: user = request.user reset_self_paced_schedule(user, course_key) return redirect( reverse('openedx.course_experience.course_home', args=[six.text_type(course_key)]))
def __init__(self, course_key, request, username=''): self.request = request self.overview = course_detail( self.request, username or self.request.user.username, course_key, ) self.original_user_is_staff = has_access(self.request.user, 'staff', self.overview).has_access self.course_key = course_key self.course_masquerade, self.effective_user = setup_masquerade( self.request, course_key, staff_access=self.original_user_is_staff, ) self.request.user = self.effective_user self.is_staff = has_access(self.effective_user, 'staff', self.overview).has_access self.enrollment_object = CourseEnrollment.get_enrollment(self.effective_user, self.course_key, select_related=['celebration'])
def reset_course_deadlines(request): """ Set the start_date of a schedule to today, which in turn will adjust due dates for sequentials belonging to a self paced course IMPORTANT NOTE: If updates are happening to the logic here, ALSO UPDATE the `reset_course_deadlines` function in openedx/features/course_experience/api/v1/views.py as well. """ course_key = CourseKey.from_string(request.POST.get('course_id')) _course_masquerade, user = setup_masquerade( request, course_key, has_access(request.user, 'staff', course_key)) missed_deadlines, missed_gated_content = dates_banner_should_display( course_key, user) if missed_deadlines and not missed_gated_content: reset_self_paced_schedule(user, course_key) referrer = request.META.get('HTTP_REFERER') return redirect(referrer) if referrer else HttpResponse()
def test_setup_masquerade(self): masquerade_settings = { self.course.id: CourseMasquerade(course_key=self.course.id, role='student', user_name=self.student.username) } self.request.session[MASQUERADE_SETTINGS_KEY] = masquerade_settings course_masquerade, masquerade_user = setup_masquerade( self.request, self.course.id, staff_access=True) # Warning: the SafeSessions middleware relies on the `real_user` attribute to see if a # user is masquerading as another user. If the name of this attribute is changing, please update # the check in SafeSessionMiddleware._verify_user_unchanged as well. assert masquerade_user.real_user == self.staff assert masquerade_user == self.student assert self.request.user.masquerade_settings == masquerade_settings assert course_masquerade == masquerade_settings[self.course.id]
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) monitoring_utils.set_custom_metric('is_staff', request.user.is_staff) _, request.user = setup_masquerade(request, course_key, staff_access=has_access( request.user, 'staff', course_key), reset_masquerade_data=True) transformers = BlockStructureTransformers() transformers += course_blocks_api.get_course_block_access_transformers( request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True) enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) data = { 'course_blocks': course_blocks, 'enrollment_mode': enrollment_mode, } serializer = self.get_serializer(data) return Response(serializer.data)
def __init__(self, course_key, request, username=''): self.overview = course_detail( request, username or request.user.username, course_key, ) self.effective_user = self.overview.effective_user # We need to memoize `is_staff` _before_ we configure masquerade. self.is_staff = has_access(self.effective_user, 'staff', self.overview).has_access self.course_key = course_key self.enrollment_object = CourseEnrollment.get_enrollment( self.effective_user, self.course_key, select_related=['celebration']) course_masquerade, _user = setup_masquerade( request, course_key, staff_access=self.is_staff, ) self.course_masquerade = course_masquerade
def reset_course_deadlines(request): """ Set the start_date of a schedule to today, which in turn will adjust due dates for sequentials belonging to a self paced course IMPORTANT NOTE: If updates are happening to the logic here, ALSO UPDATE the `reset_course_deadlines` function in openedx/features/course_experience/api/v1/views.py as well. """ course_key = CourseKey.from_string(request.POST.get('course_id')) _course_masquerade, user = setup_masquerade( request, course_key, has_access(request.user, 'staff', course_key)) # We ignore the missed_deadlines because this endpoint could be used for # learners who have remaining attempts on a problem and reset their due dates in order to # submit additional attempts. This can apply for 'completed' (submitted) content that would # not be marked as past_due _missed_deadlines, missed_gated_content = dates_banner_should_display( course_key, user) if not missed_gated_content: reset_self_paced_schedule(user, course_key) referrer = request.META.get('HTTP_REFERER') return redirect(referrer) if referrer else HttpResponse()
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) original_user_is_staff = has_access(request.user, 'staff', course_key).has_access _, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) course = course_detail(request, request.user.username, course_key) user_is_enrolled = CourseEnrollment.is_enrolled(request.user, course_key_string) browser_timezone = request.query_params.get('browser_timezone', None) celebrations = { 'streak_length_to_celebrate': UserCelebration.perform_streak_updates( request.user, course_key, browser_timezone ) } data = { 'course_id': course.id, 'is_staff': has_access(request.user, 'staff', course_key).has_access, 'original_user_is_staff': original_user_is_staff, 'number': course.display_number_with_default, 'org': course.display_org_with_default, 'tabs': get_course_tab_list(request.user, course), 'title': course.display_name_with_default, 'is_self_paced': getattr(course, 'self_paced', False), 'is_enrolled': user_is_enrolled, 'celebrations': celebrations, } context = self.get_serializer_context() context['course'] = course serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) if not course_home_mfe_outline_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) masquerade_object, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) user_is_masquerading = is_masquerading( request.user, course_key, course_masquerade=masquerade_object) course_overview = CourseOverview.get_from_id(course_key) 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 # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] dates_tab_link = request.build_absolute_uri( reverse('dates', args=[course.id])) if course_home_mfe_dates_tab_is_active(course.id): dates_tab_link = get_learning_mfe_home_url(course_key=course.id, view_name='dates') # Set all of the defaults access_expiration = None course_blocks = None course_goals = {'goal_options': [], 'selected_goal': None} course_tools = CourseToolsPluginManager.get_enabled_course_tools( request, course_key) dates_widget = { 'course_date_blocks': [], 'dates_tab_link': dates_tab_link, 'user_timezone': user_timezone, } enroll_alert = { 'can_enroll': True, 'extra_text': None, } handouts_html = None offer_data = None resume_course = { 'has_visited_course': False, 'url': None, } welcome_message_html = None is_enrolled = enrollment and enrollment.is_active is_staff = bool(has_access(request.user, 'staff', course_key)) show_enrolled = is_enrolled or is_staff if show_enrolled: course_blocks = get_course_outline_block_tree( request, course_key_string, request.user) date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) dates_widget['course_date_blocks'] = [ block for block in date_blocks if not isinstance(block, TodaysDate) ] handouts_html = get_course_info_section(request, request.user, course, 'handouts') welcome_message_html = get_current_update_for_user(request, course) offer_data = generate_offer_data(request.user, course_overview) access_expiration = get_access_expiration_data( request.user, course_overview) # Only show the set course goal message for enrolled, unverified # users in a course that allows for verified statuses. is_already_verified = CourseEnrollment.is_enrolled_as_verified( request.user, course_key) if not is_already_verified and has_course_goal_permission( request, course_key_string, {'is_enrolled': is_enrolled}): course_goals = { 'goal_options': valid_course_goals_ordered(include_unsure=True), 'selected_goal': None } selected_goal = get_course_goal(request.user, course_key) if selected_goal: course_goals['selected_goal'] = { 'key': selected_goal.goal_key, 'text': get_course_goal_text(selected_goal.goal_key), } try: resume_block = get_key_to_last_completed_block( request.user, course.id) resume_course['has_visited_course'] = True resume_path = reverse('jump_to', kwargs={ 'course_id': course_key_string, 'location': str(resume_block) }) resume_course['url'] = request.build_absolute_uri(resume_path) except UnavailableCompletionData: start_block = get_start_block(course_blocks) resume_course['url'] = start_block['lms_web_url'] elif allow_public_outline or allow_public or user_is_masquerading: course_blocks = get_course_outline_block_tree( request, course_key_string, None) if allow_public or user_is_masquerading: handouts_html = get_course_info_section( request, request.user, course, 'handouts') if not show_enrolled: if CourseMode.is_masters_only(course_key): enroll_alert['can_enroll'] = False enroll_alert['extra_text'] = _( 'Please contact your degree administrator or ' 'edX Support if you have questions.') elif course.invitation_only: enroll_alert['can_enroll'] = False data = { 'access_expiration': access_expiration, 'course_blocks': course_blocks, 'course_goals': course_goals, 'course_tools': course_tools, 'dates_widget': dates_widget, 'enroll_alert': enroll_alert, 'handouts_html': handouts_html, 'has_ended': course.has_ended(), 'offer': offer_data, 'resume_course': resume_course, 'welcome_message_html': welcome_message_html, } context = self.get_serializer_context() context['course_overview'] = course_overview context['enable_links'] = show_enrolled or allow_public context['enrollment'] = enrollment serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) if not course_home_mfe_outline_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) monitoring_utils.set_custom_metric('is_staff', request.user.is_staff) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) _masquerade, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) course_overview = CourseOverview.get_from_id(course_key) 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 is_enrolled = enrollment and enrollment.is_active is_staff = 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) welcome_message_html = None if get_course_tag(request.user, course_key, PREFERENCE_KEY) != 'False': if LATEST_UPDATE_FLAG.is_enabled(course_key): welcome_message_html = LatestUpdateFragmentView( ).latest_update_html(request, course) else: welcome_message_html = WelcomeMessageFragmentView( ).welcome_message_html(request, course) enroll_alert = { 'can_enroll': True, 'extra_text': None, } if not show_enrolled: if CourseMode.is_masters_only(course_key): enroll_alert['can_enroll'] = False enroll_alert['extra_text'] = _( 'Please contact your degree administrator or ' 'edX Support if you have questions.') elif course.invitation_only: enroll_alert['can_enroll'] = False course_tools = CourseToolsPluginManager.get_enabled_course_tools( request, course_key) date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] dates_tab_link = request.build_absolute_uri( reverse('dates', args=[course.id])) if course_home_mfe_dates_tab_is_active(course.id): dates_tab_link = get_microfrontend_url(course_key=course.id, view_name='dates') transformers = BlockStructureTransformers() transformers += get_course_block_access_transformers(request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] course_blocks = get_course_blocks(request.user, course_usage_key, transformers, include_completion=True) dates_widget = { 'course_date_blocks': [ block for block in date_blocks if not isinstance(block, TodaysDate) ], 'dates_tab_link': dates_tab_link, 'user_timezone': user_timezone, } data = { 'course_blocks': course_blocks, 'course_expired_html': course_expired_html, 'course_tools': course_tools, 'dates_widget': dates_widget, 'enroll_alert': enroll_alert, 'handouts_html': handouts_html, 'offer_html': offer_html, 'welcome_message_html': welcome_message_html, } context = self.get_serializer_context() context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) course_usage_key = modulestore().make_course_usage_key(course_key) if not course_home_mfe_outline_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) _masquerade, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) course_overview = CourseOverview.get_from_id(course_key) 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 = 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 = 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 show_enrolled: if LATEST_UPDATE_FLAG.is_enabled(course_key): welcome_message_html = LatestUpdateFragmentView( ).latest_update_html(request, course) elif get_course_tag(request.user, course_key, PREFERENCE_KEY) != 'False': welcome_message_html = WelcomeMessageFragmentView( ).welcome_message_html(request, course) enroll_alert = { 'can_enroll': True, 'extra_text': None, } if not show_enrolled: if CourseMode.is_masters_only(course_key): enroll_alert['can_enroll'] = False enroll_alert['extra_text'] = _( 'Please contact your degree administrator or ' 'edX Support if you have questions.') elif course.invitation_only: enroll_alert['can_enroll'] = False course_tools = CourseToolsPluginManager.get_enabled_course_tools( request, course_key) date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] dates_tab_link = request.build_absolute_uri( reverse('dates', args=[course.id])) 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 = 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': 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) ], 'dates_tab_link': dates_tab_link, 'user_timezone': user_timezone, } # Only show the set course goal message for enrolled, unverified # users in a course that allows for verified statuses. is_already_verified = CourseEnrollment.is_enrolled_as_verified( request.user, course_key) if (not is_already_verified and has_course_goal_permission( request, course_key_string, {'is_enrolled': is_enrolled})): course_goals = { 'goal_options': valid_course_goals_ordered(include_unsure=True), 'selected_goal': None } selected_goal = get_course_goal(request.user, course_key) if selected_goal: course_goals['selected_goal'] = { 'key': selected_goal.goal_key, 'text': get_course_goal_text(selected_goal.goal_key), } else: course_goals = {'goal_options': [], 'selected_goal': None} data = { 'course_blocks': course_blocks, '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 or None, 'has_ended': course.has_ended(), 'offer_html': offer_html or None, 'resume_course': resume_course, 'welcome_message_html': welcome_message_html or None, } context = self.get_serializer_context() context['course_key'] = course_key context['enable_links'] = show_enrolled or allow_public serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def reset_course_deadlines(request): """ Set the start_date of a schedule to today, which in turn will adjust due dates for sequentials belonging to a self paced course Request Parameters: course_key: course key research_event_data: any data that should be included in the research tracking event Example: sending the location of where the reset deadlines banner (i.e. outline-tab) IMPORTANT NOTE: If updates are happening to the logic here, ALSO UPDATE the `reset_course_deadlines` function in common/djangoapps/util/views.py as well. """ course_key = request.data.get('course_key', None) research_event_data = request.data.get('research_event_data', {}) # If body doesnt contain 'course_key', return 400 to client. if not course_key: raise ParseError(_("'course_key' is required.")) try: course_key = CourseKey.from_string(course_key) course_masquerade, user = setup_masquerade( request, course_key, has_access(request.user, 'staff', course_key)) # We ignore the missed_deadlines because this endpoint is used in the Learning MFE for # learners who have remaining attempts on a problem and reset their due dates in order to # submit additional attempts. This can apply for 'completed' (submitted) content that would # not be marked as past_due _missed_deadlines, missed_gated_content = dates_banner_should_display( course_key, user) if not missed_gated_content: reset_self_paced_schedule(user, course_key) course_overview = course_detail(request, user.username, course_key) # For context here, research_event_data should already contain `location` indicating # the page/location dates were reset from and could also contain `block_id` if reset # within courseware. research_event_data.update({ 'courserun_key': str(course_key), 'is_masquerading': is_masquerading(user, course_key, course_masquerade), 'is_staff': has_access(user, 'staff', course_key).has_access, 'org_key': course_overview.display_org_with_default, 'user_id': user.id, }) tracker.emit('edx.ui.lms.reset_deadlines.clicked', research_event_data) if course_home_legacy_is_active(course_key): body_link = '{}{}'.format(settings.LMS_ROOT_URL, reverse('dates', args=[str(course_key)])) else: body_link = get_learning_mfe_home_url(course_key=str(course_key), view_name='dates') return Response({ 'body': format_html('<a href="{}">{}</a>', body_link, _('View all dates')), 'header': _('Your due dates have been successfully shifted to help you stay on track.' ), 'link': body_link, 'link_text': _('View all dates'), 'message': _('Deadlines successfully reset.'), }) except Exception as reset_deadlines_exception: log.exception('Error occurred while trying to reset deadlines!') raise UnableToResetDeadlines from reset_deadlines_exception
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) if not course_home_mfe_dates_tab_is_active(course_key): return Response(status=status.HTTP_404_NOT_FOUND) # Enable NR tracing for this view based on course monitoring_utils.set_custom_metric('course_id', course_key_string) monitoring_utils.set_custom_metric('user_id', request.user.id) monitoring_utils.set_custom_metric('is_staff', request.user.is_staff) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) _, request.user = setup_masquerade( request, course_key, staff_access=has_access(request.user, 'staff', course_key), reset_masquerade_data=True, ) blocks = get_course_date_blocks(course, request.user, request, include_access=True, include_past_dates=True) missed_deadlines, missed_gated_content = dates_banner_should_display( course_key, request.user) learner_is_full_access = not ContentTypeGatingConfig.enabled_for_enrollment( user=request.user, course_key=course_key, ) # User locale settings user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] data = { 'has_ended': course.has_ended(), 'course_date_blocks': [block for block in blocks if not isinstance(block, TodaysDate)], 'missed_deadlines': missed_deadlines, 'missed_gated_content': missed_gated_content, 'learner_is_full_access': learner_is_full_access, 'user_timezone': user_timezone, 'verified_upgrade_link': verified_upgrade_deadline_link(request.user, course=course), } context = self.get_serializer_context() context['learner_is_full_access'] = learner_is_full_access serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)