Merge pull request #25897 from edx/ddumesnil/fix-masquerade-for-mfe

Fixes for Shift deadlines during masquerade in Learning MFE
This commit is contained in:
Dillon Dumesnil
2020-12-16 11:57:23 -08:00
committed by GitHub
4 changed files with 76 additions and 14 deletions

View File

@@ -196,6 +196,9 @@ 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(

View File

@@ -1631,6 +1631,9 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True):
u"Rendering of the xblock view '{}' is not supported.".format(bleach.clean(requested_view, strip=True))
)
staff_access = has_access(request.user, 'staff', course_key)
_course_masquerade, request.user = setup_masquerade(request, course_key, staff_access)
with modulestore().bulk_operations(course_key):
# verify the user has access to the course, including enrollment check
try:
@@ -1668,7 +1671,7 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True):
'disable_window_wrap': True,
'enable_completion_on_view_service': enable_completion_on_view_service,
'edx_notes_enabled': is_feature_enabled(course, request.user),
'staff_access': bool(request.user.has_perm(VIEW_XQA_INTERFACE, course)),
'staff_access': staff_access,
'xqa_server': settings.FEATURES.get('XQA_SERVER', 'http://your_xqa_server.com'),
'missed_deadlines': missed_deadlines,
'missed_gated_content': missed_gated_content,

View File

@@ -1,23 +1,29 @@
"""
Tests for reset deadlines endpoint.
"""
import datetime
import ddt
from django.urls import reverse
from django.utils import timezone
from mock import patch
from common.djangoapps.course_modes.models import CourseMode
from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests
from common.djangoapps.student.models import CourseEnrollment
from lms.djangoapps.courseware.tests.helpers import MasqueradeMixin
from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests
from openedx.core.djangoapps.schedules.models import Schedule
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from xmodule.modulestore.tests.factories import CourseFactory
@ddt.ddt
class ResetCourseDeadlinesViewTests(BaseCourseHomeTests):
class ResetCourseDeadlinesViewTests(BaseCourseHomeTests, MasqueradeMixin):
"""
Tests for reset deadlines endpoint.
"""
@ddt.data(CourseMode.VERIFIED)
def test_reset_deadlines(self, enrollment_mode):
CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode)
def test_reset_deadlines(self):
CourseEnrollment.enroll(self.user, self.course.id, CourseMode.VERIFIED)
# Test correct post body
response = self.client.post(reverse('course-experience-reset-course-deadlines'), {'course_key': self.course.id})
self.assertEqual(response.status_code, 200)
@@ -30,6 +36,32 @@ class ResetCourseDeadlinesViewTests(BaseCourseHomeTests):
)
self.assertEqual(response.status_code, 400)
def test_reset_deadlines_with_masquerade(self):
""" Staff users should be able to masquerade as a learner and reset the learner's schedule """
course = CourseFactory.create(self_paced=True)
student_username = self.user.username
student_enrollment = CourseEnrollment.enroll(self.user, course.id)
student_schedule = ScheduleFactory.create(
start_date=timezone.now() - datetime.timedelta(days=100),
enrollment=student_enrollment
)
staff_schedule = ScheduleFactory(
start_date=timezone.now() - datetime.timedelta(days=30),
enrollment__course__id=course.id,
enrollment__user=self.staff_user,
)
self.switch_to_staff()
self.update_masquerade(course=course, username=student_username)
with patch('openedx.features.course_experience.api.v1.views.dates_banner_should_display',
return_value=(True, False)):
self.client.post(reverse('course-experience-reset-course-deadlines'), {'course_key': course.id})
updated_schedule = Schedule.objects.get(id=student_schedule.id)
self.assertEqual(updated_schedule.start_date.date(), datetime.datetime.today().date())
updated_staff_schedule = Schedule.objects.get(id=staff_schedule.id)
self.assertEqual(updated_staff_schedule.start_date, staff_schedule.start_date)
def test_post_unauthenticated_user(self):
self.client.logout()
response = self.client.post(reverse('course-experience-reset-course-deadlines'), {'course_key': self.course.id})

View File

@@ -1,4 +1,7 @@
import six
"""
Views for Course Experience API.
"""
import logging
from django.conf import settings
from django.urls import reverse
@@ -13,15 +16,20 @@ from rest_framework.generics import RetrieveAPIView
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser
from opaque_keys.edx.keys import CourseKey
from lms.djangoapps.course_home_api.toggles import course_home_mfe_dates_tab_is_active
from lms.djangoapps.course_home_api.utils import get_microfrontend_url
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.courses import get_course_with_access
from lms.djangoapps.courseware.masquerade import setup_masquerade
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.schedules.utils import reset_self_paced_schedule
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
from openedx.features.course_experience.api.v1.serializers import CourseDeadlinesMobileSerializer
from openedx.features.course_experience.utils import dates_banner_should_display
log = logging.getLogger(__name__)
class UnableToResetDeadlines(APIException):
@@ -36,6 +44,13 @@ class UnableToResetDeadlines(APIException):
))
@permission_classes((IsAuthenticated,))
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.
@@ -47,13 +62,21 @@ def reset_course_deadlines(request):
raise ParseError(_("Only 'course_key' is expected."))
try:
reset_self_paced_schedule(request.user, course_key)
course_key = CourseKey.from_string(course_key)
_course_masquerade, user = setup_masquerade(
request,
course_key,
has_access(request.user, 'staff', course_key)
)
key = CourseKey.from_string(course_key)
if course_home_mfe_dates_tab_is_active(key):
body_link = get_microfrontend_url(course_key=course_key, view_name='dates')
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=[six.text_type(course_key)]))
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')),
@@ -62,7 +85,8 @@ def reset_course_deadlines(request):
'link_text': _('View all dates'),
'message': _('Deadlines successfully reset.'),
})
except Exception:
except Exception as e:
log.exception(e)
raise UnableToResetDeadlines