Commit 31d5d513 by Nimisha Asthagiri

MA-1337 xBlock Rendering View

parent 22b3c0bc
...@@ -1128,14 +1128,15 @@ class TestRenderXBlock(RenderXBlockTestMixin, ModuleStoreTestCase): ...@@ -1128,14 +1128,15 @@ class TestRenderXBlock(RenderXBlockTestMixin, ModuleStoreTestCase):
This class overrides the get_response method, which is used by This class overrides the get_response method, which is used by
the tests defined in RenderXBlockTestMixin. the tests defined in RenderXBlockTestMixin.
""" """
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_RENDER_XBLOCK_API': True})
def setUp(self): def setUp(self):
reload_django_url_config() reload_django_url_config()
super(TestRenderXBlock, self).setUp() super(TestRenderXBlock, self).setUp()
def get_response(self): def get_response(self, url_encoded_params=None):
""" """
Overridable method to get the response from the endpoint that is being tested. Overridable method to get the response from the endpoint that is being tested.
""" """
url = reverse('render_xblock', kwargs={"usage_key_string": unicode(self.html_block.location)}) url = reverse('render_xblock', kwargs={"usage_key_string": unicode(self.html_block.location)})
if url_encoded_params:
url += '?' + url_encoded_params
return self.client.get(url) return self.client.get(url)
...@@ -6,6 +6,7 @@ from abc import ABCMeta, abstractmethod ...@@ -6,6 +6,7 @@ from abc import ABCMeta, abstractmethod
from datetime import datetime from datetime import datetime
import ddt import ddt
from mock import patch from mock import patch
from urllib import urlencode
from lms.djangoapps.courseware.url_helpers import get_redirect_url from lms.djangoapps.courseware.url_helpers import get_redirect_url
from student.tests.factories import AdminFactory, UserFactory, CourseEnrollmentFactory from student.tests.factories import AdminFactory, UserFactory, CourseEnrollmentFactory
...@@ -40,9 +41,12 @@ class RenderXBlockTestMixin(object): ...@@ -40,9 +41,12 @@ class RenderXBlockTestMixin(object):
] ]
@abstractmethod @abstractmethod
def get_response(self): def get_response(self, url_encoded_params=None):
""" """
Abstract method to get the response from the endpoint that is being tested. Abstract method to get the response from the endpoint that is being tested.
Arguments:
url_encoded_params - URL encoded parameters that should be appended to the requested URL.
""" """
pass # pragma: no cover pass # pragma: no cover
...@@ -79,11 +83,13 @@ class RenderXBlockTestMixin(object): ...@@ -79,11 +83,13 @@ class RenderXBlockTestMixin(object):
if login: if login:
self.login() self.login()
def verify_response(self, expected_response_code=200): def verify_response(self, expected_response_code=200, url_params=None):
""" """
Helper method that calls the endpoint, verifies the expected response code, and returns the response. Helper method that calls the endpoint, verifies the expected response code, and returns the response.
""" """
response = self.get_response() if url_params:
url_params = urlencode(url_params)
response = self.get_response(url_params)
if expected_response_code == 200: if expected_response_code == 200:
self.assertContains(response, self.html_block.data, status_code=expected_response_code) self.assertContains(response, self.html_block.data, status_code=expected_response_code)
for chrome_element in [self.COURSEWARE_CHROME_HTML_ELEMENTS + self.XBLOCK_REMOVED_HTML_ELEMENTS]: for chrome_element in [self.COURSEWARE_CHROME_HTML_ELEMENTS + self.XBLOCK_REMOVED_HTML_ELEMENTS]:
...@@ -175,3 +181,13 @@ class RenderXBlockTestMixin(object): ...@@ -175,3 +181,13 @@ class RenderXBlockTestMixin(object):
self.html_block.visible_to_staff_only = True self.html_block.visible_to_staff_only = True
modulestore().update_item(self.html_block, self.user.id) modulestore().update_item(self.html_block, self.user.id)
self.verify_response(expected_response_code=404) self.verify_response(expected_response_code=404)
def test_student_view_param(self):
self.setup_course()
self.setup_user(admin=False, enroll=True, login=True)
self.verify_response(url_params={'view': 'student_view'})
def test_unsupported_view_param(self):
self.setup_course()
self.setup_user(admin=False, enroll=True, login=True)
self.verify_response(url_params={'view': 'author_view'}, expected_response_code=400)
...@@ -5,12 +5,9 @@ Courseware views functions ...@@ -5,12 +5,9 @@ Courseware views functions
import logging import logging
import urllib import urllib
import json import json
import cgi
from datetime import datetime from datetime import datetime
from django.utils import translation
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
from django.conf import settings from django.conf import settings
from django.core.context_processors import csrf from django.core.context_processors import csrf
...@@ -1415,6 +1412,10 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True): ...@@ -1415,6 +1412,10 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True):
usage_key = usage_key.replace(course_key=modulestore().fill_in_run(usage_key.course_key)) usage_key = usage_key.replace(course_key=modulestore().fill_in_run(usage_key.course_key))
course_key = usage_key.course_key course_key = usage_key.course_key
requested_view = request.GET.get('view', 'student_view')
if requested_view != 'student_view':
return HttpResponseBadRequest("Rendering of the xblock view '{}' is not supported.".format(requested_view))
with modulestore().bulk_operations(course_key): with modulestore().bulk_operations(course_key):
# verify the user has access to the course, including enrollment check # verify the user has access to the course, including enrollment check
try: try:
......
...@@ -163,7 +163,7 @@ class LtiLaunchTestRender(LtiTestMixin, RenderXBlockTestMixin, ModuleStoreTestCa ...@@ -163,7 +163,7 @@ class LtiLaunchTestRender(LtiTestMixin, RenderXBlockTestMixin, ModuleStoreTestCa
This class overrides the get_response method, which is used by This class overrides the get_response method, which is used by
the tests defined in RenderXBlockTestMixin. the tests defined in RenderXBlockTestMixin.
""" """
def get_response(self): def get_response(self, url_encoded_params=None):
""" """
Overridable method to get the response from the endpoint that is being tested. Overridable method to get the response from the endpoint that is being tested.
""" """
...@@ -174,15 +174,28 @@ class LtiLaunchTestRender(LtiTestMixin, RenderXBlockTestMixin, ModuleStoreTestCa ...@@ -174,15 +174,28 @@ class LtiLaunchTestRender(LtiTestMixin, RenderXBlockTestMixin, ModuleStoreTestCa
'usage_id': unicode(self.html_block.location) 'usage_id': unicode(self.html_block.location)
} }
) )
if url_encoded_params:
lti_launch_url += '?' + url_encoded_params
SignatureValidator.verify = MagicMock(return_value=True) SignatureValidator.verify = MagicMock(return_value=True)
return self.client.post(lti_launch_url, data=LTI_DEFAULT_PARAMS) return self.client.post(lti_launch_url, data=LTI_DEFAULT_PARAMS)
# The following test methods override the base tests for verifying access
# by unenrolled and unauthenticated students, since there is a discrepancy
# of access rules between the 2 endpoints (LTI and xBlock_render).
# TODO fix this access discrepancy to the same underlying data.
def test_unenrolled_student(self): def test_unenrolled_student(self):
"""
Override since LTI allows access to unenrolled students.
"""
self.setup_course() self.setup_course()
self.setup_user(admin=False, enroll=False, login=True) self.setup_user(admin=False, enroll=False, login=True)
self.verify_response() self.verify_response()
def test_unauthenticated(self): def test_unauthenticated(self):
"""
Override since LTI allows access to unauthenticated users.
"""
self.setup_course() self.setup_course()
self.setup_user(admin=False, enroll=True, login=False) self.setup_user(admin=False, enroll=True, login=False)
self.verify_response() self.verify_response()
...@@ -319,8 +319,7 @@ FEATURES = { ...@@ -319,8 +319,7 @@ FEATURES = {
'ENABLE_MOBILE_REST_API': False, 'ENABLE_MOBILE_REST_API': False,
'ENABLE_MOBILE_SOCIAL_FACEBOOK_FEATURES': False, 'ENABLE_MOBILE_SOCIAL_FACEBOOK_FEATURES': False,
# Enable APIs required for xBlocks on Mobile, and supported in general # Enable temporary APIs required for xBlocks on Mobile
'ENABLE_RENDER_XBLOCK_API': False,
'ENABLE_COURSE_BLOCKS_NAVIGATION_API': False, 'ENABLE_COURSE_BLOCKS_NAVIGATION_API': False,
# Enable the combined login/registration form # Enable the combined login/registration form
......
...@@ -277,7 +277,6 @@ FEATURES['ENABLE_MOBILE_REST_API'] = True ...@@ -277,7 +277,6 @@ FEATURES['ENABLE_MOBILE_REST_API'] = True
FEATURES['ENABLE_MOBILE_SOCIAL_FACEBOOK_FEATURES'] = True FEATURES['ENABLE_MOBILE_SOCIAL_FACEBOOK_FEATURES'] = True
FEATURES['ENABLE_VIDEO_ABSTRACTION_LAYER_API'] = True FEATURES['ENABLE_VIDEO_ABSTRACTION_LAYER_API'] = True
FEATURES['ENABLE_COURSE_BLOCKS_NAVIGATION_API'] = True FEATURES['ENABLE_COURSE_BLOCKS_NAVIGATION_API'] = True
FEATURES['ENABLE_RENDER_XBLOCK_API'] = True
###################### Payment ##############################3 ###################### Payment ##############################3
# Enable fake payment processing page # Enable fake payment processing page
......
...@@ -248,24 +248,62 @@ if settings.COURSEWARE_ENABLED: ...@@ -248,24 +248,62 @@ if settings.COURSEWARE_ENABLED:
) )
) )
urlpatterns += ( urlpatterns += (
url(r'^courses/{}/jump_to/(?P<location>.*)$'.format(settings.COURSE_ID_PATTERN), # jump_to URLs for direct access to a location in the course
'courseware.views.jump_to', name="jump_to"), url(
url(r'^courses/{}/jump_to_id/(?P<module_id>.*)$'.format(settings.COURSE_ID_PATTERN), r'^courses/{}/jump_to/(?P<location>.*)$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.jump_to_id', name="jump_to_id"), 'courseware.views.jump_to', name="jump_to",
url(r'^courses/{course_key}/xblock/{usage_key}/handler/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$'.format(course_key=settings.COURSE_ID_PATTERN, usage_key=settings.USAGE_ID_PATTERN), ),
url(
r'^courses/{}/jump_to_id/(?P<module_id>.*)$'.format(settings.COURSE_ID_PATTERN),
'courseware.views.jump_to_id', name="jump_to_id",
),
# xblock Handler APIs
url(
r'^courses/{course_key}/xblock/{usage_key}/handler/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$'.format(
course_key=settings.COURSE_ID_PATTERN,
usage_key=settings.USAGE_ID_PATTERN,
),
'courseware.module_render.handle_xblock_callback', 'courseware.module_render.handle_xblock_callback',
name='xblock_handler'), name='xblock_handler',
url(r'^courses/{course_key}/xblock/{usage_key}/view/(?P<view_name>[^/]*)$'.format( ),
course_key=settings.COURSE_ID_PATTERN, url(
usage_key=settings.USAGE_ID_PATTERN), r'^courses/{course_key}/xblock/{usage_key}/handler_noauth/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$'.format(
'courseware.module_render.xblock_view', course_key=settings.COURSE_ID_PATTERN,
name='xblock_view'), usage_key=settings.USAGE_ID_PATTERN,
url(r'^courses/{course_key}/xblock/{usage_key}/handler_noauth/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$'.format(course_key=settings.COURSE_ID_PATTERN, usage_key=settings.USAGE_ID_PATTERN), ),
'courseware.module_render.handle_xblock_callback_noauth', 'courseware.module_render.handle_xblock_callback_noauth',
name='xblock_handler_noauth'), name='xblock_handler_noauth',
url(r'xblock/resource/(?P<block_type>[^/]+)/(?P<uri>.*)$', ),
# xblock View API
# (unpublished) API that returns JSON with the HTML fragment and related resources
# for the xBlock's requested view.
url(
r'^courses/{course_key}/xblock/{usage_key}/view/(?P<view_name>[^/]*)$'.format(
course_key=settings.COURSE_ID_PATTERN,
usage_key=settings.USAGE_ID_PATTERN,
),
'courseware.module_render.xblock_view',
name='xblock_view',
),
# xblock Rendering View URL
# URL to provide an HTML view of an xBlock. The view type (e.g., student_view) is
# passed as a "view" parameter to the URL.
# Note: This is not an API. Compare this with the xblock_view API above.
url(
r'^xblock/{usage_key_string}$'.format(usage_key_string=settings.USAGE_KEY_PATTERN),
'courseware.views.render_xblock',
name='render_xblock',
),
# xblock Resource URL
url(
r'xblock/resource/(?P<block_type>[^/]+)/(?P<uri>.*)$',
'courseware.module_render.xblock_resource', 'courseware.module_render.xblock_resource',
name='xblock_resource_url'), name='xblock_resource_url',
),
# Software Licenses # Software Licenses
...@@ -440,15 +478,6 @@ if settings.COURSEWARE_ENABLED: ...@@ -440,15 +478,6 @@ if settings.COURSEWARE_ENABLED:
url(r'^courses/{}/teams'.format(settings.COURSE_ID_PATTERN), include('teams.urls'), name="teams_endpoints"), url(r'^courses/{}/teams'.format(settings.COURSE_ID_PATTERN), include('teams.urls'), name="teams_endpoints"),
) )
if settings.FEATURES.get('ENABLE_RENDER_XBLOCK_API'):
# TODO (MA-789) This endpoint path still needs to be approved by the arch council.
# Until then, keep the version at v0.
urlpatterns += (
url(r'api/xblock/v0/xblock/{usage_key_string}$'.format(usage_key_string=settings.USAGE_KEY_PATTERN),
'courseware.views.render_xblock',
name='render_xblock'),
)
# allow course staff to change to student view of courseware # allow course staff to change to student view of courseware
if settings.FEATURES.get('ENABLE_MASQUERADE'): if settings.FEATURES.get('ENABLE_MASQUERADE'):
urlpatterns += ( urlpatterns += (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment