Skip to content
Merged
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 CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected.

Blades: Allow user with BetaTester role correctly use LTI. BLD-641.

Blades: Video player persist speed preferences between videos. BLD-237.

Blades: Change the download video field to a dropdown that will allow students
Expand Down
21 changes: 16 additions & 5 deletions lms/djangoapps/courseware/features/lti.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ Feature: LMS.LTI component

#1
Scenario: LTI component in LMS with no launch_url is not rendered
Given the course has correct LTI credentials
Given the course has correct LTI credentials with registered Instructor
And the course has an LTI component with no_launch_url fields:
| open_in_a_new_page |
| False |
Then I view the LTI and error is shown

#2
Scenario: LTI component in LMS with incorrect lti_id is rendered incorrectly
Given the course has correct LTI credentials
Given the course has correct LTI credentials with registered Instructor
And the course has an LTI component with incorrect_lti_id fields:
| open_in_a_new_page |
| False |
Expand All @@ -28,21 +28,21 @@ Feature: LMS.LTI component

#4
Scenario: LTI component in LMS is correctly rendered in new page
Given the course has correct LTI credentials
Given the course has correct LTI credentials with registered Instructor
And the course has an LTI component with correct fields
Then I view the LTI and it is rendered in new page

#5
Scenario: LTI component in LMS is correctly rendered in iframe
Given the course has correct LTI credentials
Given the course has correct LTI credentials with registered Instructor
And the course has an LTI component with correct fields:
| open_in_a_new_page |
| False |
Then I view the LTI and it is rendered in iframe

#6
Scenario: Graded LTI component in LMS is correctly works
Given the course has correct LTI credentials
Given the course has correct LTI credentials with registered Instructor
And the course has an LTI component with correct fields:
| open_in_a_new_page | weight | is_graded | has_score |
| False | 10 | True | True |
Expand All @@ -55,4 +55,15 @@ Feature: LMS.LTI component
And I see in the gradebook table that "HW" is "50"
And I see in the gradebook table that "Total" is "5"

#7
Scenario: Graded LTI component in LMS is correctly works with beta testers
Given the course has correct LTI credentials with registered BetaTester
And the course has an LTI component with correct fields:
| open_in_a_new_page | weight | is_graded | has_score |
| False | 10 | True | True |
And I submit answer to LTI question
And I click on the "Progress" tab
Then I see text "Problem Scores: 5/10"
And I see graph with total progress "5%"


52 changes: 38 additions & 14 deletions lms/djangoapps/courseware/features/lti.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
#pylint: disable=C0111

import datetime
import os
import pytz
from mock import patch

from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from lettuce import world, step
from lettuce.django import django_url
from common import course_id, visit_scenario_item

from courseware.tests.factories import InstructorFactory
from common import course_id, visit_scenario_item
from courseware.tests.factories import InstructorFactory, BetaTesterFactory
from courseware.access import has_access
from student.tests.factories import UserFactory


@step('I view the LTI and error is shown$')
Expand Down Expand Up @@ -65,16 +70,16 @@ def incorrect_lti_is_rendered(_step):
check_lti_iframe_content("Wrong LTI signature")


@step('the course has correct LTI credentials$')
def set_correct_lti_passport(_step):
@step('the course has correct LTI credentials with registered (.*)$')
def set_correct_lti_passport(_step, user='Instructor'):
coursenum = 'test_course'
metadata = {
'lti_passports': ["correct_lti_id:{}:{}".format(
world.lti_server.oauth_settings['client_key'],
world.lti_server.oauth_settings['client_secret']
)]
}
i_am_registered_for_the_course(coursenum, metadata)
i_am_registered_for_the_course(coursenum, metadata, user)


@step('the course has incorrect LTI credentials$')
Expand Down Expand Up @@ -126,7 +131,7 @@ def add_correct_lti_to_course(_step, fields):
visit_scenario_item('LTI')


def create_course(course, metadata):
def create_course_for_lti(course, metadata):

# First clear the modulestore so we don't try to recreate
# the same course twice
Expand Down Expand Up @@ -181,16 +186,35 @@ def create_course(course, metadata):
metadata={'graded': True, 'format': 'Homework'})


def i_am_registered_for_the_course(course, metadata):
# Create the course
create_course(course, metadata)

# Create an instructor
instructor = InstructorFactory(course=world.scenario_dict['COURSE'].location)
@patch.dict('courseware.access.settings.FEATURES', {'DISABLE_START_DATES': False})
def i_am_registered_for_the_course(coursenum, metadata, user='Instructor'):
# Create user
if user == 'BetaTester':
# Create the course
now = datetime.datetime.now(pytz.UTC)
tomorrow = now + datetime.timedelta(days=5)
metadata.update({'days_early_for_beta': 5, 'start': tomorrow})
create_course_for_lti(coursenum, metadata)
course_descriptor = world.scenario_dict['COURSE']
course_location = world.scenario_dict['COURSE'].location

# create beta tester
user = BetaTesterFactory(course=course_location)
normal_student = UserFactory()
instructor = InstructorFactory(course=course_location)
assert not has_access(normal_student, course_descriptor, 'load')
assert has_access(user, course_descriptor, 'load')
assert has_access(instructor, course_descriptor, 'load')
else:
create_course_for_lti(coursenum, metadata)
course_descriptor = world.scenario_dict['COURSE']
course_location = world.scenario_dict['COURSE'].location
user = InstructorFactory(course=course_location)

# Enroll the user in the course and log them in
world.enroll_user(instructor, course_id(course))
world.log_in(username=instructor.username, password='test')
if has_access(user, course_descriptor, 'load'):
world.enroll_user(user, course_id(coursenum))
world.log_in(username=user.username, password='test')


def check_lti_popup():
Expand Down
10 changes: 7 additions & 3 deletions lms/djangoapps/courseware/module_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,11 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
See get_module() docstring for further details.
"""

# Short circuit--if the user shouldn't have access, bail without doing any work
if not has_access(user, descriptor, 'load', course_id):
return None
# Do not check access when it's a noauth request.
if getattr(user, 'known', True):
# Short circuit--if the user shouldn't have access, bail without doing any work
if not has_access(user, descriptor, 'load', course_id):
return None

student_data = KvsFieldData(DjangoKeyValueStore(field_data_cache))
descriptor._field_data = LmsFieldData(descriptor._field_data, student_data)
Expand Down Expand Up @@ -516,6 +518,8 @@ def handle_xblock_callback_noauth(request, course_id, usage_id, handler, suffix=
"""
Entry point for unauthenticated XBlock handlers.
"""
request.user.known = False

return _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, request.user)


Expand Down