Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.
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
69 changes: 53 additions & 16 deletions lms/djangoapps/courseware/module_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.conf import settings
from django.contrib.auth.models import User
from django.core.cache import cache
from django.core.context_processors import csrf
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.http import Http404, HttpResponse
Expand All @@ -29,7 +30,7 @@
from xblock.core import XBlock
from xblock.fields import Scope
from xblock.runtime import KvsFieldData, KeyValueStore
from xblock.exceptions import NoSuchHandlerError
from xblock.exceptions import NoSuchHandlerError, NoSuchViewError
from xblock.django.request import django_to_webob_request, webob_to_django_response
from xmodule.error_module import ErrorDescriptor, NonStaffErrorDescriptor
from xmodule.exceptions import NotFoundError, ProcessingError
Expand Down Expand Up @@ -562,23 +563,19 @@ def xblock_resource(request, block_type, uri): # pylint: disable=unused-argumen
return HttpResponse(content, mimetype=mimetype)


def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, user):
def _get_module_by_usage_id(request, course_id, usage_id):
"""
Invoke an XBlock handler, either authenticated or not.
Gets a module instance based on its `usage_id` in a course, for a given request/user

Returns (location, descriptor, instance)
"""
location = unquote_slashes(usage_id)
user = request.user

# Check parameters and fail fast if there's a problem
if not Location.is_valid(location):
raise Http404("Invalid location")

# Check submitted files
files = request.FILES or {}
error_msg = _check_files_limits(files)
if error_msg:
return HttpResponse(json.dumps({'success': error_msg}))

try:
descriptor = modulestore().get_instance(course_id, location)
except ItemNotFoundError:
Expand All @@ -590,13 +587,6 @@ def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, user):
)
raise Http404

tracking_context_name = 'module_callback_handler'
tracking_context = {
'module': {
'display_name': descriptor.display_name_with_default,
}
}

field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
course_id,
user,
Expand All @@ -609,6 +599,29 @@ def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, user):
log.debug("No module %s for user %s -- access denied?", location, user)
raise Http404

return (location, descriptor, instance)


def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, user):
"""
Invoke an XBlock handler, either authenticated or not.

"""
location, descriptor, instance = _get_module_by_usage_id(request, course_id, usage_id)

# Check submitted files
files = request.FILES or {}
error_msg = _check_files_limits(files)
if error_msg:
return HttpResponse(json.dumps({'success': error_msg}))

tracking_context_name = 'module_callback_handler'
tracking_context = {
'module': {
'display_name': descriptor.display_name_with_default,
}
}

req = django_to_webob_request(request)
try:
with tracker.get_tracker().context(tracking_context_name, tracking_context):
Expand Down Expand Up @@ -637,6 +650,30 @@ def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, user):
return webob_to_django_response(resp)


def xblock_view(request, course_id, usage_id, view_name):
"""
Returns the rendered view of a given XBlock, with related resources

Returns a json object containing two keys:
html: The rendered html of the view
resources: A list of tuples where the first element is the resource hash, and
the second is the resource description
"""
location, descriptor, instance = _get_module_by_usage_id(request, course_id, usage_id)

try:
fragment = instance.render(view_name)
except NoSuchViewError:
log.exception("Attempt to render missing view on %s: %s", instance, view_name)
raise Http404

return JsonResponse({
'html': fragment.content,
'resources': fragment.resources,
'csrf_token': str(csrf(request)['csrf_token']),
})


def get_score_bucket(grade, max_grade):
"""
Function to split arbitrary score ranges into 3 buckets.
Expand Down
8 changes: 8 additions & 0 deletions lms/envs/aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,14 @@

HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS = ENV_TOKENS.get('HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS',{})

############# CORS headers for cross-domain requests #################

if FEATURES.get('ENABLE_CORS_HEADERS'):
INSTALLED_APPS += ('corsheaders',)
MIDDLEWARE_CLASSES += ('corsheaders.middleware.CorsMiddleware',)
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = ENV_TOKENS.get('CORS_ORIGIN_WHITELIST', ())

############################## SECURE AUTH ITEMS ###############
# Secret things: passwords, access keys, etc.

Expand Down
11 changes: 11 additions & 0 deletions lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@
# with Shib. Feature was requested by Stanford's office of general counsel
'SHIB_DISABLE_TOS': False,

# Allows to configure the LMS to provide CORS headers to serve requests from other domains
'ENABLE_CORS_HEADERS': False,

# Can be turned off if course lists need to be hidden. Effects views and templates.
'COURSES_ARE_BROWSABLE': True,

Expand Down Expand Up @@ -1243,6 +1246,14 @@
INSTALLED_APPS += ('django_cas',)
MIDDLEWARE_CLASSES += ('django_cas.middleware.CASMiddleware',)

############# CORS headers for cross-domain requests #################

if FEATURES.get('ENABLE_CORS_HEADERS'):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that I understand why this is here - I believe what's in ams.py (above) will set it anyway with the correct information; but I trust you! It all seems to work with this in place 😸

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, aws.py will set it correctly if it's used - but some people still don't use devstack for development, so aws.py is not always loaded for configuration, while common.py is.

INSTALLED_APPS += ('corsheaders',)
MIDDLEWARE_CLASSES += ('corsheaders.middleware.CorsMiddleware',)
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = ('')

###################### Registration ##################################

# For each of the fields, give one of the following values:
Expand Down
4 changes: 3 additions & 1 deletion lms/lib/xblock/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import re

from django.conf import settings
from django.core.urlresolvers import reverse

from user_api import user_service
Expand Down Expand Up @@ -106,10 +107,11 @@ def local_resource_url(self, block, uri):
"""
local_resource_url for Studio
"""
return reverse('xblock_resource_url', kwargs={
path = reverse('xblock_resource_url', kwargs={
'block_type': block.scope_ids.block_type,
'uri': uri,
})
return '//{}{}'.format(settings.SITE_NAME, path)


class LmsPartitionService(PartitionService):
Expand Down
3 changes: 3 additions & 0 deletions lms/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/xblock/(?P<usage_id>[^/]*)/handler/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$',
'courseware.module_render.handle_xblock_callback',
name='xblock_handler'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/xblock/(?P<usage_id>[^/]*)/view/(?P<view_name>[^/]*)$',
'courseware.module_render.xblock_view',
name='xblock_view'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/xblock/(?P<usage_id>[^/]*)/handler_noauth/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$',
'courseware.module_render.handle_xblock_callback_noauth',
name='xblock_handler_noauth'),
Expand Down
3 changes: 3 additions & 0 deletions requirements/edx/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ sphinx_rtd_theme==0.1.5
Babel==1.3
transifex-client==0.10

# Used to allow to configure CORS headers for cross-domain requests
django-cors-headers==0.12

# We've tried several times to update the debug toolbar to version 1.0.1,
# and had problems each time, resulting in us rolling back to 0.9.4. Before
# submitting another pull request to do this update, check the following:
Expand Down